diff --git a/Gulpfile.js b/Gulpfile.js
index c9d3feff80b01..79f4273715cca 100644
--- a/Gulpfile.js
+++ b/Gulpfile.js
@@ -18,6 +18,9 @@ const { buildProject, cleanProject, watchProject } = require("./scripts/build/pr
const cmdLineOptions = require("./scripts/build/options");
const copyright = "CopyrightNotice.txt";
+const testRootFile = "built/local/testRunner/Harness.js";
+const testRootBundle = "built/local/run.js";
+const testRoot = testRootBundle; // use testRootFile to run directly from the module files
const cleanTasks = [];
const buildScripts = () => buildProject("scripts");
@@ -125,6 +128,7 @@ const localPreBuild = parallel(generateLibs, series(buildScripts, generateDiagno
const preBuild = cmdLineOptions.lkg ? lkgPreBuild : localPreBuild;
const buildServices = (() => {
+ return cb => { console.log("!!!TODO!!! buildServices"); cb(); };
// build typescriptServices.out.js
const buildTypescriptServicesOut = () => buildProject("src/typescriptServices/tsconfig.json", cmdLineOptions);
@@ -249,6 +253,7 @@ task("watch-min").flags = {
};
const buildLssl = (() => {
+ return cb => { console.log("!!!TODO!!! buildLssl"); cb(); };
// build tsserverlibrary.out.js
const buildServerLibraryOut = () => buildProject("src/tsserverlibrary/tsconfig.json", cmdLineOptions);
@@ -318,7 +323,14 @@ task("watch-lssl").flags = {
" --built": "Compile using the built version of the compiler."
};
-const buildTests = () => buildProject("src/testRunner");
+const buildTests = series(
+ () => buildProject("src/testRunner"),
+ () => exec("npx", [
+ "esbuild", "--bundle", "--format=cjs", "--platform=node", "--target=node12",
+ "--legal-comments=none",
+ `--outfile=${testRootBundle}`, testRootFile,
+ ]),
+);
task("tests", series(preBuild, parallel(buildLssl, buildTests)));
task("tests").description = "Builds the test infrastructure";
task("tests").flags = {
@@ -442,7 +454,7 @@ preTest.displayName = "preTest";
const postTest = (done) => cmdLineOptions.lint ? lint(done) : done();
-const runTests = () => runConsoleTests("built/local/run.js", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false);
+const runTests = () => runConsoleTests(testRoot, "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false);
task("runtests", series(preBuild, preTest, runTests, postTest));
task("runtests").description = "Runs the tests using the built run.js file.";
task("runtests").flags = {
@@ -462,7 +474,7 @@ task("runtests").flags = {
" --shardId": "1-based ID of this shard (default: 1)",
};
-const runTestsParallel = () => runConsoleTests("built/local/run.js", "min", /*runInParallel*/ cmdLineOptions.workers > 1, /*watchMode*/ false);
+const runTestsParallel = () => runConsoleTests(testRoot, "min", /*runInParallel*/ cmdLineOptions.workers > 1, /*watchMode*/ false);
task("runtests-parallel", series(preBuild, preTest, runTestsParallel, postTest));
task("runtests-parallel").description = "Runs all the tests in parallel using the built run.js file.";
task("runtests-parallel").flags = {
@@ -611,10 +623,10 @@ task("publish-nightly").description = "Runs `npm publish --tag next` to create a
// write some kind of trigger file that indicates build completion that we could listen for instead.
const watchRuntests = () => watch(["built/local/*.js", "tests/cases/**/*.ts", "tests/cases/**/tsconfig.json"], { delay: 5000 }, async () => {
if (cmdLineOptions.tests || cmdLineOptions.failed) {
- await runConsoleTests("built/local/run.js", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ true);
+ await runConsoleTests(testRoot, "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ true);
}
else {
- await runConsoleTests("built/local/run.js", "min", /*runInParallel*/ true, /*watchMode*/ true);
+ await runConsoleTests(testRoot, "min", /*runInParallel*/ true, /*watchMode*/ true);
}
});
task("watch", series(preBuild, preTest, parallel(watchLib, watchDiagnostics, watchServices, watchLssl, watchTests, watchRuntests)));
diff --git a/package.json b/package.json
index aafbd543bf869..ab184263d642b 100644
--- a/package.json
+++ b/package.json
@@ -66,6 +66,7 @@
"convert-source-map": "latest",
"del": "5.1.0",
"diff": "^4.0.2",
+ "esbuild": "^0.13.8",
"eslint": "7.12.1",
"eslint-formatter-autolinkable-stylish": "1.1.4",
"eslint-plugin-import": "2.22.1",
diff --git a/scripts/bisect-test.ts b/scripts/bisect-test.ts
index a3e48b29a81f5..52668a849a9a5 100644
--- a/scripts/bisect-test.ts
+++ b/scripts/bisect-test.ts
@@ -1,3 +1,5 @@
+//!!! should this be updated or removed?
+
/**
* You should have ts-node installed globally before executing this, probably!
* Otherwise you'll need to compile this script before you start bisecting!
diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.ts
index 53d1ca75db887..17bc4612c4f84 100644
--- a/scripts/processDiagnosticMessages.ts
+++ b/scripts/processDiagnosticMessages.ts
@@ -38,10 +38,7 @@ function main(): void {
}
}
- const outputFilesDir = path.dirname(inputFilePath);
- const thisFilePathRel = path.relative(process.cwd(), outputFilesDir);
-
- const infoFileOutput = buildInfoFileOutput(diagnosticMessages, "./diagnosticInformationMap.generated.ts", thisFilePathRel);
+ const infoFileOutput = buildInfoFileOutput(diagnosticMessages, inputFilePath);
checkForUniqueCodes(diagnosticMessages);
writeFile("diagnosticInformationMap.generated.ts", infoFileOutput);
@@ -59,28 +56,31 @@ function checkForUniqueCodes(diagnosticTable: InputDiagnosticMessageTable) {
});
}
-function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, inputFilePathRel: string, thisFilePathRel: string): string {
- let result =
- "// \r\n" +
- "// generated from '" + inputFilePathRel + "' by '" + thisFilePathRel.replace(/\\/g, "/") + "'\r\n" +
- "/* @internal */\r\n" +
- "namespace ts {\r\n" +
- " function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean, reportsDeprecated?: {}): DiagnosticMessage {\r\n" +
- " return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated };\r\n" +
- " }\r\n" +
- " export const Diagnostics = {\r\n";
+function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, inputFilePathRel: string): string {
+ const result = [
+ "// ",
+ `// generated from '${inputFilePathRel}'`,
+ "",
+ "import { DiagnosticCategory, DiagnosticMessage } from \"./ts\";",
+ "",
+ "function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean, reportsDeprecated?: {}): DiagnosticMessage {",
+ " return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated };",
+ "}",
+ "",
+ "export const Diagnostics = {",
+ ];
messageTable.forEach(({ code, category, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated }, name) => {
const propName = convertPropertyName(name);
const argReportsUnnecessary = reportsUnnecessary ? `, /*reportsUnnecessary*/ ${reportsUnnecessary}` : "";
const argElidedInCompatabilityPyramid = elidedInCompatabilityPyramid ? `${!reportsUnnecessary ? ", /*reportsUnnecessary*/ undefined" : ""}, /*elidedInCompatabilityPyramid*/ ${elidedInCompatabilityPyramid}` : "";
const argReportsDeprecated = reportsDeprecated ? `${!argElidedInCompatabilityPyramid ? ", /*reportsUnnecessary*/ undefined, /*elidedInCompatabilityPyramid*/ undefined" : ""}, /*reportsDeprecated*/ ${reportsDeprecated}` : "";
- result += ` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}${argReportsUnnecessary}${argElidedInCompatabilityPyramid}${argReportsDeprecated}),\r\n`;
+ result.push(` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}${argReportsUnnecessary}${argElidedInCompatabilityPyramid}${argReportsDeprecated}),`);
});
- result += " };\r\n}";
+ result.push("};");
- return result;
+ return result.join("\r\n");
}
function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable): string {
diff --git a/src/cancellationToken/cancellationToken.ts b/src/cancellationToken/cancellationToken.ts
index aaa19750b8710..02cba37fced6d 100644
--- a/src/cancellationToken/cancellationToken.ts
+++ b/src/cancellationToken/cancellationToken.ts
@@ -1,6 +1,6 @@
///
-import fs = require("fs");
+import * as fs from "fs";
interface ServerCancellationToken {
isCancellationRequested(): boolean;
@@ -62,7 +62,7 @@ function createCancellationToken(args: string[]): ServerCancellationToken {
}
else {
return {
- isCancellationRequested: () => pipeExists(cancellationPipeName!), // TODO: GH#18217
+ isCancellationRequested: () => pipeExists(cancellationPipeName!),
setRequest: (_requestId: number): void => void 0,
resetRequest: (_requestId: number): void => void 0
};
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index eefc41fdaf8b5..50393837465cf 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -1,3530 +1,3534 @@
+import { __String, FlowLabel, ModuleDeclaration, ESMap, setParent, setParentRecursive, Node, getNodeId, SyntaxKind, isEnumConst, EnumDeclaration, hasSyntacticModifier, ModifierFlags, ExportDeclaration, forEachChild, Debug, Identifier, ExportSpecifier, isBlock, isModuleBlock, isSourceFile, nodeHasName, FlowNode, SourceFile, CompilerOptions, tracing, perfLogger, ScriptTarget, JSDocTypedefTag, JSDocCallbackTag, JSDocEnumTag, NodeFlags, SymbolFlags, FlowFlags, DiagnosticMessage, DiagnosticWithLocation, createDiagnosticForNodeInSourceFile, getSourceFileOfNode, getEmitScriptTarget, objectAllocator, getStrictOptionValue, Declaration, appendIfUnique, createSymbolTable, setValueDeclaration, ExportAssignment, InternalSymbolName, getNameOfDeclaration, isAmbientModule, getTextOfIdentifierOrLiteral, StringLiteral, isGlobalScopeAugmentation, isStringOrNumericLiteralLike, escapeLeadingUnderscores, isSignedNumericLiteral, tokenToString, isPrivateIdentifier, getContainingClass, getSymbolNameForPrivateIdentifier, isPropertyNameLiteral, getEscapedTextOfIdentifierOrLiteral, getAssignmentDeclarationKind, BinaryExpression, AssignmentDeclarationKind, isJSDocConstructSignature, JSDocFunctionType, ParameterDeclaration, isNamedDeclaration, declarationNameToString, unescapeLeadingUnderscores, SymbolTable, hasDynamicName, isExportSpecifier, Diagnostics, length, DiagnosticRelatedInformation, isTypeAliasDeclaration, nodeIsMissing, forEach, addRelatedInfo, getCombinedModifierFlags, isJSDocTypeAlias, isInJSFile, isModuleDeclaration, isJSDocEnumTag, isPropertyAccessEntityNameExpression, isDeclaration, Mutable, FunctionLikeDeclaration, getImmediatelyInvokedFunctionExpression, FunctionExpression, ArrowFunction, MethodDeclaration, GetAccessorDeclaration, SetAccessorDeclaration, nodeIsPresent, ClassStaticBlockDeclaration, NodeArray, WhileStatement, DoStatement, ForStatement, ForInOrOfStatement, IfStatement, ReturnStatement, ThrowStatement, BreakOrContinueStatement, TryStatement, SwitchStatement, CaseBlock, CaseClause, ExpressionStatement, LabeledStatement, PrefixUnaryExpression, PostfixUnaryExpression, isDestructuringAssignment, DeleteExpression, ConditionalExpression, VariableDeclaration, AccessExpression, CallExpression, NonNullExpression, Block, BindingElement, Expression, ParenthesizedExpression, TypeOfExpression, isDottedName, isPropertyAccessExpression, isNonNullExpression, isParenthesizedExpression, isBinaryExpression, isElementAccessExpression, isAssignmentExpression, isOptionalChain, PropertyAccessExpression, isTypeOfExpression, isStringLiteralLike, FlowReduceLabel, contains, isExpressionOfOptionalChainRoot, isNullishCoalesce, ArrayBindingElement, skipParentheses, isLogicalOrCoalescingAssignmentOperator, isPrefixUnaryExpression, isOutermostOptionalChain, Statement, concatenate, unusedLabelIsError, ArrayLiteralExpression, SpreadElement, ObjectLiteralExpression, DestructuringAssignment, createBinaryExpressionTrampoline, BinaryOperatorToken, isAssignmentOperator, isAssignmentTarget, ElementAccessExpression, isOmittedExpression, isBindingPattern, isForInOrOfStatement, JSDocClassTag, getHostSignatureFromJSDoc, OptionalChain, isOptionalChainRoot, NonNullChain, PropertyAccessChain, ElementAccessChain, CallChain, isIdentifier, isPushOrUnshiftIdentifier, isObjectLiteralOrClassExpressionMethodOrAccessor, PropertyDeclaration, isFunctionLike, isClassStaticBlockDeclaration, isStatic, isExternalModule, tryCast, isExportDeclaration, isExportAssignment, isModuleAugmentationExternal, Pattern, tryParsePattern, append, PatternAmbientModule, isString, SignatureDeclaration, JSDocSignature, JsxAttributes, JsxAttribute, isExternalOrCommonJsModule, findAncestor, getEnclosingBlockScopeContainer, getAssignmentDeclarationPropertyAccessKind, isIdentifierName, isInTopLevelContext, PrivateIdentifier, isLeftHandSideExpression, CatchClause, getErrorSpanForNode, createFileDiagnostic, idText, FunctionDeclaration, isFunctionLikeOrClassStaticBlockDeclaration, NumericLiteral, TokenFlags, WithStatement, isDeclarationStatement, isVariableStatement, getSpanOfTokenAtPosition, getTokenPosOfNode, TextRange, DiagnosticCategory, hasJSDocNodes, isPrologueDirective, getSourceTextOfNodeFromSourceFile, isExpression, isPartOfTypeQuery, isSpecialPropertyDeclaration, isModuleExportsAccessExpression, BindableStaticPropertyAssignmentExpression, BindablePropertyAssignmentExpression, isThisInitializedDeclaration, TypeParameterDeclaration, PropertySignature, isObjectLiteralMethod, TypeLiteralNode, MappedTypeNode, JSDocTypeLiteral, BindableObjectDefinePropertyCall, ClassLikeDeclaration, NamespaceExportDeclaration, ImportClause, ModuleBlock, JSDocParameterTag, JSDocPropertyLikeTag, isJsonSourceFile, removeFileExtension, exportAssignmentIsAlias, isNamespaceExport, isAliasableExpression, isExportsIdentifier, getRightMostAssignedExpression, isEmptyObjectLiteral, isObjectLiteralExpression, every, isShorthandPropertyAssignment, ShorthandPropertyAssignment, LiteralLikeElementAccessExpression, getThisContainer, isBindableStaticAccessExpression, isPrototypeAccess, DynamicNamedDeclaration, EntityNameExpression, BindableStaticAccessExpression, isFunctionSymbol, getLeftmostAccessExpression, cast, isBindableStaticNameExpression, BindableStaticNameExpression, isFunctionLikeDeclaration, getAssignedExpandoInitializer, isCallExpression, isBindableObjectDefinePropertyCall, some, BindableAccessExpression, isVariableDeclaration, getExpandoInitializer, getElementOrPropertyAccessName, getNameOrArgument, isRequireCall, symbolName, isRequireVariableDeclaration, getJSDocTypeTag, isBlockOrCatchScoped, isParameterDeclaration, isParameterPropertyDeclaration, isAsyncFunction, ConditionalTypeNode, isConditionalTypeNode, isJSDocTemplateTag, getEffectiveContainerForJSDocTemplateTag, shouldPreserveConstEnums, isStatementButNotDeclaration, unreachableCodeIsError, getCombinedNodeFlags, isStatement, sliceAfter, getRangesWhere, isFunctionDeclaration, isEnumDeclaration, Symbol } from "./ts";
+import { mark, measure } from "./ts.performance";
+import * as ts from "./ts";
/* @internal */
-namespace ts {
- export const enum ModuleInstanceState {
- NonInstantiated = 0,
- Instantiated = 1,
- ConstEnumOnly = 2
- }
+export const enum ModuleInstanceState {
+ NonInstantiated = 0,
+ Instantiated = 1,
+ ConstEnumOnly = 2
+}
- interface ActiveLabel {
- next: ActiveLabel | undefined;
- name: __String;
- breakTarget: FlowLabel;
- continueTarget: FlowLabel | undefined;
- referenced: boolean;
- }
+/* @internal */
+interface ActiveLabel {
+ next: ActiveLabel | undefined;
+ name: __String;
+ breakTarget: FlowLabel;
+ continueTarget: FlowLabel | undefined;
+ referenced: boolean;
+}
- export function getModuleInstanceState(node: ModuleDeclaration, visited?: ESMap): ModuleInstanceState {
- if (node.body && !node.body.parent) {
- // getModuleInstanceStateForAliasTarget needs to walk up the parent chain, so parent pointers must be set on this tree already
- setParent(node.body, node);
- setParentRecursive(node.body, /*incremental*/ false);
- }
- return node.body ? getModuleInstanceStateCached(node.body, visited) : ModuleInstanceState.Instantiated;
+/* @internal */
+export function getModuleInstanceState(node: ModuleDeclaration, visited?: ESMap): ModuleInstanceState {
+ if (node.body && !node.body.parent) {
+ // getModuleInstanceStateForAliasTarget needs to walk up the parent chain, so parent pointers must be set on this tree already
+ setParent(node.body, node);
+ setParentRecursive(node.body, /*incremental*/ false);
}
+ return node.body ? getModuleInstanceStateCached(node.body, visited) : ModuleInstanceState.Instantiated;
+}
- function getModuleInstanceStateCached(node: Node, visited = new Map()) {
- const nodeId = getNodeId(node);
- if (visited.has(nodeId)) {
- return visited.get(nodeId) || ModuleInstanceState.NonInstantiated;
- }
- visited.set(nodeId, undefined);
- const result = getModuleInstanceStateWorker(node, visited);
- visited.set(nodeId, result);
- return result;
+/* @internal */
+function getModuleInstanceStateCached(node: Node, visited = new ts.Map()) {
+ const nodeId = getNodeId(node);
+ if (visited.has(nodeId)) {
+ return visited.get(nodeId) || ModuleInstanceState.NonInstantiated;
}
+ visited.set(nodeId, undefined);
+ const result = getModuleInstanceStateWorker(node, visited);
+ visited.set(nodeId, result);
+ return result;
+}
- function getModuleInstanceStateWorker(node: Node, visited: ESMap): ModuleInstanceState {
- // A module is uninstantiated if it contains only
- switch (node.kind) {
- // 1. interface declarations, type alias declarations
- case SyntaxKind.InterfaceDeclaration:
- case SyntaxKind.TypeAliasDeclaration:
+/* @internal */
+function getModuleInstanceStateWorker(node: Node, visited: ESMap): ModuleInstanceState {
+ // A module is uninstantiated if it contains only
+ switch (node.kind) {
+ // 1. interface declarations, type alias declarations
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.TypeAliasDeclaration:
+ return ModuleInstanceState.NonInstantiated;
+ // 2. const enum declarations
+ case SyntaxKind.EnumDeclaration:
+ if (isEnumConst(node as EnumDeclaration)) {
+ return ModuleInstanceState.ConstEnumOnly;
+ }
+ break;
+ // 3. non-exported import declarations
+ case SyntaxKind.ImportDeclaration:
+ case SyntaxKind.ImportEqualsDeclaration:
+ if (!(hasSyntacticModifier(node, ModifierFlags.Export))) {
return ModuleInstanceState.NonInstantiated;
- // 2. const enum declarations
- case SyntaxKind.EnumDeclaration:
- if (isEnumConst(node as EnumDeclaration)) {
- return ModuleInstanceState.ConstEnumOnly;
- }
- break;
- // 3. non-exported import declarations
- case SyntaxKind.ImportDeclaration:
- case SyntaxKind.ImportEqualsDeclaration:
- if (!(hasSyntacticModifier(node, ModifierFlags.Export))) {
- return ModuleInstanceState.NonInstantiated;
- }
- break;
- // 4. Export alias declarations pointing at only uninstantiated modules or things uninstantiated modules contain
- case SyntaxKind.ExportDeclaration:
- const exportDeclaration = node as ExportDeclaration;
- if (!exportDeclaration.moduleSpecifier && exportDeclaration.exportClause && exportDeclaration.exportClause.kind === SyntaxKind.NamedExports) {
- let state = ModuleInstanceState.NonInstantiated;
- for (const specifier of exportDeclaration.exportClause.elements) {
- const specifierState = getModuleInstanceStateForAliasTarget(specifier, visited);
- if (specifierState > state) {
- state = specifierState;
- }
- if (state === ModuleInstanceState.Instantiated) {
- return state;
- }
- }
- return state;
- }
- break;
- // 5. other uninstantiated module declarations.
- case SyntaxKind.ModuleBlock: {
+ }
+ break;
+ // 4. Export alias declarations pointing at only uninstantiated modules or things uninstantiated modules contain
+ case SyntaxKind.ExportDeclaration:
+ const exportDeclaration = node as ExportDeclaration;
+ if (!exportDeclaration.moduleSpecifier && exportDeclaration.exportClause && exportDeclaration.exportClause.kind === SyntaxKind.NamedExports) {
let state = ModuleInstanceState.NonInstantiated;
- forEachChild(node, n => {
- const childState = getModuleInstanceStateCached(n, visited);
- switch (childState) {
- case ModuleInstanceState.NonInstantiated:
- // child is non-instantiated - continue searching
- return;
- case ModuleInstanceState.ConstEnumOnly:
- // child is const enum only - record state and continue searching
- state = ModuleInstanceState.ConstEnumOnly;
- return;
- case ModuleInstanceState.Instantiated:
- // child is instantiated - record state and stop
- state = ModuleInstanceState.Instantiated;
- return true;
- default:
- Debug.assertNever(childState);
+ for (const specifier of exportDeclaration.exportClause.elements) {
+ const specifierState = getModuleInstanceStateForAliasTarget(specifier, visited);
+ if (specifierState > state) {
+ state = specifierState;
}
- });
+ if (state === ModuleInstanceState.Instantiated) {
+ return state;
+ }
+ }
return state;
}
- case SyntaxKind.ModuleDeclaration:
- return getModuleInstanceState(node as ModuleDeclaration, visited);
- case SyntaxKind.Identifier:
- // Only jsdoc typedef definition can exist in jsdoc namespace, and it should
- // be considered the same as type alias
- if ((node as Identifier).isInJSDocNamespace) {
- return ModuleInstanceState.NonInstantiated;
+ break;
+ // 5. other uninstantiated module declarations.
+ case SyntaxKind.ModuleBlock: {
+ let state = ModuleInstanceState.NonInstantiated;
+ forEachChild(node, n => {
+ const childState = getModuleInstanceStateCached(n, visited);
+ switch (childState) {
+ case ModuleInstanceState.NonInstantiated:
+ // child is non-instantiated - continue searching
+ return;
+ case ModuleInstanceState.ConstEnumOnly:
+ // child is const enum only - record state and continue searching
+ state = ModuleInstanceState.ConstEnumOnly;
+ return;
+ case ModuleInstanceState.Instantiated:
+ // child is instantiated - record state and stop
+ state = ModuleInstanceState.Instantiated;
+ return true;
+ default:
+ Debug.assertNever(childState);
}
+ });
+ return state;
}
- return ModuleInstanceState.Instantiated;
+ case SyntaxKind.ModuleDeclaration:
+ return getModuleInstanceState(node as ModuleDeclaration, visited);
+ case SyntaxKind.Identifier:
+ // Only jsdoc typedef definition can exist in jsdoc namespace, and it should
+ // be considered the same as type alias
+ if ((node as Identifier).isInJSDocNamespace) {
+ return ModuleInstanceState.NonInstantiated;
+ }
}
+ return ModuleInstanceState.Instantiated;
+}
- function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: ESMap) {
- const name = specifier.propertyName || specifier.name;
- let p: Node | undefined = specifier.parent;
- while (p) {
- if (isBlock(p) || isModuleBlock(p) || isSourceFile(p)) {
- const statements = p.statements;
- let found: ModuleInstanceState | undefined;
- for (const statement of statements) {
- if (nodeHasName(statement, name)) {
- if (!statement.parent) {
- setParent(statement, p);
- setParentRecursive(statement, /*incremental*/ false);
- }
- const state = getModuleInstanceStateCached(statement, visited);
- if (found === undefined || state > found) {
- found = state;
- }
- if (found === ModuleInstanceState.Instantiated) {
- return found;
- }
+/* @internal */
+function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: ESMap) {
+ const name = specifier.propertyName || specifier.name;
+ let p: Node | undefined = specifier.parent;
+ while (p) {
+ if (isBlock(p) || isModuleBlock(p) || isSourceFile(p)) {
+ const statements = p.statements;
+ let found: ModuleInstanceState | undefined;
+ for (const statement of statements) {
+ if (nodeHasName(statement, name)) {
+ if (!statement.parent) {
+ setParent(statement, p);
+ setParentRecursive(statement, /*incremental*/ false);
+ }
+ const state = getModuleInstanceStateCached(statement, visited);
+ if (found === undefined || state > found) {
+ found = state;
+ }
+ if (found === ModuleInstanceState.Instantiated) {
+ return found;
}
}
- if (found !== undefined) {
- return found;
- }
}
- p = p.parent;
+ if (found !== undefined) {
+ return found;
+ }
}
- return ModuleInstanceState.Instantiated; // Couldn't locate, assume could refer to a value
+ p = p.parent;
}
+ return ModuleInstanceState.Instantiated; // Couldn't locate, assume could refer to a value
+}
- const enum ContainerFlags {
- // The current node is not a container, and no container manipulation should happen before
- // recursing into it.
- None = 0,
+/* @internal */
+const enum ContainerFlags {
+ // The current node is not a container, and no container manipulation should happen before
+ // recursing into it.
+ None = 0,
+
+ // The current node is a container. It should be set as the current container (and block-
+ // container) before recursing into it. The current node does not have locals. Examples:
+ //
+ // Classes, ObjectLiterals, TypeLiterals, Interfaces...
+ IsContainer = 1 << 0,
+
+ // The current node is a block-scoped-container. It should be set as the current block-
+ // container before recursing into it. Examples:
+ //
+ // Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements...
+ IsBlockScopedContainer = 1 << 1,
+
+ // The current node is the container of a control flow path. The current control flow should
+ // be saved and restored, and a new control flow initialized within the container.
+ IsControlFlowContainer = 1 << 2,
+
+ IsFunctionLike = 1 << 3,
+ IsFunctionExpression = 1 << 4,
+ HasLocals = 1 << 5,
+ IsInterface = 1 << 6,
+ IsObjectLiteralOrClassExpressionMethodOrAccessor = 1 << 7
+}
- // The current node is a container. It should be set as the current container (and block-
- // container) before recursing into it. The current node does not have locals. Examples:
- //
- // Classes, ObjectLiterals, TypeLiterals, Interfaces...
- IsContainer = 1 << 0,
+/* @internal */
+function initFlowNode(node: T) {
+ Debug.attachFlowNodeDebugInfo(node);
+ return node;
+}
- // The current node is a block-scoped-container. It should be set as the current block-
- // container before recursing into it. Examples:
- //
- // Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements...
- IsBlockScopedContainer = 1 << 1,
-
- // The current node is the container of a control flow path. The current control flow should
- // be saved and restored, and a new control flow initialized within the container.
- IsControlFlowContainer = 1 << 2,
-
- IsFunctionLike = 1 << 3,
- IsFunctionExpression = 1 << 4,
- HasLocals = 1 << 5,
- IsInterface = 1 << 6,
- IsObjectLiteralOrClassExpressionMethodOrAccessor = 1 << 7,
- }
-
- function initFlowNode(node: T) {
- Debug.attachFlowNodeDebugInfo(node);
- return node;
- }
-
- const binder = createBinder();
-
- export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
- tracing?.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true);
- performance.mark("beforeBind");
- perfLogger.logStartBindFile("" + file.fileName);
- binder(file, options);
- perfLogger.logStopBindFile();
- performance.mark("afterBind");
- performance.measure("Bind", "beforeBind", "afterBind");
- tracing?.pop();
- }
-
- function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
- let file: SourceFile;
- let options: CompilerOptions;
- let languageVersion: ScriptTarget;
- let parent: Node;
- let container: Node;
- let thisParentContainer: Node; // Container one level up
- let blockScopeContainer: Node;
- let lastContainer: Node;
- let delayedTypeAliases: (JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag)[];
- let seenThisKeyword: boolean;
-
- // state used by control flow analysis
- let currentFlow: FlowNode;
- let currentBreakTarget: FlowLabel | undefined;
- let currentContinueTarget: FlowLabel | undefined;
- let currentReturnTarget: FlowLabel | undefined;
- let currentTrueTarget: FlowLabel | undefined;
- let currentFalseTarget: FlowLabel | undefined;
- let currentExceptionTarget: FlowLabel | undefined;
- let preSwitchCaseFlow: FlowNode | undefined;
- let activeLabelList: ActiveLabel | undefined;
- let hasExplicitReturn: boolean;
-
- // state used for emit helpers
- let emitFlags: NodeFlags;
-
- // If this file is an external module, then it is automatically in strict-mode according to
- // ES6. If it is not an external module, then we'll determine if it is in strict mode or
- // not depending on if we see "use strict" in certain places or if we hit a class/namespace
- // or if compiler options contain alwaysStrict.
- let inStrictMode: boolean;
-
- // If we are binding an assignment pattern, we will bind certain expressions differently.
- let inAssignmentPattern = false;
-
- let symbolCount = 0;
-
- let Symbol: new (flags: SymbolFlags, name: __String) => Symbol;
- let classifiableNames: Set<__String>;
-
- const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
- const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
- const bindBinaryExpressionFlow = createBindBinaryExpressionFlow();
-
- /**
- * Inside the binder, we may create a diagnostic for an as-yet unbound node (with potentially no parent pointers, implying no accessible source file)
- * If so, the node _must_ be in the current file (as that's the only way anything could have traversed to it to yield it as the error node)
- * This version of `createDiagnosticForNode` uses the binder's context to account for this, and always yields correct diagnostics even in these situations.
- */
- function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
- return createDiagnosticForNodeInSourceFile(getSourceFileOfNode(node) || file, node, message, arg0, arg1, arg2);
- }
-
- function bindSourceFile(f: SourceFile, opts: CompilerOptions) {
- file = f;
- options = opts;
- languageVersion = getEmitScriptTarget(options);
- inStrictMode = bindInStrictMode(file, opts);
- classifiableNames = new Set();
- symbolCount = 0;
-
- Symbol = objectAllocator.getSymbolConstructor();
-
- // Attach debugging information if necessary
- Debug.attachFlowNodeDebugInfo(unreachableFlow);
- Debug.attachFlowNodeDebugInfo(reportedUnreachableFlow);
-
- if (!file.locals) {
- bind(file);
- file.symbolCount = symbolCount;
- file.classifiableNames = classifiableNames;
- delayedBindJSDocTypedefTag();
- }
-
- file = undefined!;
- options = undefined!;
- languageVersion = undefined!;
- parent = undefined!;
- container = undefined!;
- thisParentContainer = undefined!;
- blockScopeContainer = undefined!;
- lastContainer = undefined!;
- delayedTypeAliases = undefined!;
- seenThisKeyword = false;
- currentFlow = undefined!;
- currentBreakTarget = undefined;
- currentContinueTarget = undefined;
- currentReturnTarget = undefined;
- currentTrueTarget = undefined;
- currentFalseTarget = undefined;
- currentExceptionTarget = undefined;
- activeLabelList = undefined;
- hasExplicitReturn = false;
- inAssignmentPattern = false;
- emitFlags = NodeFlags.None;
- }
+/* @internal */
+const binder = createBinder();
- return bindSourceFile;
+/* @internal */
+export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
+ tracing?.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true);
+ mark("beforeBind");
+ perfLogger.logStartBindFile("" + file.fileName);
+ binder(file, options);
+ perfLogger.logStopBindFile();
+ mark("afterBind");
+ measure("Bind", "beforeBind", "afterBind");
+ tracing?.pop();
+}
- function bindInStrictMode(file: SourceFile, opts: CompilerOptions): boolean {
- if (getStrictOptionValue(opts, "alwaysStrict") && !file.isDeclarationFile) {
- // bind in strict mode source files with alwaysStrict option
- return true;
- }
- else {
- return !!file.externalModuleIndicator;
- }
- }
+/* @internal */
+function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
+ let file: SourceFile;
+ let options: CompilerOptions;
+ let languageVersion: ScriptTarget;
+ let parent: Node;
+ let container: Node;
+ let thisParentContainer: Node; // Container one level up
+ let blockScopeContainer: Node;
+ let lastContainer: Node;
+ let delayedTypeAliases: (JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag)[];
+ let seenThisKeyword: boolean;
+
+ // state used by control flow analysis
+ let currentFlow: FlowNode;
+ let currentBreakTarget: FlowLabel | undefined;
+ let currentContinueTarget: FlowLabel | undefined;
+ let currentReturnTarget: FlowLabel | undefined;
+ let currentTrueTarget: FlowLabel | undefined;
+ let currentFalseTarget: FlowLabel | undefined;
+ let currentExceptionTarget: FlowLabel | undefined;
+ let preSwitchCaseFlow: FlowNode | undefined;
+ let activeLabelList: ActiveLabel | undefined;
+ let hasExplicitReturn: boolean;
+
+ // state used for emit helpers
+ let emitFlags: NodeFlags;
+
+ // If this file is an external module, then it is automatically in strict-mode according to
+ // ES6. If it is not an external module, then we'll determine if it is in strict mode or
+ // not depending on if we see "use strict" in certain places or if we hit a class/namespace
+ // or if compiler options contain alwaysStrict.
+ let inStrictMode: boolean;
+
+ // If we are binding an assignment pattern, we will bind certain expressions differently.
+ let inAssignmentPattern = false;
+
+ let symbolCount = 0;
+
+ let Symbol: new (flags: SymbolFlags, name: __String) => ts.Symbol;
+ let classifiableNames: ts.Set<__String>;
+
+ const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
+ const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
+ const bindBinaryExpressionFlow = createBindBinaryExpressionFlow();
+
+ /**
+ * Inside the binder, we may create a diagnostic for an as-yet unbound node (with potentially no parent pointers, implying no accessible source file)
+ * If so, the node _must_ be in the current file (as that's the only way anything could have traversed to it to yield it as the error node)
+ * This version of `createDiagnosticForNode` uses the binder's context to account for this, and always yields correct diagnostics even in these situations.
+ */
+ function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
+ return createDiagnosticForNodeInSourceFile(getSourceFileOfNode(node) || file, node, message, arg0, arg1, arg2);
+ }
- function createSymbol(flags: SymbolFlags, name: __String): Symbol {
- symbolCount++;
- return new Symbol(flags, name);
+ function bindSourceFile(f: SourceFile, opts: CompilerOptions) {
+ file = f;
+ options = opts;
+ languageVersion = getEmitScriptTarget(options);
+ inStrictMode = bindInStrictMode(file, opts);
+ classifiableNames = new ts.Set();
+ symbolCount = 0;
+
+ Symbol = objectAllocator.getSymbolConstructor();
+
+ // Attach debugging information if necessary
+ Debug.attachFlowNodeDebugInfo(unreachableFlow);
+ Debug.attachFlowNodeDebugInfo(reportedUnreachableFlow);
+
+ if (!file.locals) {
+ bind(file);
+ file.symbolCount = symbolCount;
+ file.classifiableNames = classifiableNames;
+ delayedBindJSDocTypedefTag();
+ }
+
+ file = undefined!;
+ options = undefined!;
+ languageVersion = undefined!;
+ parent = undefined!;
+ container = undefined!;
+ thisParentContainer = undefined!;
+ blockScopeContainer = undefined!;
+ lastContainer = undefined!;
+ delayedTypeAliases = undefined!;
+ seenThisKeyword = false;
+ currentFlow = undefined!;
+ currentBreakTarget = undefined;
+ currentContinueTarget = undefined;
+ currentReturnTarget = undefined;
+ currentTrueTarget = undefined;
+ currentFalseTarget = undefined;
+ currentExceptionTarget = undefined;
+ activeLabelList = undefined;
+ hasExplicitReturn = false;
+ inAssignmentPattern = false;
+ emitFlags = NodeFlags.None;
+ }
+
+ return bindSourceFile;
+
+ function bindInStrictMode(file: SourceFile, opts: CompilerOptions): boolean {
+ if (getStrictOptionValue(opts, "alwaysStrict") && !file.isDeclarationFile) {
+ // bind in strict mode source files with alwaysStrict option
+ return true;
}
+ else {
+ return !!file.externalModuleIndicator;
+ }
+ }
- function addDeclarationToSymbol(symbol: Symbol, node: Declaration, symbolFlags: SymbolFlags) {
- symbol.flags |= symbolFlags;
+ function createSymbol(flags: SymbolFlags, name: __String): ts.Symbol {
+ symbolCount++;
+ return new Symbol(flags, name);
+ }
- node.symbol = symbol;
- symbol.declarations = appendIfUnique(symbol.declarations, node);
+ function addDeclarationToSymbol(symbol: ts.Symbol, node: Declaration, symbolFlags: SymbolFlags) {
+ symbol.flags |= symbolFlags;
- if (symbolFlags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.Module | SymbolFlags.Variable) && !symbol.exports) {
- symbol.exports = createSymbolTable();
- }
+ node.symbol = symbol;
+ symbol.declarations = appendIfUnique(symbol.declarations, node);
- if (symbolFlags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && !symbol.members) {
- symbol.members = createSymbolTable();
- }
+ if (symbolFlags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.Module | SymbolFlags.Variable) && !symbol.exports) {
+ symbol.exports = createSymbolTable();
+ }
- // On merge of const enum module with class or function, reset const enum only flag (namespaces will already recalculate)
- if (symbol.constEnumOnlyModule && (symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum))) {
- symbol.constEnumOnlyModule = false;
- }
+ if (symbolFlags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && !symbol.members) {
+ symbol.members = createSymbolTable();
+ }
- if (symbolFlags & SymbolFlags.Value) {
- setValueDeclaration(symbol, node);
- }
+ // On merge of const enum module with class or function, reset const enum only flag (namespaces will already recalculate)
+ if (symbol.constEnumOnlyModule && (symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum))) {
+ symbol.constEnumOnlyModule = false;
}
- // Should not be called on a declaration with a computed property name,
- // unless it is a well known Symbol.
- function getDeclarationName(node: Declaration): __String | undefined {
- if (node.kind === SyntaxKind.ExportAssignment) {
- return (node as ExportAssignment).isExportEquals ? InternalSymbolName.ExportEquals : InternalSymbolName.Default;
- }
+ if (symbolFlags & SymbolFlags.Value) {
+ setValueDeclaration(symbol, node);
+ }
+ }
+
+ // Should not be called on a declaration with a computed property name,
+ // unless it is a well known Symbol.
+ function getDeclarationName(node: Declaration): __String | undefined {
+ if (node.kind === SyntaxKind.ExportAssignment) {
+ return (node as ExportAssignment).isExportEquals ? InternalSymbolName.ExportEquals : InternalSymbolName.Default;
+ }
- const name = getNameOfDeclaration(node);
- if (name) {
- if (isAmbientModule(node)) {
- const moduleName = getTextOfIdentifierOrLiteral(name as Identifier | StringLiteral);
- return (isGlobalScopeAugmentation(node as ModuleDeclaration) ? "__global" : `"${moduleName}"`) as __String;
+ const name = getNameOfDeclaration(node);
+ if (name) {
+ if (isAmbientModule(node)) {
+ const moduleName = getTextOfIdentifierOrLiteral(name as Identifier | StringLiteral);
+ return (isGlobalScopeAugmentation(node as ModuleDeclaration) ? "__global" : `"${moduleName}"`) as __String;
+ }
+ if (name.kind === SyntaxKind.ComputedPropertyName) {
+ const nameExpression = name.expression;
+ // treat computed property names where expression is string/numeric literal as just string/numeric literal
+ if (isStringOrNumericLiteralLike(nameExpression)) {
+ return escapeLeadingUnderscores(nameExpression.text);
}
- if (name.kind === SyntaxKind.ComputedPropertyName) {
- const nameExpression = name.expression;
- // treat computed property names where expression is string/numeric literal as just string/numeric literal
- if (isStringOrNumericLiteralLike(nameExpression)) {
- return escapeLeadingUnderscores(nameExpression.text);
- }
- if (isSignedNumericLiteral(nameExpression)) {
- return tokenToString(nameExpression.operator) + nameExpression.operand.text as __String;
- }
- else {
- Debug.fail("Only computed properties with literal names have declaration names");
- }
+ if (isSignedNumericLiteral(nameExpression)) {
+ return tokenToString(nameExpression.operator) + nameExpression.operand.text as __String;
}
- if (isPrivateIdentifier(name)) {
- // containingClass exists because private names only allowed inside classes
- const containingClass = getContainingClass(node);
- if (!containingClass) {
- // we can get here in cases where there is already a parse error.
- return undefined;
- }
- const containingClassSymbol = containingClass.symbol;
- return getSymbolNameForPrivateIdentifier(containingClassSymbol, name.escapedText);
- }
- return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;
- }
- switch (node.kind) {
- case SyntaxKind.Constructor:
- return InternalSymbolName.Constructor;
- case SyntaxKind.FunctionType:
- case SyntaxKind.CallSignature:
- case SyntaxKind.JSDocSignature:
- return InternalSymbolName.Call;
- case SyntaxKind.ConstructorType:
- case SyntaxKind.ConstructSignature:
- return InternalSymbolName.New;
- case SyntaxKind.IndexSignature:
- return InternalSymbolName.Index;
- case SyntaxKind.ExportDeclaration:
- return InternalSymbolName.ExportStar;
- case SyntaxKind.SourceFile:
- // json file should behave as
+ else {
+ Debug.fail("Only computed properties with literal names have declaration names");
+ }
+ }
+ if (isPrivateIdentifier(name)) {
+ // containingClass exists because private names only allowed inside classes
+ const containingClass = getContainingClass(node);
+ if (!containingClass) {
+ // we can get here in cases where there is already a parse error.
+ return undefined;
+ }
+ const containingClassSymbol = containingClass.symbol;
+ return getSymbolNameForPrivateIdentifier(containingClassSymbol, name.escapedText);
+ }
+ return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;
+ }
+ switch (node.kind) {
+ case SyntaxKind.Constructor:
+ return InternalSymbolName.Constructor;
+ case SyntaxKind.FunctionType:
+ case SyntaxKind.CallSignature:
+ case SyntaxKind.JSDocSignature:
+ return InternalSymbolName.Call;
+ case SyntaxKind.ConstructorType:
+ case SyntaxKind.ConstructSignature:
+ return InternalSymbolName.New;
+ case SyntaxKind.IndexSignature:
+ return InternalSymbolName.Index;
+ case SyntaxKind.ExportDeclaration:
+ return InternalSymbolName.ExportStar;
+ case SyntaxKind.SourceFile:
+ // json file should behave as
+ // module.exports = ...
+ return InternalSymbolName.ExportEquals;
+ case SyntaxKind.BinaryExpression:
+ if (getAssignmentDeclarationKind(node as BinaryExpression) === AssignmentDeclarationKind.ModuleExports) {
// module.exports = ...
return InternalSymbolName.ExportEquals;
- case SyntaxKind.BinaryExpression:
- if (getAssignmentDeclarationKind(node as BinaryExpression) === AssignmentDeclarationKind.ModuleExports) {
- // module.exports = ...
- return InternalSymbolName.ExportEquals;
- }
- Debug.fail("Unknown binary declaration kind");
- break;
- case SyntaxKind.JSDocFunctionType:
- return (isJSDocConstructSignature(node) ? InternalSymbolName.New : InternalSymbolName.Call);
- case SyntaxKind.Parameter:
- // Parameters with names are handled at the top of this function. Parameters
- // without names can only come from JSDocFunctionTypes.
- Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType, "Impossible parameter parent kind", () => `parent is: ${(ts as any).SyntaxKind ? (ts as any).SyntaxKind[node.parent.kind] : node.parent.kind}, expected JSDocFunctionType`);
- const functionType = node.parent as JSDocFunctionType;
- const index = functionType.parameters.indexOf(node as ParameterDeclaration);
- return "arg" + index as __String;
- }
+ }
+ Debug.fail("Unknown binary declaration kind");
+ break;
+ case SyntaxKind.JSDocFunctionType:
+ return (isJSDocConstructSignature(node) ? InternalSymbolName.New : InternalSymbolName.Call);
+ case SyntaxKind.Parameter:
+ // Parameters with names are handled at the top of this function. Parameters
+ // without names can only come from JSDocFunctionTypes.
+ Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType, "Impossible parameter parent kind", () => `parent is: ${(ts as any).SyntaxKind ? (ts as any).SyntaxKind[node.parent.kind] : node.parent.kind}, expected JSDocFunctionType`);
+ const functionType = node.parent as JSDocFunctionType;
+ const index = functionType.parameters.indexOf(node as ParameterDeclaration);
+ return "arg" + index as __String;
}
+ }
- function getDisplayName(node: Declaration): string {
- return isNamedDeclaration(node) ? declarationNameToString(node.name) : unescapeLeadingUnderscores(Debug.checkDefined(getDeclarationName(node)));
- }
+ function getDisplayName(node: Declaration): string {
+ return isNamedDeclaration(node) ? declarationNameToString(node.name) : unescapeLeadingUnderscores(Debug.checkDefined(getDeclarationName(node)));
+ }
- /**
- * Declares a Symbol for the node and adds it to symbols. Reports errors for conflicting identifier names.
- * @param symbolTable - The symbol table which node will be added to.
- * @param parent - node's parent declaration.
- * @param node - The declaration to be added to the symbol table
- * @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.)
- * @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations.
- */
- function declareSymbol(symbolTable: SymbolTable, parent: Symbol | undefined, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags, isReplaceableByMethod?: boolean, isComputedName?: boolean): Symbol {
- Debug.assert(isComputedName || !hasDynamicName(node));
+ /**
+ * Declares a Symbol for the node and adds it to symbols. Reports errors for conflicting identifier names.
+ * @param symbolTable - The symbol table which node will be added to.
+ * @param parent - node's parent declaration.
+ * @param node - The declaration to be added to the symbol table
+ * @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.)
+ * @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations.
+ */
+ function declareSymbol(symbolTable: SymbolTable, parent: ts.Symbol | undefined, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags, isReplaceableByMethod?: boolean, isComputedName?: boolean): ts.Symbol {
+ Debug.assert(isComputedName || !hasDynamicName(node));
- const isDefaultExport = hasSyntacticModifier(node, ModifierFlags.Default) || isExportSpecifier(node) && node.name.escapedText === "default";
+ const isDefaultExport = hasSyntacticModifier(node, ModifierFlags.Default) || isExportSpecifier(node) && node.name.escapedText === "default";
- // The exported symbol for an export default function/class node is always named "default"
- const name = isComputedName ? InternalSymbolName.Computed
- : isDefaultExport && parent ? InternalSymbolName.Default
- : getDeclarationName(node);
+ // The exported symbol for an export default function/class node is always named "default"
+ const name = isComputedName ? InternalSymbolName.Computed
+ : isDefaultExport && parent ? InternalSymbolName.Default
+ : getDeclarationName(node);
- let symbol: Symbol | undefined;
- if (name === undefined) {
- symbol = createSymbol(SymbolFlags.None, InternalSymbolName.Missing);
- }
- else {
- // Check and see if the symbol table already has a symbol with this name. If not,
- // create a new symbol with this name and add it to the table. Note that we don't
- // give the new symbol any flags *yet*. This ensures that it will not conflict
- // with the 'excludes' flags we pass in.
- //
- // If we do get an existing symbol, see if it conflicts with the new symbol we're
- // creating. For example, a 'var' symbol and a 'class' symbol will conflict within
- // the same symbol table. If we have a conflict, report the issue on each
- // declaration we have for this symbol, and then create a new symbol for this
- // declaration.
- //
- // Note that when properties declared in Javascript constructors
- // (marked by isReplaceableByMethod) conflict with another symbol, the property loses.
- // Always. This allows the common Javascript pattern of overwriting a prototype method
- // with an bound instance method of the same type: `this.method = this.method.bind(this)`
- //
- // If we created a new symbol, either because we didn't have a symbol with this name
- // in the symbol table, or we conflicted with an existing symbol, then just add this
- // node as the sole declaration of the new symbol.
- //
- // Otherwise, we'll be merging into a compatible existing symbol (for example when
- // you have multiple 'vars' with the same name in the same container). In this case
- // just add this node into the declarations list of the symbol.
- symbol = symbolTable.get(name);
+ let symbol: ts.Symbol | undefined;
+ if (name === undefined) {
+ symbol = createSymbol(SymbolFlags.None, InternalSymbolName.Missing);
+ }
+ else {
+ // Check and see if the symbol table already has a symbol with this name. If not,
+ // create a new symbol with this name and add it to the table. Note that we don't
+ // give the new symbol any flags *yet*. This ensures that it will not conflict
+ // with the 'excludes' flags we pass in.
+ //
+ // If we do get an existing symbol, see if it conflicts with the new symbol we're
+ // creating. For example, a 'var' symbol and a 'class' symbol will conflict within
+ // the same symbol table. If we have a conflict, report the issue on each
+ // declaration we have for this symbol, and then create a new symbol for this
+ // declaration.
+ //
+ // Note that when properties declared in Javascript constructors
+ // (marked by isReplaceableByMethod) conflict with another symbol, the property loses.
+ // Always. This allows the common Javascript pattern of overwriting a prototype method
+ // with an bound instance method of the same type: `this.method = this.method.bind(this)`
+ //
+ // If we created a new symbol, either because we didn't have a symbol with this name
+ // in the symbol table, or we conflicted with an existing symbol, then just add this
+ // node as the sole declaration of the new symbol.
+ //
+ // Otherwise, we'll be merging into a compatible existing symbol (for example when
+ // you have multiple 'vars' with the same name in the same container). In this case
+ // just add this node into the declarations list of the symbol.
+ symbol = symbolTable.get(name);
- if (includes & SymbolFlags.Classifiable) {
- classifiableNames.add(name);
- }
+ if (includes & SymbolFlags.Classifiable) {
+ classifiableNames.add(name);
+ }
- if (!symbol) {
+ if (!symbol) {
+ symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name));
+ if (isReplaceableByMethod)
+ symbol.isReplaceableByMethod = true;
+ }
+ else if (isReplaceableByMethod && !symbol.isReplaceableByMethod) {
+ // A symbol already exists, so don't add this as a declaration.
+ return symbol;
+ }
+ else if (symbol.flags & excludes) {
+ if (symbol.isReplaceableByMethod) {
+ // Javascript constructor-declared symbols can be discarded in favor of
+ // prototype symbols like methods.
symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name));
- if (isReplaceableByMethod) symbol.isReplaceableByMethod = true;
- }
- else if (isReplaceableByMethod && !symbol.isReplaceableByMethod) {
- // A symbol already exists, so don't add this as a declaration.
- return symbol;
}
- else if (symbol.flags & excludes) {
- if (symbol.isReplaceableByMethod) {
- // Javascript constructor-declared symbols can be discarded in favor of
- // prototype symbols like methods.
- symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name));
- }
- else if (!(includes & SymbolFlags.Variable && symbol.flags & SymbolFlags.Assignment)) {
- // Assignment declarations are allowed to merge with variables, no matter what other flags they have.
- if (isNamedDeclaration(node)) {
- setParent(node.name, node);
- }
- // Report errors every position with duplicate declaration
- // Report errors on previous encountered declarations
- let message = symbol.flags & SymbolFlags.BlockScopedVariable
- ? Diagnostics.Cannot_redeclare_block_scoped_variable_0
- : Diagnostics.Duplicate_identifier_0;
- let messageNeedsName = true;
-
- if (symbol.flags & SymbolFlags.Enum || includes & SymbolFlags.Enum) {
- message = Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations;
+ else if (!(includes & SymbolFlags.Variable && symbol.flags & SymbolFlags.Assignment)) {
+ // Assignment declarations are allowed to merge with variables, no matter what other flags they have.
+ if (isNamedDeclaration(node)) {
+ setParent(node.name, node);
+ }
+ // Report errors every position with duplicate declaration
+ // Report errors on previous encountered declarations
+ let message = symbol.flags & SymbolFlags.BlockScopedVariable
+ ? Diagnostics.Cannot_redeclare_block_scoped_variable_0
+ : Diagnostics.Duplicate_identifier_0;
+ let messageNeedsName = true;
+
+ if (symbol.flags & SymbolFlags.Enum || includes & SymbolFlags.Enum) {
+ message = Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations;
+ messageNeedsName = false;
+ }
+
+ let multipleDefaultExports = false;
+ if (length(symbol.declarations)) {
+ // If the current node is a default export of some sort, then check if
+ // there are any other default exports that we need to error on.
+ // We'll know whether we have other default exports depending on if `symbol` already has a declaration list set.
+ if (isDefaultExport) {
+ message = Diagnostics.A_module_cannot_have_multiple_default_exports;
messageNeedsName = false;
+ multipleDefaultExports = true;
}
-
- let multipleDefaultExports = false;
- if (length(symbol.declarations)) {
- // If the current node is a default export of some sort, then check if
- // there are any other default exports that we need to error on.
- // We'll know whether we have other default exports depending on if `symbol` already has a declaration list set.
- if (isDefaultExport) {
+ else {
+ // This is to properly report an error in the case "export default { }" is after export default of class declaration or function declaration.
+ // Error on multiple export default in the following case:
+ // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default
+ // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers)
+ if (symbol.declarations && symbol.declarations.length &&
+ (node.kind === SyntaxKind.ExportAssignment && !(node as ExportAssignment).isExportEquals)) {
message = Diagnostics.A_module_cannot_have_multiple_default_exports;
messageNeedsName = false;
multipleDefaultExports = true;
}
- else {
- // This is to properly report an error in the case "export default { }" is after export default of class declaration or function declaration.
- // Error on multiple export default in the following case:
- // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default
- // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers)
- if (symbol.declarations && symbol.declarations.length &&
- (node.kind === SyntaxKind.ExportAssignment && !(node as ExportAssignment).isExportEquals)) {
- message = Diagnostics.A_module_cannot_have_multiple_default_exports;
- messageNeedsName = false;
- multipleDefaultExports = true;
- }
- }
}
+ }
- const relatedInformation: DiagnosticRelatedInformation[] = [];
- if (isTypeAliasDeclaration(node) && nodeIsMissing(node.type) && hasSyntacticModifier(node, ModifierFlags.Export) && symbol.flags & (SymbolFlags.Alias | SymbolFlags.Type | SymbolFlags.Namespace)) {
- // export type T; - may have meant export type { T }?
- relatedInformation.push(createDiagnosticForNode(node, Diagnostics.Did_you_mean_0, `export type { ${unescapeLeadingUnderscores(node.name.escapedText)} }`));
- }
+ const relatedInformation: DiagnosticRelatedInformation[] = [];
+ if (isTypeAliasDeclaration(node) && nodeIsMissing(node.type) && hasSyntacticModifier(node, ModifierFlags.Export) && symbol.flags & (SymbolFlags.Alias | SymbolFlags.Type | SymbolFlags.Namespace)) {
+ // export type T; - may have meant export type { T }?
+ relatedInformation.push(createDiagnosticForNode(node, Diagnostics.Did_you_mean_0, `export type { ${unescapeLeadingUnderscores(node.name.escapedText)} }`));
+ }
- const declarationName = getNameOfDeclaration(node) || node;
- forEach(symbol.declarations, (declaration, index) => {
- const decl = getNameOfDeclaration(declaration) || declaration;
- const diag = createDiagnosticForNode(decl, message, messageNeedsName ? getDisplayName(declaration) : undefined);
- file.bindDiagnostics.push(
- multipleDefaultExports ? addRelatedInfo(diag, createDiagnosticForNode(declarationName, index === 0 ? Diagnostics.Another_export_default_is_here : Diagnostics.and_here)) : diag
- );
- if (multipleDefaultExports) {
- relatedInformation.push(createDiagnosticForNode(decl, Diagnostics.The_first_export_default_is_here));
- }
- });
+ const declarationName = getNameOfDeclaration(node) || node;
+ forEach(symbol.declarations, (declaration, index) => {
+ const decl = getNameOfDeclaration(declaration) || declaration;
+ const diag = createDiagnosticForNode(decl, message, messageNeedsName ? getDisplayName(declaration) : undefined);
+ file.bindDiagnostics.push(multipleDefaultExports ? addRelatedInfo(diag, createDiagnosticForNode(declarationName, index === 0 ? Diagnostics.Another_export_default_is_here : Diagnostics.and_here)) : diag);
+ if (multipleDefaultExports) {
+ relatedInformation.push(createDiagnosticForNode(decl, Diagnostics.The_first_export_default_is_here));
+ }
+ });
- const diag = createDiagnosticForNode(declarationName, message, messageNeedsName ? getDisplayName(node) : undefined);
- file.bindDiagnostics.push(addRelatedInfo(diag, ...relatedInformation));
+ const diag = createDiagnosticForNode(declarationName, message, messageNeedsName ? getDisplayName(node) : undefined);
+ file.bindDiagnostics.push(addRelatedInfo(diag, ...relatedInformation));
- symbol = createSymbol(SymbolFlags.None, name);
- }
+ symbol = createSymbol(SymbolFlags.None, name);
}
}
+ }
+
+ addDeclarationToSymbol(symbol, node, includes);
+ if (symbol.parent) {
+ Debug.assert(symbol.parent === parent, "Existing symbol parent should match new one");
+ }
+ else {
+ symbol.parent = parent;
+ }
+
+ return symbol;
+ }
- addDeclarationToSymbol(symbol, node, includes);
- if (symbol.parent) {
- Debug.assert(symbol.parent === parent, "Existing symbol parent should match new one");
+ function declareModuleMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): ts.Symbol {
+ const hasExportModifier = !!(getCombinedModifierFlags(node) & ModifierFlags.Export) || jsdocTreatAsExported(node);
+ if (symbolFlags & SymbolFlags.Alias) {
+ if (node.kind === SyntaxKind.ExportSpecifier || (node.kind === SyntaxKind.ImportEqualsDeclaration && hasExportModifier)) {
+ return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes);
}
else {
- symbol.parent = parent;
+ return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
}
-
- return symbol;
}
-
- function declareModuleMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol {
- const hasExportModifier = !!(getCombinedModifierFlags(node) & ModifierFlags.Export) || jsdocTreatAsExported(node);
- if (symbolFlags & SymbolFlags.Alias) {
- if (node.kind === SyntaxKind.ExportSpecifier || (node.kind === SyntaxKind.ImportEqualsDeclaration && hasExportModifier)) {
- return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes);
- }
- else {
- return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
- }
+ else {
+ // Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue flag,
+ // and an associated export symbol with all the correct flags set on it. There are 2 main reasons:
+ //
+ // 1. We treat locals and exports of the same name as mutually exclusive within a container.
+ // That means the binder will issue a Duplicate Identifier error if you mix locals and exports
+ // with the same name in the same container.
+ // TODO: Make this a more specific error and decouple it from the exclusion logic.
+ // 2. When we checkIdentifier in the checker, we set its resolved symbol to the local symbol,
+ // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way
+ // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope.
+
+ // NOTE: Nested ambient modules always should go to to 'locals' table to prevent their automatic merge
+ // during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation
+ // and this case is specially handled. Module augmentations should only be merged with original module definition
+ // and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed.
+ if (isJSDocTypeAlias(node))
+ Debug.assert(isInJSFile(node)); // We shouldn't add symbols for JSDoc nodes if not in a JS file.
+ if (!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) {
+ if (!container.locals || (hasSyntacticModifier(node, ModifierFlags.Default) && !getDeclarationName(node))) {
+ return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); // No local symbol for an unnamed default!
+ }
+ const exportKind = symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0;
+ const local = declareSymbol(container.locals, /*parent*/ undefined, node, exportKind, symbolExcludes);
+ local.exportSymbol = declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes);
+ node.localSymbol = local;
+ return local;
}
else {
- // Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue flag,
- // and an associated export symbol with all the correct flags set on it. There are 2 main reasons:
- //
- // 1. We treat locals and exports of the same name as mutually exclusive within a container.
- // That means the binder will issue a Duplicate Identifier error if you mix locals and exports
- // with the same name in the same container.
- // TODO: Make this a more specific error and decouple it from the exclusion logic.
- // 2. When we checkIdentifier in the checker, we set its resolved symbol to the local symbol,
- // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way
- // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope.
-
- // NOTE: Nested ambient modules always should go to to 'locals' table to prevent their automatic merge
- // during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation
- // and this case is specially handled. Module augmentations should only be merged with original module definition
- // and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed.
- if (isJSDocTypeAlias(node)) Debug.assert(isInJSFile(node)); // We shouldn't add symbols for JSDoc nodes if not in a JS file.
- if (!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) {
- if (!container.locals || (hasSyntacticModifier(node, ModifierFlags.Default) && !getDeclarationName(node))) {
- return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); // No local symbol for an unnamed default!
- }
- const exportKind = symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0;
- const local = declareSymbol(container.locals, /*parent*/ undefined, node, exportKind, symbolExcludes);
- local.exportSymbol = declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes);
- node.localSymbol = local;
- return local;
- }
- else {
- return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
- }
+ return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
}
}
+ }
- function jsdocTreatAsExported(node: Node) {
- if (node.parent && isModuleDeclaration(node)) {
- node = node.parent;
- }
- if (!isJSDocTypeAlias(node)) return false;
- // jsdoc typedef handling is a bit of a doozy, but to summarize, treat the typedef as exported if:
- // 1. It has an explicit name (since by default typedefs are always directly exported, either at the top level or in a container), or
- if (!isJSDocEnumTag(node) && !!node.fullName) return true;
- // 2. The thing a nameless typedef pulls its name from is implicitly a direct export (either by assignment or actual export flag).
- const declName = getNameOfDeclaration(node);
- if (!declName) return false;
- if (isPropertyAccessEntityNameExpression(declName.parent) && isTopLevelNamespaceAssignment(declName.parent)) return true;
- if (isDeclaration(declName.parent) && getCombinedModifierFlags(declName.parent) & ModifierFlags.Export) return true;
- // This could potentially be simplified by having `delayedBindJSDocTypedefTag` pass in an override for `hasExportModifier`, since it should
- // already have calculated and branched on most of this.
- return false;
+ function jsdocTreatAsExported(node: Node) {
+ if (node.parent && isModuleDeclaration(node)) {
+ node = node.parent;
}
+ if (!isJSDocTypeAlias(node))
+ return false;
+ // jsdoc typedef handling is a bit of a doozy, but to summarize, treat the typedef as exported if:
+ // 1. It has an explicit name (since by default typedefs are always directly exported, either at the top level or in a container), or
+ if (!isJSDocEnumTag(node) && !!node.fullName)
+ return true;
+ // 2. The thing a nameless typedef pulls its name from is implicitly a direct export (either by assignment or actual export flag).
+ const declName = getNameOfDeclaration(node);
+ if (!declName)
+ return false;
+ if (isPropertyAccessEntityNameExpression(declName.parent) && isTopLevelNamespaceAssignment(declName.parent))
+ return true;
+ if (isDeclaration(declName.parent) && getCombinedModifierFlags(declName.parent) & ModifierFlags.Export)
+ return true;
+ // This could potentially be simplified by having `delayedBindJSDocTypedefTag` pass in an override for `hasExportModifier`, since it should
+ // already have calculated and branched on most of this.
+ return false;
+ }
- // All container nodes are kept on a linked list in declaration order. This list is used by
- // the getLocalNameOfContainer function in the type checker to validate that the local name
- // used for a container is unique.
- function bindContainer(node: Mutable, containerFlags: ContainerFlags) {
- // Before we recurse into a node's children, we first save the existing parent, container
- // and block-container. Then after we pop out of processing the children, we restore
- // these saved values.
- const saveContainer = container;
- const saveThisParentContainer = thisParentContainer;
- const savedBlockScopeContainer = blockScopeContainer;
-
- // Depending on what kind of node this is, we may have to adjust the current container
- // and block-container. If the current node is a container, then it is automatically
- // considered the current block-container as well. Also, for containers that we know
- // may contain locals, we eagerly initialize the .locals field. We do this because
- // it's highly likely that the .locals will be needed to place some child in (for example,
- // a parameter, or variable declaration).
- //
- // However, we do not proactively create the .locals for block-containers because it's
- // totally normal and common for block-containers to never actually have a block-scoped
- // variable in them. We don't want to end up allocating an object for every 'block' we
- // run into when most of them won't be necessary.
- //
- // Finally, if this is a block-container, then we clear out any existing .locals object
- // it may contain within it. This happens in incremental scenarios. Because we can be
- // reusing a node from a previous compilation, that node may have had 'locals' created
- // for it. We must clear this so we don't accidentally move any stale data forward from
- // a previous compilation.
- if (containerFlags & ContainerFlags.IsContainer) {
- if (node.kind !== SyntaxKind.ArrowFunction) {
- thisParentContainer = container;
- }
- container = blockScopeContainer = node;
- if (containerFlags & ContainerFlags.HasLocals) {
- container.locals = createSymbolTable();
- }
- addToContainerChain(container);
- }
- else if (containerFlags & ContainerFlags.IsBlockScopedContainer) {
- blockScopeContainer = node;
- blockScopeContainer.locals = undefined;
- }
- if (containerFlags & ContainerFlags.IsControlFlowContainer) {
- const saveCurrentFlow = currentFlow;
- const saveBreakTarget = currentBreakTarget;
- const saveContinueTarget = currentContinueTarget;
- const saveReturnTarget = currentReturnTarget;
- const saveExceptionTarget = currentExceptionTarget;
- const saveActiveLabelList = activeLabelList;
- const saveHasExplicitReturn = hasExplicitReturn;
- const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !hasSyntacticModifier(node, ModifierFlags.Async) &&
- !(node as FunctionLikeDeclaration).asteriskToken && !!getImmediatelyInvokedFunctionExpression(node);
- // A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave
- // similarly to break statements that exit to a label just past the statement body.
- if (!isIIFE) {
- currentFlow = initFlowNode({ flags: FlowFlags.Start });
- if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor)) {
- currentFlow.node = node as FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration;
- }
- }
- // We create a return control flow graph for IIFEs and constructors. For constructors
- // we use the return control flow graph in strict property initialization checks.
- currentReturnTarget = isIIFE || node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined;
- currentExceptionTarget = undefined;
- currentBreakTarget = undefined;
- currentContinueTarget = undefined;
- activeLabelList = undefined;
- hasExplicitReturn = false;
- bindChildren(node);
- // Reset all reachability check related flags on node (for incremental scenarios)
- node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
- if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).body)) {
- node.flags |= NodeFlags.HasImplicitReturn;
- if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn;
- (node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).endFlowNode = currentFlow;
- }
- if (node.kind === SyntaxKind.SourceFile) {
- node.flags |= emitFlags;
- (node as SourceFile).endFlowNode = currentFlow;
- }
-
- if (currentReturnTarget) {
- addAntecedent(currentReturnTarget, currentFlow);
- currentFlow = finishFlowLabel(currentReturnTarget);
- if (node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) {
- (node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).returnFlowNode = currentFlow;
- }
- }
- if (!isIIFE) {
- currentFlow = saveCurrentFlow;
+ // All container nodes are kept on a linked list in declaration order. This list is used by
+ // the getLocalNameOfContainer function in the type checker to validate that the local name
+ // used for a container is unique.
+ function bindContainer(node: Mutable, containerFlags: ContainerFlags) {
+ // Before we recurse into a node's children, we first save the existing parent, container
+ // and block-container. Then after we pop out of processing the children, we restore
+ // these saved values.
+ const saveContainer = container;
+ const saveThisParentContainer = thisParentContainer;
+ const savedBlockScopeContainer = blockScopeContainer;
+
+ // Depending on what kind of node this is, we may have to adjust the current container
+ // and block-container. If the current node is a container, then it is automatically
+ // considered the current block-container as well. Also, for containers that we know
+ // may contain locals, we eagerly initialize the .locals field. We do this because
+ // it's highly likely that the .locals will be needed to place some child in (for example,
+ // a parameter, or variable declaration).
+ //
+ // However, we do not proactively create the .locals for block-containers because it's
+ // totally normal and common for block-containers to never actually have a block-scoped
+ // variable in them. We don't want to end up allocating an object for every 'block' we
+ // run into when most of them won't be necessary.
+ //
+ // Finally, if this is a block-container, then we clear out any existing .locals object
+ // it may contain within it. This happens in incremental scenarios. Because we can be
+ // reusing a node from a previous compilation, that node may have had 'locals' created
+ // for it. We must clear this so we don't accidentally move any stale data forward from
+ // a previous compilation.
+ if (containerFlags & ContainerFlags.IsContainer) {
+ if (node.kind !== SyntaxKind.ArrowFunction) {
+ thisParentContainer = container;
+ }
+ container = blockScopeContainer = node;
+ if (containerFlags & ContainerFlags.HasLocals) {
+ container.locals = createSymbolTable();
+ }
+ addToContainerChain(container);
+ }
+ else if (containerFlags & ContainerFlags.IsBlockScopedContainer) {
+ blockScopeContainer = node;
+ blockScopeContainer.locals = undefined;
+ }
+ if (containerFlags & ContainerFlags.IsControlFlowContainer) {
+ const saveCurrentFlow = currentFlow;
+ const saveBreakTarget = currentBreakTarget;
+ const saveContinueTarget = currentContinueTarget;
+ const saveReturnTarget = currentReturnTarget;
+ const saveExceptionTarget = currentExceptionTarget;
+ const saveActiveLabelList = activeLabelList;
+ const saveHasExplicitReturn = hasExplicitReturn;
+ const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !hasSyntacticModifier(node, ModifierFlags.Async) &&
+ !(node as FunctionLikeDeclaration).asteriskToken && !!getImmediatelyInvokedFunctionExpression(node);
+ // A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave
+ // similarly to break statements that exit to a label just past the statement body.
+ if (!isIIFE) {
+ currentFlow = initFlowNode({ flags: FlowFlags.Start });
+ if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor)) {
+ currentFlow.node = node as FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration;
}
- currentBreakTarget = saveBreakTarget;
- currentContinueTarget = saveContinueTarget;
- currentReturnTarget = saveReturnTarget;
- currentExceptionTarget = saveExceptionTarget;
- activeLabelList = saveActiveLabelList;
- hasExplicitReturn = saveHasExplicitReturn;
}
- else if (containerFlags & ContainerFlags.IsInterface) {
- seenThisKeyword = false;
- bindChildren(node);
- node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis;
+ // We create a return control flow graph for IIFEs and constructors. For constructors
+ // we use the return control flow graph in strict property initialization checks.
+ currentReturnTarget = isIIFE || node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined;
+ currentExceptionTarget = undefined;
+ currentBreakTarget = undefined;
+ currentContinueTarget = undefined;
+ activeLabelList = undefined;
+ hasExplicitReturn = false;
+ bindChildren(node);
+ // Reset all reachability check related flags on node (for incremental scenarios)
+ node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
+ if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).body)) {
+ node.flags |= NodeFlags.HasImplicitReturn;
+ if (hasExplicitReturn)
+ node.flags |= NodeFlags.HasExplicitReturn;
+ (node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).endFlowNode = currentFlow;
}
- else {
- bindChildren(node);
+ if (node.kind === SyntaxKind.SourceFile) {
+ node.flags |= emitFlags;
+ (node as SourceFile).endFlowNode = currentFlow;
}
- container = saveContainer;
- thisParentContainer = saveThisParentContainer;
- blockScopeContainer = savedBlockScopeContainer;
+ if (currentReturnTarget) {
+ addAntecedent(currentReturnTarget, currentFlow);
+ currentFlow = finishFlowLabel(currentReturnTarget);
+ if (node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) {
+ (node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).returnFlowNode = currentFlow;
+ }
+ }
+ if (!isIIFE) {
+ currentFlow = saveCurrentFlow;
+ }
+ currentBreakTarget = saveBreakTarget;
+ currentContinueTarget = saveContinueTarget;
+ currentReturnTarget = saveReturnTarget;
+ currentExceptionTarget = saveExceptionTarget;
+ activeLabelList = saveActiveLabelList;
+ hasExplicitReturn = saveHasExplicitReturn;
}
-
- function bindEachFunctionsFirst(nodes: NodeArray | undefined): void {
- bindEach(nodes, n => n.kind === SyntaxKind.FunctionDeclaration ? bind(n) : undefined);
- bindEach(nodes, n => n.kind !== SyntaxKind.FunctionDeclaration ? bind(n) : undefined);
+ else if (containerFlags & ContainerFlags.IsInterface) {
+ seenThisKeyword = false;
+ bindChildren(node);
+ node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis;
+ }
+ else {
+ bindChildren(node);
}
- function bindEach(nodes: NodeArray | undefined, bindFunction: (node: Node) => void = bind): void {
- if (nodes === undefined) {
- return;
- }
+ container = saveContainer;
+ thisParentContainer = saveThisParentContainer;
+ blockScopeContainer = savedBlockScopeContainer;
+ }
- forEach(nodes, bindFunction);
- }
+ function bindEachFunctionsFirst(nodes: NodeArray | undefined): void {
+ bindEach(nodes, n => n.kind === SyntaxKind.FunctionDeclaration ? bind(n) : undefined);
+ bindEach(nodes, n => n.kind !== SyntaxKind.FunctionDeclaration ? bind(n) : undefined);
+ }
- function bindEachChild(node: Node) {
- forEachChild(node, bind, bindEach);
+ function bindEach(nodes: NodeArray | undefined, bindFunction: (node: Node) => void = bind): void {
+ if (nodes === undefined) {
+ return;
}
- function bindChildren(node: Node): void {
- const saveInAssignmentPattern = inAssignmentPattern;
- // Most nodes aren't valid in an assignment pattern, so we clear the value here
- // and set it before we descend into nodes that could actually be part of an assignment pattern.
- inAssignmentPattern = false;
- if (checkUnreachable(node)) {
- bindEachChild(node);
- bindJSDoc(node);
- inAssignmentPattern = saveInAssignmentPattern;
- return;
- }
- if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && !options.allowUnreachableCode) {
- node.flowNode = currentFlow;
- }
- switch (node.kind) {
- case SyntaxKind.WhileStatement:
- bindWhileStatement(node as WhileStatement);
- break;
- case SyntaxKind.DoStatement:
- bindDoStatement(node as DoStatement);
- break;
- case SyntaxKind.ForStatement:
- bindForStatement(node as ForStatement);
- break;
- case SyntaxKind.ForInStatement:
- case SyntaxKind.ForOfStatement:
- bindForInOrForOfStatement(node as ForInOrOfStatement);
- break;
- case SyntaxKind.IfStatement:
- bindIfStatement(node as IfStatement);
- break;
- case SyntaxKind.ReturnStatement:
- case SyntaxKind.ThrowStatement:
- bindReturnOrThrow(node as ReturnStatement | ThrowStatement);
- break;
- case SyntaxKind.BreakStatement:
- case SyntaxKind.ContinueStatement:
- bindBreakOrContinueStatement(node as BreakOrContinueStatement);
- break;
- case SyntaxKind.TryStatement:
- bindTryStatement(node as TryStatement);
- break;
- case SyntaxKind.SwitchStatement:
- bindSwitchStatement(node as SwitchStatement);
- break;
- case SyntaxKind.CaseBlock:
- bindCaseBlock(node as CaseBlock);
- break;
- case SyntaxKind.CaseClause:
- bindCaseClause(node as CaseClause);
- break;
- case SyntaxKind.ExpressionStatement:
- bindExpressionStatement(node as ExpressionStatement);
- break;
- case SyntaxKind.LabeledStatement:
- bindLabeledStatement(node as LabeledStatement);
- break;
- case SyntaxKind.PrefixUnaryExpression:
- bindPrefixUnaryExpressionFlow(node as PrefixUnaryExpression);
- break;
- case SyntaxKind.PostfixUnaryExpression:
- bindPostfixUnaryExpressionFlow(node as PostfixUnaryExpression);
- break;
- case SyntaxKind.BinaryExpression:
- if (isDestructuringAssignment(node)) {
- // Carry over whether we are in an assignment pattern to
- // binary expressions that could actually be an initializer
- inAssignmentPattern = saveInAssignmentPattern;
- bindDestructuringAssignmentFlow(node);
- return;
- }
- bindBinaryExpressionFlow(node as BinaryExpression);
- break;
- case SyntaxKind.DeleteExpression:
- bindDeleteExpressionFlow(node as DeleteExpression);
- break;
- case SyntaxKind.ConditionalExpression:
- bindConditionalExpressionFlow(node as ConditionalExpression);
- break;
- case SyntaxKind.VariableDeclaration:
- bindVariableDeclarationFlow(node as VariableDeclaration);
- break;
- case SyntaxKind.PropertyAccessExpression:
- case SyntaxKind.ElementAccessExpression:
- bindAccessExpressionFlow(node as AccessExpression);
- break;
- case SyntaxKind.CallExpression:
- bindCallExpressionFlow(node as CallExpression);
- break;
- case SyntaxKind.NonNullExpression:
- bindNonNullExpressionFlow(node as NonNullExpression);
- break;
- case SyntaxKind.JSDocTypedefTag:
- case SyntaxKind.JSDocCallbackTag:
- case SyntaxKind.JSDocEnumTag:
- bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag);
- break;
- // In source files and blocks, bind functions first to match hoisting that occurs at runtime
- case SyntaxKind.SourceFile: {
- bindEachFunctionsFirst((node as SourceFile).statements);
- bind((node as SourceFile).endOfFileToken);
- break;
- }
- case SyntaxKind.Block:
- case SyntaxKind.ModuleBlock:
- bindEachFunctionsFirst((node as Block).statements);
- break;
- case SyntaxKind.BindingElement:
- bindBindingElementFlow(node as BindingElement);
- break;
- case SyntaxKind.ObjectLiteralExpression:
- case SyntaxKind.ArrayLiteralExpression:
- case SyntaxKind.PropertyAssignment:
- case SyntaxKind.SpreadElement:
- // Carry over whether we are in an assignment pattern of Object and Array literals
- // as well as their children that are valid assignment targets.
- inAssignmentPattern = saveInAssignmentPattern;
- // falls through
- default:
- bindEachChild(node);
- break;
- }
+ forEach(nodes, bindFunction);
+ }
+
+ function bindEachChild(node: Node) {
+ forEachChild(node, bind, bindEach);
+ }
+
+ function bindChildren(node: Node): void {
+ const saveInAssignmentPattern = inAssignmentPattern;
+ // Most nodes aren't valid in an assignment pattern, so we clear the value here
+ // and set it before we descend into nodes that could actually be part of an assignment pattern.
+ inAssignmentPattern = false;
+ if (checkUnreachable(node)) {
+ bindEachChild(node);
bindJSDoc(node);
inAssignmentPattern = saveInAssignmentPattern;
+ return;
}
-
- function isNarrowingExpression(expr: Expression): boolean {
- switch (expr.kind) {
- case SyntaxKind.Identifier:
- case SyntaxKind.PrivateIdentifier:
- case SyntaxKind.ThisKeyword:
- case SyntaxKind.PropertyAccessExpression:
- case SyntaxKind.ElementAccessExpression:
- return containsNarrowableReference(expr);
- case SyntaxKind.CallExpression:
- return hasNarrowableArgument(expr as CallExpression);
- case SyntaxKind.ParenthesizedExpression:
- case SyntaxKind.NonNullExpression:
- return isNarrowingExpression((expr as ParenthesizedExpression | NonNullExpression).expression);
- case SyntaxKind.BinaryExpression:
- return isNarrowingBinaryExpression(expr as BinaryExpression);
- case SyntaxKind.PrefixUnaryExpression:
- return (expr as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken && isNarrowingExpression((expr as PrefixUnaryExpression).operand);
- case SyntaxKind.TypeOfExpression:
- return isNarrowingExpression((expr as TypeOfExpression).expression);
+ if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && !options.allowUnreachableCode) {
+ node.flowNode = currentFlow;
+ }
+ switch (node.kind) {
+ case SyntaxKind.WhileStatement:
+ bindWhileStatement(node as WhileStatement);
+ break;
+ case SyntaxKind.DoStatement:
+ bindDoStatement(node as DoStatement);
+ break;
+ case SyntaxKind.ForStatement:
+ bindForStatement(node as ForStatement);
+ break;
+ case SyntaxKind.ForInStatement:
+ case SyntaxKind.ForOfStatement:
+ bindForInOrForOfStatement(node as ForInOrOfStatement);
+ break;
+ case SyntaxKind.IfStatement:
+ bindIfStatement(node as IfStatement);
+ break;
+ case SyntaxKind.ReturnStatement:
+ case SyntaxKind.ThrowStatement:
+ bindReturnOrThrow(node as ReturnStatement | ThrowStatement);
+ break;
+ case SyntaxKind.BreakStatement:
+ case SyntaxKind.ContinueStatement:
+ bindBreakOrContinueStatement(node as BreakOrContinueStatement);
+ break;
+ case SyntaxKind.TryStatement:
+ bindTryStatement(node as TryStatement);
+ break;
+ case SyntaxKind.SwitchStatement:
+ bindSwitchStatement(node as SwitchStatement);
+ break;
+ case SyntaxKind.CaseBlock:
+ bindCaseBlock(node as CaseBlock);
+ break;
+ case SyntaxKind.CaseClause:
+ bindCaseClause(node as CaseClause);
+ break;
+ case SyntaxKind.ExpressionStatement:
+ bindExpressionStatement(node as ExpressionStatement);
+ break;
+ case SyntaxKind.LabeledStatement:
+ bindLabeledStatement(node as LabeledStatement);
+ break;
+ case SyntaxKind.PrefixUnaryExpression:
+ bindPrefixUnaryExpressionFlow(node as PrefixUnaryExpression);
+ break;
+ case SyntaxKind.PostfixUnaryExpression:
+ bindPostfixUnaryExpressionFlow(node as PostfixUnaryExpression);
+ break;
+ case SyntaxKind.BinaryExpression:
+ if (isDestructuringAssignment(node)) {
+ // Carry over whether we are in an assignment pattern to
+ // binary expressions that could actually be an initializer
+ inAssignmentPattern = saveInAssignmentPattern;
+ bindDestructuringAssignmentFlow(node);
+ return;
+ }
+ bindBinaryExpressionFlow(node as BinaryExpression);
+ break;
+ case SyntaxKind.DeleteExpression:
+ bindDeleteExpressionFlow(node as DeleteExpression);
+ break;
+ case SyntaxKind.ConditionalExpression:
+ bindConditionalExpressionFlow(node as ConditionalExpression);
+ break;
+ case SyntaxKind.VariableDeclaration:
+ bindVariableDeclarationFlow(node as VariableDeclaration);
+ break;
+ case SyntaxKind.PropertyAccessExpression:
+ case SyntaxKind.ElementAccessExpression:
+ bindAccessExpressionFlow(node as AccessExpression);
+ break;
+ case SyntaxKind.CallExpression:
+ bindCallExpressionFlow(node as CallExpression);
+ break;
+ case SyntaxKind.NonNullExpression:
+ bindNonNullExpressionFlow(node as NonNullExpression);
+ break;
+ case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocCallbackTag:
+ case SyntaxKind.JSDocEnumTag:
+ bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag);
+ break;
+ // In source files and blocks, bind functions first to match hoisting that occurs at runtime
+ case SyntaxKind.SourceFile: {
+ bindEachFunctionsFirst((node as SourceFile).statements);
+ bind((node as SourceFile).endOfFileToken);
+ break;
}
- return false;
+ case SyntaxKind.Block:
+ case SyntaxKind.ModuleBlock:
+ bindEachFunctionsFirst((node as Block).statements);
+ break;
+ case SyntaxKind.BindingElement:
+ bindBindingElementFlow(node as BindingElement);
+ break;
+ case SyntaxKind.ObjectLiteralExpression:
+ case SyntaxKind.ArrayLiteralExpression:
+ case SyntaxKind.PropertyAssignment:
+ case SyntaxKind.SpreadElement:
+ // Carry over whether we are in an assignment pattern of Object and Array literals
+ // as well as their children that are valid assignment targets.
+ inAssignmentPattern = saveInAssignmentPattern;
+ // falls through
+ default:
+ bindEachChild(node);
+ break;
}
+ bindJSDoc(node);
+ inAssignmentPattern = saveInAssignmentPattern;
+ }
- function isNarrowableReference(expr: Expression): boolean {
- return isDottedName(expr)
- || (isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression)
- || isBinaryExpression(expr) && expr.operatorToken.kind === SyntaxKind.CommaToken && isNarrowableReference(expr.right)
- || isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression)
- || isAssignmentExpression(expr) && isNarrowableReference(expr.left);
+ function isNarrowingExpression(expr: Expression): boolean {
+ switch (expr.kind) {
+ case SyntaxKind.Identifier:
+ case SyntaxKind.PrivateIdentifier:
+ case SyntaxKind.ThisKeyword:
+ case SyntaxKind.PropertyAccessExpression:
+ case SyntaxKind.ElementAccessExpression:
+ return containsNarrowableReference(expr);
+ case SyntaxKind.CallExpression:
+ return hasNarrowableArgument(expr as CallExpression);
+ case SyntaxKind.ParenthesizedExpression:
+ case SyntaxKind.NonNullExpression:
+ return isNarrowingExpression((expr as ParenthesizedExpression | NonNullExpression).expression);
+ case SyntaxKind.BinaryExpression:
+ return isNarrowingBinaryExpression(expr as BinaryExpression);
+ case SyntaxKind.PrefixUnaryExpression:
+ return (expr as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken && isNarrowingExpression((expr as PrefixUnaryExpression).operand);
+ case SyntaxKind.TypeOfExpression:
+ return isNarrowingExpression((expr as TypeOfExpression).expression);
}
+ return false;
+ }
- function containsNarrowableReference(expr: Expression): boolean {
- return isNarrowableReference(expr) || isOptionalChain(expr) && containsNarrowableReference(expr.expression);
- }
+ function isNarrowableReference(expr: Expression): boolean {
+ return isDottedName(expr)
+ || (isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression)
+ || isBinaryExpression(expr) && expr.operatorToken.kind === SyntaxKind.CommaToken && isNarrowableReference(expr.right)
+ || isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression)
+ || isAssignmentExpression(expr) && isNarrowableReference(expr.left);
+ }
- function hasNarrowableArgument(expr: CallExpression) {
- if (expr.arguments) {
- for (const argument of expr.arguments) {
- if (containsNarrowableReference(argument)) {
- return true;
- }
+ function containsNarrowableReference(expr: Expression): boolean {
+ return isNarrowableReference(expr) || isOptionalChain(expr) && containsNarrowableReference(expr.expression);
+ }
+
+ function hasNarrowableArgument(expr: CallExpression) {
+ if (expr.arguments) {
+ for (const argument of expr.arguments) {
+ if (containsNarrowableReference(argument)) {
+ return true;
}
}
- if (expr.expression.kind === SyntaxKind.PropertyAccessExpression &&
- containsNarrowableReference((expr.expression as PropertyAccessExpression).expression)) {
- return true;
- }
- return false;
}
-
- function isNarrowingTypeofOperands(expr1: Expression, expr2: Expression) {
- return isTypeOfExpression(expr1) && isNarrowableOperand(expr1.expression) && isStringLiteralLike(expr2);
- }
-
- function isNarrowingBinaryExpression(expr: BinaryExpression) {
- switch (expr.operatorToken.kind) {
- case SyntaxKind.EqualsToken:
- case SyntaxKind.BarBarEqualsToken:
- case SyntaxKind.AmpersandAmpersandEqualsToken:
- case SyntaxKind.QuestionQuestionEqualsToken:
- return containsNarrowableReference(expr.left);
- case SyntaxKind.EqualsEqualsToken:
- case SyntaxKind.ExclamationEqualsToken:
- case SyntaxKind.EqualsEqualsEqualsToken:
- case SyntaxKind.ExclamationEqualsEqualsToken:
- return isNarrowableOperand(expr.left) || isNarrowableOperand(expr.right) ||
- isNarrowingTypeofOperands(expr.right, expr.left) || isNarrowingTypeofOperands(expr.left, expr.right);
- case SyntaxKind.InstanceOfKeyword:
- return isNarrowableOperand(expr.left);
- case SyntaxKind.InKeyword:
- return isNarrowingExpression(expr.right);
- case SyntaxKind.CommaToken:
- return isNarrowingExpression(expr.right);
- }
- return false;
+ if (expr.expression.kind === SyntaxKind.PropertyAccessExpression &&
+ containsNarrowableReference((expr.expression as PropertyAccessExpression).expression)) {
+ return true;
}
+ return false;
+ }
- function isNarrowableOperand(expr: Expression): boolean {
- switch (expr.kind) {
- case SyntaxKind.ParenthesizedExpression:
- return isNarrowableOperand((expr as ParenthesizedExpression).expression);
- case SyntaxKind.BinaryExpression:
- switch ((expr as BinaryExpression).operatorToken.kind) {
- case SyntaxKind.EqualsToken:
- return isNarrowableOperand((expr as BinaryExpression).left);
- case SyntaxKind.CommaToken:
- return isNarrowableOperand((expr as BinaryExpression).right);
- }
- }
- return containsNarrowableReference(expr);
- }
+ function isNarrowingTypeofOperands(expr1: Expression, expr2: Expression) {
+ return isTypeOfExpression(expr1) && isNarrowableOperand(expr1.expression) && isStringLiteralLike(expr2);
+ }
- function createBranchLabel(): FlowLabel {
- return initFlowNode({ flags: FlowFlags.BranchLabel, antecedents: undefined });
+ function isNarrowingBinaryExpression(expr: BinaryExpression) {
+ switch (expr.operatorToken.kind) {
+ case SyntaxKind.EqualsToken:
+ case SyntaxKind.BarBarEqualsToken:
+ case SyntaxKind.AmpersandAmpersandEqualsToken:
+ case SyntaxKind.QuestionQuestionEqualsToken:
+ return containsNarrowableReference(expr.left);
+ case SyntaxKind.EqualsEqualsToken:
+ case SyntaxKind.ExclamationEqualsToken:
+ case SyntaxKind.EqualsEqualsEqualsToken:
+ case SyntaxKind.ExclamationEqualsEqualsToken:
+ return isNarrowableOperand(expr.left) || isNarrowableOperand(expr.right) ||
+ isNarrowingTypeofOperands(expr.right, expr.left) || isNarrowingTypeofOperands(expr.left, expr.right);
+ case SyntaxKind.InstanceOfKeyword:
+ return isNarrowableOperand(expr.left);
+ case SyntaxKind.InKeyword:
+ return isNarrowingExpression(expr.right);
+ case SyntaxKind.CommaToken:
+ return isNarrowingExpression(expr.right);
}
+ return false;
+ }
- function createLoopLabel(): FlowLabel {
- return initFlowNode({ flags: FlowFlags.LoopLabel, antecedents: undefined });
+ function isNarrowableOperand(expr: Expression): boolean {
+ switch (expr.kind) {
+ case SyntaxKind.ParenthesizedExpression:
+ return isNarrowableOperand((expr as ParenthesizedExpression).expression);
+ case SyntaxKind.BinaryExpression:
+ switch ((expr as BinaryExpression).operatorToken.kind) {
+ case SyntaxKind.EqualsToken:
+ return isNarrowableOperand((expr as BinaryExpression).left);
+ case SyntaxKind.CommaToken:
+ return isNarrowableOperand((expr as BinaryExpression).right);
+ }
}
+ return containsNarrowableReference(expr);
+ }
- function createReduceLabel(target: FlowLabel, antecedents: FlowNode[], antecedent: FlowNode): FlowReduceLabel {
- return initFlowNode({ flags: FlowFlags.ReduceLabel, target, antecedents, antecedent });
- }
+ function createBranchLabel(): FlowLabel {
+ return initFlowNode({ flags: FlowFlags.BranchLabel, antecedents: undefined });
+ }
- function setFlowNodeReferenced(flow: FlowNode) {
- // On first reference we set the Referenced flag, thereafter we set the Shared flag
- flow.flags |= flow.flags & FlowFlags.Referenced ? FlowFlags.Shared : FlowFlags.Referenced;
- }
+ function createLoopLabel(): FlowLabel {
+ return initFlowNode({ flags: FlowFlags.LoopLabel, antecedents: undefined });
+ }
- function addAntecedent(label: FlowLabel, antecedent: FlowNode): void {
- if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) {
- (label.antecedents || (label.antecedents = [])).push(antecedent);
- setFlowNodeReferenced(antecedent);
- }
- }
+ function createReduceLabel(target: FlowLabel, antecedents: FlowNode[], antecedent: FlowNode): FlowReduceLabel {
+ return initFlowNode({ flags: FlowFlags.ReduceLabel, target, antecedents, antecedent });
+ }
- function createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression | undefined): FlowNode {
- if (antecedent.flags & FlowFlags.Unreachable) {
- return antecedent;
- }
- if (!expression) {
- return flags & FlowFlags.TrueCondition ? antecedent : unreachableFlow;
- }
- if ((expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition ||
- expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) &&
- !isExpressionOfOptionalChainRoot(expression) && !isNullishCoalesce(expression.parent)) {
- return unreachableFlow;
- }
- if (!isNarrowingExpression(expression)) {
- return antecedent;
- }
- setFlowNodeReferenced(antecedent);
- return initFlowNode({ flags, antecedent, node: expression });
- }
+ function setFlowNodeReferenced(flow: FlowNode) {
+ // On first reference we set the Referenced flag, thereafter we set the Shared flag
+ flow.flags |= flow.flags & FlowFlags.Referenced ? FlowFlags.Shared : FlowFlags.Referenced;
+ }
- function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
+ function addAntecedent(label: FlowLabel, antecedent: FlowNode): void {
+ if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) {
+ (label.antecedents || (label.antecedents = [])).push(antecedent);
setFlowNodeReferenced(antecedent);
- return initFlowNode({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd });
}
+ }
- function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Expression | VariableDeclaration | ArrayBindingElement): FlowNode {
- setFlowNodeReferenced(antecedent);
- const result = initFlowNode({ flags, antecedent, node });
- if (currentExceptionTarget) {
- addAntecedent(currentExceptionTarget, result);
- }
- return result;
+ function createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression | undefined): FlowNode {
+ if (antecedent.flags & FlowFlags.Unreachable) {
+ return antecedent;
}
-
- function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode {
- setFlowNodeReferenced(antecedent);
- return initFlowNode({ flags: FlowFlags.Call, antecedent, node });
+ if (!expression) {
+ return flags & FlowFlags.TrueCondition ? antecedent : unreachableFlow;
}
-
- function finishFlowLabel(flow: FlowLabel): FlowNode {
- const antecedents = flow.antecedents;
- if (!antecedents) {
- return unreachableFlow;
- }
- if (antecedents.length === 1) {
- return antecedents[0];
- }
- return flow;
+ if ((expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition ||
+ expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) &&
+ !isExpressionOfOptionalChainRoot(expression) && !isNullishCoalesce(expression.parent)) {
+ return unreachableFlow;
}
-
- function isStatementCondition(node: Node) {
- const parent = node.parent;
- switch (parent.kind) {
- case SyntaxKind.IfStatement:
- case SyntaxKind.WhileStatement:
- case SyntaxKind.DoStatement:
- return (parent as IfStatement | WhileStatement | DoStatement).expression === node;
- case SyntaxKind.ForStatement:
- case SyntaxKind.ConditionalExpression:
- return (parent as ForStatement | ConditionalExpression).condition === node;
- }
- return false;
+ if (!isNarrowingExpression(expression)) {
+ return antecedent;
}
+ setFlowNodeReferenced(antecedent);
+ return initFlowNode({ flags, antecedent, node: expression });
+ }
- function isLogicalExpression(node: Node) {
- while (true) {
- if (node.kind === SyntaxKind.ParenthesizedExpression) {
- node = (node as ParenthesizedExpression).expression;
- }
- else if (node.kind === SyntaxKind.PrefixUnaryExpression && (node as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken) {
- node = (node as PrefixUnaryExpression).operand;
- }
- else {
- return node.kind === SyntaxKind.BinaryExpression && (
- (node as BinaryExpression).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken ||
- (node as BinaryExpression).operatorToken.kind === SyntaxKind.BarBarToken ||
- (node as BinaryExpression).operatorToken.kind === SyntaxKind.QuestionQuestionToken);
- }
- }
- }
+ function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
+ setFlowNodeReferenced(antecedent);
+ return initFlowNode({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd });
+ }
- function isLogicalAssignmentExpression(node: Node) {
- node = skipParentheses(node);
- return isBinaryExpression(node) && isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind);
+ function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Expression | VariableDeclaration | ArrayBindingElement): FlowNode {
+ setFlowNodeReferenced(antecedent);
+ const result = initFlowNode({ flags, antecedent, node });
+ if (currentExceptionTarget) {
+ addAntecedent(currentExceptionTarget, result);
}
+ return result;
+ }
- function isTopLevelLogicalExpression(node: Node): boolean {
- while (isParenthesizedExpression(node.parent) ||
- isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.ExclamationToken) {
- node = node.parent;
- }
- return !isStatementCondition(node) &&
- !isLogicalAssignmentExpression(node.parent) &&
- !isLogicalExpression(node.parent) &&
- !(isOptionalChain(node.parent) && node.parent.expression === node);
+ function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode {
+ setFlowNodeReferenced(antecedent);
+ return initFlowNode({ flags: FlowFlags.Call, antecedent, node });
+ }
+
+ function finishFlowLabel(flow: FlowLabel): FlowNode {
+ const antecedents = flow.antecedents;
+ if (!antecedents) {
+ return unreachableFlow;
}
+ if (antecedents.length === 1) {
+ return antecedents[0];
+ }
+ return flow;
+ }
- function doWithConditionalBranches(action: (value: T) => void, value: T, trueTarget: FlowLabel, falseTarget: FlowLabel) {
- const savedTrueTarget = currentTrueTarget;
- const savedFalseTarget = currentFalseTarget;
- currentTrueTarget = trueTarget;
- currentFalseTarget = falseTarget;
- action(value);
- currentTrueTarget = savedTrueTarget;
- currentFalseTarget = savedFalseTarget;
+ function isStatementCondition(node: Node) {
+ const parent = node.parent;
+ switch (parent.kind) {
+ case SyntaxKind.IfStatement:
+ case SyntaxKind.WhileStatement:
+ case SyntaxKind.DoStatement:
+ return (parent as IfStatement | WhileStatement | DoStatement).expression === node;
+ case SyntaxKind.ForStatement:
+ case SyntaxKind.ConditionalExpression:
+ return (parent as ForStatement | ConditionalExpression).condition === node;
}
+ return false;
+ }
- function bindCondition(node: Expression | undefined, trueTarget: FlowLabel, falseTarget: FlowLabel) {
- doWithConditionalBranches(bind, node, trueTarget, falseTarget);
- if (!node || !isLogicalAssignmentExpression(node) && !isLogicalExpression(node) && !(isOptionalChain(node) && isOutermostOptionalChain(node))) {
- addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
- addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
+ function isLogicalExpression(node: Node) {
+ while (true) {
+ if (node.kind === SyntaxKind.ParenthesizedExpression) {
+ node = (node as ParenthesizedExpression).expression;
+ }
+ else if (node.kind === SyntaxKind.PrefixUnaryExpression && (node as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken) {
+ node = (node as PrefixUnaryExpression).operand;
+ }
+ else {
+ return node.kind === SyntaxKind.BinaryExpression && ((node as BinaryExpression).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken ||
+ (node as BinaryExpression).operatorToken.kind === SyntaxKind.BarBarToken ||
+ (node as BinaryExpression).operatorToken.kind === SyntaxKind.QuestionQuestionToken);
}
}
+ }
- function bindIterativeStatement(node: Statement, breakTarget: FlowLabel, continueTarget: FlowLabel): void {
- const saveBreakTarget = currentBreakTarget;
- const saveContinueTarget = currentContinueTarget;
- currentBreakTarget = breakTarget;
- currentContinueTarget = continueTarget;
- bind(node);
- currentBreakTarget = saveBreakTarget;
- currentContinueTarget = saveContinueTarget;
+ function isLogicalAssignmentExpression(node: Node) {
+ node = skipParentheses(node);
+ return isBinaryExpression(node) && isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind);
+ }
+
+ function isTopLevelLogicalExpression(node: Node): boolean {
+ while (isParenthesizedExpression(node.parent) ||
+ isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.ExclamationToken) {
+ node = node.parent;
}
+ return !isStatementCondition(node) &&
+ !isLogicalAssignmentExpression(node.parent) &&
+ !isLogicalExpression(node.parent) &&
+ !(isOptionalChain(node.parent) && node.parent.expression === node);
+ }
- function setContinueTarget(node: Node, target: FlowLabel) {
- let label = activeLabelList;
- while (label && node.parent.kind === SyntaxKind.LabeledStatement) {
- label.continueTarget = target;
- label = label.next;
- node = node.parent;
- }
- return target;
- }
-
- function bindWhileStatement(node: WhileStatement): void {
- const preWhileLabel = setContinueTarget(node, createLoopLabel());
- const preBodyLabel = createBranchLabel();
- const postWhileLabel = createBranchLabel();
- addAntecedent(preWhileLabel, currentFlow);
- currentFlow = preWhileLabel;
- bindCondition(node.expression, preBodyLabel, postWhileLabel);
- currentFlow = finishFlowLabel(preBodyLabel);
- bindIterativeStatement(node.statement, postWhileLabel, preWhileLabel);
- addAntecedent(preWhileLabel, currentFlow);
- currentFlow = finishFlowLabel(postWhileLabel);
- }
-
- function bindDoStatement(node: DoStatement): void {
- const preDoLabel = createLoopLabel();
- const preConditionLabel = setContinueTarget(node, createBranchLabel());
- const postDoLabel = createBranchLabel();
- addAntecedent(preDoLabel, currentFlow);
- currentFlow = preDoLabel;
- bindIterativeStatement(node.statement, postDoLabel, preConditionLabel);
- addAntecedent(preConditionLabel, currentFlow);
- currentFlow = finishFlowLabel(preConditionLabel);
- bindCondition(node.expression, preDoLabel, postDoLabel);
- currentFlow = finishFlowLabel(postDoLabel);
- }
-
- function bindForStatement(node: ForStatement): void {
- const preLoopLabel = setContinueTarget(node, createLoopLabel());
- const preBodyLabel = createBranchLabel();
- const postLoopLabel = createBranchLabel();
- bind(node.initializer);
- addAntecedent(preLoopLabel, currentFlow);
- currentFlow = preLoopLabel;
- bindCondition(node.condition, preBodyLabel, postLoopLabel);
- currentFlow = finishFlowLabel(preBodyLabel);
- bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel);
- bind(node.incrementor);
- addAntecedent(preLoopLabel, currentFlow);
- currentFlow = finishFlowLabel(postLoopLabel);
- }
-
- function bindForInOrForOfStatement(node: ForInOrOfStatement): void {
- const preLoopLabel = setContinueTarget(node, createLoopLabel());
- const postLoopLabel = createBranchLabel();
- bind(node.expression);
- addAntecedent(preLoopLabel, currentFlow);
- currentFlow = preLoopLabel;
- if (node.kind === SyntaxKind.ForOfStatement) {
- bind(node.awaitModifier);
- }
- addAntecedent(postLoopLabel, currentFlow);
- bind(node.initializer);
- if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) {
- bindAssignmentTargetFlow(node.initializer);
- }
- bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel);
- addAntecedent(preLoopLabel, currentFlow);
- currentFlow = finishFlowLabel(postLoopLabel);
+ function doWithConditionalBranches(action: (value: T) => void, value: T, trueTarget: FlowLabel, falseTarget: FlowLabel) {
+ const savedTrueTarget = currentTrueTarget;
+ const savedFalseTarget = currentFalseTarget;
+ currentTrueTarget = trueTarget;
+ currentFalseTarget = falseTarget;
+ action(value);
+ currentTrueTarget = savedTrueTarget;
+ currentFalseTarget = savedFalseTarget;
+ }
+
+ function bindCondition(node: Expression | undefined, trueTarget: FlowLabel, falseTarget: FlowLabel) {
+ doWithConditionalBranches(bind, node, trueTarget, falseTarget);
+ if (!node || !isLogicalAssignmentExpression(node) && !isLogicalExpression(node) && !(isOptionalChain(node) && isOutermostOptionalChain(node))) {
+ addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
+ addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
}
+ }
+
+ function bindIterativeStatement(node: Statement, breakTarget: FlowLabel, continueTarget: FlowLabel): void {
+ const saveBreakTarget = currentBreakTarget;
+ const saveContinueTarget = currentContinueTarget;
+ currentBreakTarget = breakTarget;
+ currentContinueTarget = continueTarget;
+ bind(node);
+ currentBreakTarget = saveBreakTarget;
+ currentContinueTarget = saveContinueTarget;
+ }
- function bindIfStatement(node: IfStatement): void {
- const thenLabel = createBranchLabel();
- const elseLabel = createBranchLabel();
- const postIfLabel = createBranchLabel();
- bindCondition(node.expression, thenLabel, elseLabel);
- currentFlow = finishFlowLabel(thenLabel);
- bind(node.thenStatement);
- addAntecedent(postIfLabel, currentFlow);
- currentFlow = finishFlowLabel(elseLabel);
- bind(node.elseStatement);
- addAntecedent(postIfLabel, currentFlow);
- currentFlow = finishFlowLabel(postIfLabel);
+ function setContinueTarget(node: Node, target: FlowLabel) {
+ let label = activeLabelList;
+ while (label && node.parent.kind === SyntaxKind.LabeledStatement) {
+ label.continueTarget = target;
+ label = label.next;
+ node = node.parent;
}
+ return target;
+ }
- function bindReturnOrThrow(node: ReturnStatement | ThrowStatement): void {
- bind(node.expression);
- if (node.kind === SyntaxKind.ReturnStatement) {
- hasExplicitReturn = true;
- if (currentReturnTarget) {
- addAntecedent(currentReturnTarget, currentFlow);
- }
+ function bindWhileStatement(node: WhileStatement): void {
+ const preWhileLabel = setContinueTarget(node, createLoopLabel());
+ const preBodyLabel = createBranchLabel();
+ const postWhileLabel = createBranchLabel();
+ addAntecedent(preWhileLabel, currentFlow);
+ currentFlow = preWhileLabel;
+ bindCondition(node.expression, preBodyLabel, postWhileLabel);
+ currentFlow = finishFlowLabel(preBodyLabel);
+ bindIterativeStatement(node.statement, postWhileLabel, preWhileLabel);
+ addAntecedent(preWhileLabel, currentFlow);
+ currentFlow = finishFlowLabel(postWhileLabel);
+ }
+
+ function bindDoStatement(node: DoStatement): void {
+ const preDoLabel = createLoopLabel();
+ const preConditionLabel = setContinueTarget(node, createBranchLabel());
+ const postDoLabel = createBranchLabel();
+ addAntecedent(preDoLabel, currentFlow);
+ currentFlow = preDoLabel;
+ bindIterativeStatement(node.statement, postDoLabel, preConditionLabel);
+ addAntecedent(preConditionLabel, currentFlow);
+ currentFlow = finishFlowLabel(preConditionLabel);
+ bindCondition(node.expression, preDoLabel, postDoLabel);
+ currentFlow = finishFlowLabel(postDoLabel);
+ }
+
+ function bindForStatement(node: ForStatement): void {
+ const preLoopLabel = setContinueTarget(node, createLoopLabel());
+ const preBodyLabel = createBranchLabel();
+ const postLoopLabel = createBranchLabel();
+ bind(node.initializer);
+ addAntecedent(preLoopLabel, currentFlow);
+ currentFlow = preLoopLabel;
+ bindCondition(node.condition, preBodyLabel, postLoopLabel);
+ currentFlow = finishFlowLabel(preBodyLabel);
+ bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel);
+ bind(node.incrementor);
+ addAntecedent(preLoopLabel, currentFlow);
+ currentFlow = finishFlowLabel(postLoopLabel);
+ }
+
+ function bindForInOrForOfStatement(node: ForInOrOfStatement): void {
+ const preLoopLabel = setContinueTarget(node, createLoopLabel());
+ const postLoopLabel = createBranchLabel();
+ bind(node.expression);
+ addAntecedent(preLoopLabel, currentFlow);
+ currentFlow = preLoopLabel;
+ if (node.kind === SyntaxKind.ForOfStatement) {
+ bind(node.awaitModifier);
+ }
+ addAntecedent(postLoopLabel, currentFlow);
+ bind(node.initializer);
+ if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) {
+ bindAssignmentTargetFlow(node.initializer);
+ }
+ bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel);
+ addAntecedent(preLoopLabel, currentFlow);
+ currentFlow = finishFlowLabel(postLoopLabel);
+ }
+
+ function bindIfStatement(node: IfStatement): void {
+ const thenLabel = createBranchLabel();
+ const elseLabel = createBranchLabel();
+ const postIfLabel = createBranchLabel();
+ bindCondition(node.expression, thenLabel, elseLabel);
+ currentFlow = finishFlowLabel(thenLabel);
+ bind(node.thenStatement);
+ addAntecedent(postIfLabel, currentFlow);
+ currentFlow = finishFlowLabel(elseLabel);
+ bind(node.elseStatement);
+ addAntecedent(postIfLabel, currentFlow);
+ currentFlow = finishFlowLabel(postIfLabel);
+ }
+
+ function bindReturnOrThrow(node: ReturnStatement | ThrowStatement): void {
+ bind(node.expression);
+ if (node.kind === SyntaxKind.ReturnStatement) {
+ hasExplicitReturn = true;
+ if (currentReturnTarget) {
+ addAntecedent(currentReturnTarget, currentFlow);
}
- currentFlow = unreachableFlow;
}
+ currentFlow = unreachableFlow;
+ }
- function findActiveLabel(name: __String) {
- for (let label = activeLabelList; label; label = label.next) {
- if (label.name === name) {
- return label;
- }
+ function findActiveLabel(name: __String) {
+ for (let label = activeLabelList; label; label = label.next) {
+ if (label.name === name) {
+ return label;
}
- return undefined;
}
+ return undefined;
+ }
- function bindBreakOrContinueFlow(node: BreakOrContinueStatement, breakTarget: FlowLabel | undefined, continueTarget: FlowLabel | undefined) {
- const flowLabel = node.kind === SyntaxKind.BreakStatement ? breakTarget : continueTarget;
- if (flowLabel) {
- addAntecedent(flowLabel, currentFlow);
- currentFlow = unreachableFlow;
- }
+ function bindBreakOrContinueFlow(node: BreakOrContinueStatement, breakTarget: FlowLabel | undefined, continueTarget: FlowLabel | undefined) {
+ const flowLabel = node.kind === SyntaxKind.BreakStatement ? breakTarget : continueTarget;
+ if (flowLabel) {
+ addAntecedent(flowLabel, currentFlow);
+ currentFlow = unreachableFlow;
}
+ }
- function bindBreakOrContinueStatement(node: BreakOrContinueStatement): void {
- bind(node.label);
- if (node.label) {
- const activeLabel = findActiveLabel(node.label.escapedText);
- if (activeLabel) {
- activeLabel.referenced = true;
- bindBreakOrContinueFlow(node, activeLabel.breakTarget, activeLabel.continueTarget);
- }
- }
- else {
- bindBreakOrContinueFlow(node, currentBreakTarget, currentContinueTarget);
+ function bindBreakOrContinueStatement(node: BreakOrContinueStatement): void {
+ bind(node.label);
+ if (node.label) {
+ const activeLabel = findActiveLabel(node.label.escapedText);
+ if (activeLabel) {
+ activeLabel.referenced = true;
+ bindBreakOrContinueFlow(node, activeLabel.breakTarget, activeLabel.continueTarget);
}
}
+ else {
+ bindBreakOrContinueFlow(node, currentBreakTarget, currentContinueTarget);
+ }
+ }
- function bindTryStatement(node: TryStatement): void {
- // We conservatively assume that *any* code in the try block can cause an exception, but we only need
- // to track code that causes mutations (because only mutations widen the possible control flow type of
- // a variable). The exceptionLabel is the target label for control flows that result from exceptions.
- // We add all mutation flow nodes as antecedents of this label such that we can analyze them as possible
- // antecedents of the start of catch or finally blocks. Furthermore, we add the current control flow to
- // represent exceptions that occur before any mutations.
- const saveReturnTarget = currentReturnTarget;
- const saveExceptionTarget = currentExceptionTarget;
- const normalExitLabel = createBranchLabel();
- const returnLabel = createBranchLabel();
- let exceptionLabel = createBranchLabel();
- if (node.finallyBlock) {
- currentReturnTarget = returnLabel;
- }
+ function bindTryStatement(node: TryStatement): void {
+ // We conservatively assume that *any* code in the try block can cause an exception, but we only need
+ // to track code that causes mutations (because only mutations widen the possible control flow type of
+ // a variable). The exceptionLabel is the target label for control flows that result from exceptions.
+ // We add all mutation flow nodes as antecedents of this label such that we can analyze them as possible
+ // antecedents of the start of catch or finally blocks. Furthermore, we add the current control flow to
+ // represent exceptions that occur before any mutations.
+ const saveReturnTarget = currentReturnTarget;
+ const saveExceptionTarget = currentExceptionTarget;
+ const normalExitLabel = createBranchLabel();
+ const returnLabel = createBranchLabel();
+ let exceptionLabel = createBranchLabel();
+ if (node.finallyBlock) {
+ currentReturnTarget = returnLabel;
+ }
+ addAntecedent(exceptionLabel, currentFlow);
+ currentExceptionTarget = exceptionLabel;
+ bind(node.tryBlock);
+ addAntecedent(normalExitLabel, currentFlow);
+ if (node.catchClause) {
+ // Start of catch clause is the target of exceptions from try block.
+ currentFlow = finishFlowLabel(exceptionLabel);
+ // The currentExceptionTarget now represents control flows from exceptions in the catch clause.
+ // Effectively, in a try-catch-finally, if an exception occurs in the try block, the catch block
+ // acts like a second try block.
+ exceptionLabel = createBranchLabel();
addAntecedent(exceptionLabel, currentFlow);
currentExceptionTarget = exceptionLabel;
- bind(node.tryBlock);
+ bind(node.catchClause);
addAntecedent(normalExitLabel, currentFlow);
- if (node.catchClause) {
- // Start of catch clause is the target of exceptions from try block.
- currentFlow = finishFlowLabel(exceptionLabel);
- // The currentExceptionTarget now represents control flows from exceptions in the catch clause.
- // Effectively, in a try-catch-finally, if an exception occurs in the try block, the catch block
- // acts like a second try block.
- exceptionLabel = createBranchLabel();
- addAntecedent(exceptionLabel, currentFlow);
- currentExceptionTarget = exceptionLabel;
- bind(node.catchClause);
- addAntecedent(normalExitLabel, currentFlow);
+ }
+ currentReturnTarget = saveReturnTarget;
+ currentExceptionTarget = saveExceptionTarget;
+ if (node.finallyBlock) {
+ // Possible ways control can reach the finally block:
+ // 1) Normal completion of try block of a try-finally or try-catch-finally
+ // 2) Normal completion of catch block (following exception in try block) of a try-catch-finally
+ // 3) Return in try or catch block of a try-finally or try-catch-finally
+ // 4) Exception in try block of a try-finally
+ // 5) Exception in catch block of a try-catch-finally
+ // When analyzing a control flow graph that starts inside a finally block we want to consider all
+ // five possibilities above. However, when analyzing a control flow graph that starts outside (past)
+ // the finally block, we only want to consider the first two (if we're past a finally block then it
+ // must have completed normally). Likewise, when analyzing a control flow graph from return statements
+ // in try or catch blocks in an IIFE, we only want to consider the third. To make this possible, we
+ // inject a ReduceLabel node into the control flow graph. This node contains an alternate reduced
+ // set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel
+ // node, the pre-finally label is temporarily switched to the reduced antecedent set.
+ const finallyLabel = createBranchLabel();
+ finallyLabel.antecedents = concatenate(concatenate(normalExitLabel.antecedents, exceptionLabel.antecedents), returnLabel.antecedents);
+ currentFlow = finallyLabel;
+ bind(node.finallyBlock);
+ if (currentFlow.flags & FlowFlags.Unreachable) {
+ // If the end of the finally block is unreachable, the end of the entire try statement is unreachable.
+ currentFlow = unreachableFlow;
}
- currentReturnTarget = saveReturnTarget;
- currentExceptionTarget = saveExceptionTarget;
- if (node.finallyBlock) {
- // Possible ways control can reach the finally block:
- // 1) Normal completion of try block of a try-finally or try-catch-finally
- // 2) Normal completion of catch block (following exception in try block) of a try-catch-finally
- // 3) Return in try or catch block of a try-finally or try-catch-finally
- // 4) Exception in try block of a try-finally
- // 5) Exception in catch block of a try-catch-finally
- // When analyzing a control flow graph that starts inside a finally block we want to consider all
- // five possibilities above. However, when analyzing a control flow graph that starts outside (past)
- // the finally block, we only want to consider the first two (if we're past a finally block then it
- // must have completed normally). Likewise, when analyzing a control flow graph from return statements
- // in try or catch blocks in an IIFE, we only want to consider the third. To make this possible, we
- // inject a ReduceLabel node into the control flow graph. This node contains an alternate reduced
- // set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel
- // node, the pre-finally label is temporarily switched to the reduced antecedent set.
- const finallyLabel = createBranchLabel();
- finallyLabel.antecedents = concatenate(concatenate(normalExitLabel.antecedents, exceptionLabel.antecedents), returnLabel.antecedents);
- currentFlow = finallyLabel;
- bind(node.finallyBlock);
- if (currentFlow.flags & FlowFlags.Unreachable) {
- // If the end of the finally block is unreachable, the end of the entire try statement is unreachable.
- currentFlow = unreachableFlow;
+ else {
+ // If we have an IIFE return target and return statements in the try or catch blocks, add a control
+ // flow that goes back through the finally block and back through only the return statements.
+ if (currentReturnTarget && returnLabel.antecedents) {
+ addAntecedent(currentReturnTarget, createReduceLabel(finallyLabel, returnLabel.antecedents, currentFlow));
}
- else {
- // If we have an IIFE return target and return statements in the try or catch blocks, add a control
- // flow that goes back through the finally block and back through only the return statements.
- if (currentReturnTarget && returnLabel.antecedents) {
- addAntecedent(currentReturnTarget, createReduceLabel(finallyLabel, returnLabel.antecedents, currentFlow));
- }
- // If we have an outer exception target (i.e. a containing try-finally or try-catch-finally), add a
- // control flow that goes back through the finally blok and back through each possible exception source.
- if (currentExceptionTarget && exceptionLabel.antecedents) {
- addAntecedent(currentExceptionTarget, createReduceLabel(finallyLabel, exceptionLabel.antecedents, currentFlow));
- }
- // If the end of the finally block is reachable, but the end of the try and catch blocks are not,
- // convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should
- // result in an unreachable current control flow.
- currentFlow = normalExitLabel.antecedents ? createReduceLabel(finallyLabel, normalExitLabel.antecedents, currentFlow) : unreachableFlow;
+ // If we have an outer exception target (i.e. a containing try-finally or try-catch-finally), add a
+ // control flow that goes back through the finally blok and back through each possible exception source.
+ if (currentExceptionTarget && exceptionLabel.antecedents) {
+ addAntecedent(currentExceptionTarget, createReduceLabel(finallyLabel, exceptionLabel.antecedents, currentFlow));
}
- }
- else {
- currentFlow = finishFlowLabel(normalExitLabel);
+ // If the end of the finally block is reachable, but the end of the try and catch blocks are not,
+ // convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should
+ // result in an unreachable current control flow.
+ currentFlow = normalExitLabel.antecedents ? createReduceLabel(finallyLabel, normalExitLabel.antecedents, currentFlow) : unreachableFlow;
}
}
+ else {
+ currentFlow = finishFlowLabel(normalExitLabel);
+ }
+ }
- function bindSwitchStatement(node: SwitchStatement): void {
- const postSwitchLabel = createBranchLabel();
- bind(node.expression);
- const saveBreakTarget = currentBreakTarget;
- const savePreSwitchCaseFlow = preSwitchCaseFlow;
- currentBreakTarget = postSwitchLabel;
- preSwitchCaseFlow = currentFlow;
- bind(node.caseBlock);
- addAntecedent(postSwitchLabel, currentFlow);
- const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause);
- // We mark a switch statement as possibly exhaustive if it has no default clause and if all
- // case clauses have unreachable end points (e.g. they all return). Note, we no longer need
- // this property in control flow analysis, it's there only for backwards compatibility.
- node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedents;
- if (!hasDefault) {
- addAntecedent(postSwitchLabel, createFlowSwitchClause(preSwitchCaseFlow, node, 0, 0));
+ function bindSwitchStatement(node: SwitchStatement): void {
+ const postSwitchLabel = createBranchLabel();
+ bind(node.expression);
+ const saveBreakTarget = currentBreakTarget;
+ const savePreSwitchCaseFlow = preSwitchCaseFlow;
+ currentBreakTarget = postSwitchLabel;
+ preSwitchCaseFlow = currentFlow;
+ bind(node.caseBlock);
+ addAntecedent(postSwitchLabel, currentFlow);
+ const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause);
+ // We mark a switch statement as possibly exhaustive if it has no default clause and if all
+ // case clauses have unreachable end points (e.g. they all return). Note, we no longer need
+ // this property in control flow analysis, it's there only for backwards compatibility.
+ node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedents;
+ if (!hasDefault) {
+ addAntecedent(postSwitchLabel, createFlowSwitchClause(preSwitchCaseFlow, node, 0, 0));
+ }
+ currentBreakTarget = saveBreakTarget;
+ preSwitchCaseFlow = savePreSwitchCaseFlow;
+ currentFlow = finishFlowLabel(postSwitchLabel);
+ }
+
+ function bindCaseBlock(node: CaseBlock): void {
+ const clauses = node.clauses;
+ const isNarrowingSwitch = isNarrowingExpression(node.parent.expression);
+ let fallthroughFlow = unreachableFlow;
+ for (let i = 0; i < clauses.length; i++) {
+ const clauseStart = i;
+ while (!clauses[i].statements.length && i + 1 < clauses.length) {
+ bind(clauses[i]);
+ i++;
+ }
+ const preCaseLabel = createBranchLabel();
+ addAntecedent(preCaseLabel, isNarrowingSwitch ? createFlowSwitchClause(preSwitchCaseFlow!, node.parent, clauseStart, i + 1) : preSwitchCaseFlow!);
+ addAntecedent(preCaseLabel, fallthroughFlow);
+ currentFlow = finishFlowLabel(preCaseLabel);
+ const clause = clauses[i];
+ bind(clause);
+ fallthroughFlow = currentFlow;
+ if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) {
+ clause.fallthroughFlowNode = currentFlow;
}
- currentBreakTarget = saveBreakTarget;
- preSwitchCaseFlow = savePreSwitchCaseFlow;
- currentFlow = finishFlowLabel(postSwitchLabel);
}
+ }
- function bindCaseBlock(node: CaseBlock): void {
- const clauses = node.clauses;
- const isNarrowingSwitch = isNarrowingExpression(node.parent.expression);
- let fallthroughFlow = unreachableFlow;
- for (let i = 0; i < clauses.length; i++) {
- const clauseStart = i;
- while (!clauses[i].statements.length && i + 1 < clauses.length) {
- bind(clauses[i]);
- i++;
- }
- const preCaseLabel = createBranchLabel();
- addAntecedent(preCaseLabel, isNarrowingSwitch ? createFlowSwitchClause(preSwitchCaseFlow!, node.parent, clauseStart, i + 1) : preSwitchCaseFlow!);
- addAntecedent(preCaseLabel, fallthroughFlow);
- currentFlow = finishFlowLabel(preCaseLabel);
- const clause = clauses[i];
- bind(clause);
- fallthroughFlow = currentFlow;
- if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) {
- clause.fallthroughFlowNode = currentFlow;
- }
+ function bindCaseClause(node: CaseClause): void {
+ const saveCurrentFlow = currentFlow;
+ currentFlow = preSwitchCaseFlow!;
+ bind(node.expression);
+ currentFlow = saveCurrentFlow;
+ bindEach(node.statements);
+ }
+
+ function bindExpressionStatement(node: ExpressionStatement): void {
+ bind(node.expression);
+ maybeBindExpressionFlowIfCall(node.expression);
+ }
+
+ function maybeBindExpressionFlowIfCall(node: Expression) {
+ // A top level or LHS of comma expression call expression with a dotted function name and at least one argument
+ // is potentially an assertion and is therefore included in the control flow.
+ if (node.kind === SyntaxKind.CallExpression) {
+ const call = node as CallExpression;
+ if (call.expression.kind !== SyntaxKind.SuperKeyword && isDottedName(call.expression)) {
+ currentFlow = createFlowCall(currentFlow, call);
}
}
+ }
- function bindCaseClause(node: CaseClause): void {
- const saveCurrentFlow = currentFlow;
- currentFlow = preSwitchCaseFlow!;
- bind(node.expression);
- currentFlow = saveCurrentFlow;
- bindEach(node.statements);
- }
+ function bindLabeledStatement(node: LabeledStatement): void {
+ const postStatementLabel = createBranchLabel();
+ activeLabelList = {
+ next: activeLabelList,
+ name: node.label.escapedText,
+ breakTarget: postStatementLabel,
+ continueTarget: undefined,
+ referenced: false
+ };
+ bind(node.label);
+ bind(node.statement);
+ if (!activeLabelList.referenced && !options.allowUnusedLabels) {
+ errorOrSuggestionOnNode(unusedLabelIsError(options), node.label, Diagnostics.Unused_label);
+ }
+ activeLabelList = activeLabelList.next;
+ addAntecedent(postStatementLabel, currentFlow);
+ currentFlow = finishFlowLabel(postStatementLabel);
+ }
- function bindExpressionStatement(node: ExpressionStatement): void {
- bind(node.expression);
- maybeBindExpressionFlowIfCall(node.expression);
+ function bindDestructuringTargetFlow(node: Expression) {
+ if (node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
+ bindAssignmentTargetFlow((node as BinaryExpression).left);
}
-
- function maybeBindExpressionFlowIfCall(node: Expression) {
- // A top level or LHS of comma expression call expression with a dotted function name and at least one argument
- // is potentially an assertion and is therefore included in the control flow.
- if (node.kind === SyntaxKind.CallExpression) {
- const call = node as CallExpression;
- if (call.expression.kind !== SyntaxKind.SuperKeyword && isDottedName(call.expression)) {
- currentFlow = createFlowCall(currentFlow, call);
- }
- }
+ else {
+ bindAssignmentTargetFlow(node);
}
+ }
- function bindLabeledStatement(node: LabeledStatement): void {
- const postStatementLabel = createBranchLabel();
- activeLabelList = {
- next: activeLabelList,
- name: node.label.escapedText,
- breakTarget: postStatementLabel,
- continueTarget: undefined,
- referenced: false
- };
- bind(node.label);
- bind(node.statement);
- if (!activeLabelList.referenced && !options.allowUnusedLabels) {
- errorOrSuggestionOnNode(unusedLabelIsError(options), node.label, Diagnostics.Unused_label);
- }
- activeLabelList = activeLabelList.next;
- addAntecedent(postStatementLabel, currentFlow);
- currentFlow = finishFlowLabel(postStatementLabel);
+ function bindAssignmentTargetFlow(node: Expression) {
+ if (isNarrowableReference(node)) {
+ currentFlow = createFlowMutation(FlowFlags.Assignment, currentFlow, node);
}
-
- function bindDestructuringTargetFlow(node: Expression) {
- if (node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
- bindAssignmentTargetFlow((node as BinaryExpression).left);
- }
- else {
- bindAssignmentTargetFlow(node);
+ else if (node.kind === SyntaxKind.ArrayLiteralExpression) {
+ for (const e of (node as ArrayLiteralExpression).elements) {
+ if (e.kind === SyntaxKind.SpreadElement) {
+ bindAssignmentTargetFlow((e as SpreadElement).expression);
+ }
+ else {
+ bindDestructuringTargetFlow(e);
+ }
}
}
-
- function bindAssignmentTargetFlow(node: Expression) {
- if (isNarrowableReference(node)) {
- currentFlow = createFlowMutation(FlowFlags.Assignment, currentFlow, node);
- }
- else if (node.kind === SyntaxKind.ArrayLiteralExpression) {
- for (const e of (node as ArrayLiteralExpression).elements) {
- if (e.kind === SyntaxKind.SpreadElement) {
- bindAssignmentTargetFlow((e as SpreadElement).expression);
- }
- else {
- bindDestructuringTargetFlow(e);
- }
+ else if (node.kind === SyntaxKind.ObjectLiteralExpression) {
+ for (const p of (node as ObjectLiteralExpression).properties) {
+ if (p.kind === SyntaxKind.PropertyAssignment) {
+ bindDestructuringTargetFlow(p.initializer);
}
- }
- else if (node.kind === SyntaxKind.ObjectLiteralExpression) {
- for (const p of (node as ObjectLiteralExpression).properties) {
- if (p.kind === SyntaxKind.PropertyAssignment) {
- bindDestructuringTargetFlow(p.initializer);
- }
- else if (p.kind === SyntaxKind.ShorthandPropertyAssignment) {
- bindAssignmentTargetFlow(p.name);
- }
- else if (p.kind === SyntaxKind.SpreadAssignment) {
- bindAssignmentTargetFlow(p.expression);
- }
+ else if (p.kind === SyntaxKind.ShorthandPropertyAssignment) {
+ bindAssignmentTargetFlow(p.name);
+ }
+ else if (p.kind === SyntaxKind.SpreadAssignment) {
+ bindAssignmentTargetFlow(p.expression);
}
}
}
+ }
- function bindLogicalLikeExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) {
- const preRightLabel = createBranchLabel();
- if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === SyntaxKind.AmpersandAmpersandEqualsToken) {
- bindCondition(node.left, preRightLabel, falseTarget);
- }
- else {
- bindCondition(node.left, trueTarget, preRightLabel);
- }
- currentFlow = finishFlowLabel(preRightLabel);
- bind(node.operatorToken);
+ function bindLogicalLikeExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) {
+ const preRightLabel = createBranchLabel();
+ if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === SyntaxKind.AmpersandAmpersandEqualsToken) {
+ bindCondition(node.left, preRightLabel, falseTarget);
+ }
+ else {
+ bindCondition(node.left, trueTarget, preRightLabel);
+ }
+ currentFlow = finishFlowLabel(preRightLabel);
+ bind(node.operatorToken);
- if (isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind)) {
- doWithConditionalBranches(bind, node.right, trueTarget, falseTarget);
- bindAssignmentTargetFlow(node.left);
+ if (isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind)) {
+ doWithConditionalBranches(bind, node.right, trueTarget, falseTarget);
+ bindAssignmentTargetFlow(node.left);
- addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
- addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
- }
- else {
- bindCondition(node.right, trueTarget, falseTarget);
- }
+ addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
+ addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
}
-
- function bindPrefixUnaryExpressionFlow(node: PrefixUnaryExpression) {
- if (node.operator === SyntaxKind.ExclamationToken) {
- const saveTrueTarget = currentTrueTarget;
- currentTrueTarget = currentFalseTarget;
- currentFalseTarget = saveTrueTarget;
- bindEachChild(node);
- currentFalseTarget = currentTrueTarget;
- currentTrueTarget = saveTrueTarget;
- }
- else {
- bindEachChild(node);
- if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) {
- bindAssignmentTargetFlow(node.operand);
- }
- }
+ else {
+ bindCondition(node.right, trueTarget, falseTarget);
}
+ }
- function bindPostfixUnaryExpressionFlow(node: PostfixUnaryExpression) {
+ function bindPrefixUnaryExpressionFlow(node: PrefixUnaryExpression) {
+ if (node.operator === SyntaxKind.ExclamationToken) {
+ const saveTrueTarget = currentTrueTarget;
+ currentTrueTarget = currentFalseTarget;
+ currentFalseTarget = saveTrueTarget;
+ bindEachChild(node);
+ currentFalseTarget = currentTrueTarget;
+ currentTrueTarget = saveTrueTarget;
+ }
+ else {
bindEachChild(node);
if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) {
bindAssignmentTargetFlow(node.operand);
}
}
+ }
- function bindDestructuringAssignmentFlow(node: DestructuringAssignment) {
- if (inAssignmentPattern) {
- inAssignmentPattern = false;
- bind(node.operatorToken);
- bind(node.right);
- inAssignmentPattern = true;
- bind(node.left);
- }
- else {
- inAssignmentPattern = true;
- bind(node.left);
- inAssignmentPattern = false;
- bind(node.operatorToken);
- bind(node.right);
- }
- bindAssignmentTargetFlow(node.left);
+ function bindPostfixUnaryExpressionFlow(node: PostfixUnaryExpression) {
+ bindEachChild(node);
+ if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) {
+ bindAssignmentTargetFlow(node.operand);
}
+ }
- function createBindBinaryExpressionFlow() {
- interface WorkArea {
- stackIndex: number;
- skip: boolean;
- inStrictModeStack: (boolean | undefined)[];
- parentStack: (Node | undefined)[];
- }
+ function bindDestructuringAssignmentFlow(node: DestructuringAssignment) {
+ if (inAssignmentPattern) {
+ inAssignmentPattern = false;
+ bind(node.operatorToken);
+ bind(node.right);
+ inAssignmentPattern = true;
+ bind(node.left);
+ }
+ else {
+ inAssignmentPattern = true;
+ bind(node.left);
+ inAssignmentPattern = false;
+ bind(node.operatorToken);
+ bind(node.right);
+ }
+ bindAssignmentTargetFlow(node.left);
+ }
+
+ function createBindBinaryExpressionFlow() {
+ interface WorkArea {
+ stackIndex: number;
+ skip: boolean;
+ inStrictModeStack: (boolean | undefined)[];
+ parentStack: (Node | undefined)[];
+ }
- return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, /*foldState*/ undefined);
+ return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, /*foldState*/ undefined);
- function onEnter(node: BinaryExpression, state: WorkArea | undefined) {
- if (state) {
- state.stackIndex++;
- // Emulate the work that `bind` does before reaching `bindChildren`. A normal call to
- // `bindBinaryExpressionFlow` will already have done this work.
- setParent(node, parent);
- const saveInStrictMode = inStrictMode;
- bindWorker(node);
- const saveParent = parent;
- parent = node;
- state.skip = false;
- state.inStrictModeStack[state.stackIndex] = saveInStrictMode;
- state.parentStack[state.stackIndex] = saveParent;
+ function onEnter(node: BinaryExpression, state: WorkArea | undefined) {
+ if (state) {
+ state.stackIndex++;
+ // Emulate the work that `bind` does before reaching `bindChildren`. A normal call to
+ // `bindBinaryExpressionFlow` will already have done this work.
+ setParent(node, parent);
+ const saveInStrictMode = inStrictMode;
+ bindWorker(node);
+ const saveParent = parent;
+ parent = node;
+ state.skip = false;
+ state.inStrictModeStack[state.stackIndex] = saveInStrictMode;
+ state.parentStack[state.stackIndex] = saveParent;
+ }
+ else {
+ state = {
+ stackIndex: 0,
+ skip: false,
+ inStrictModeStack: [undefined],
+ parentStack: [undefined]
+ };
+ }
+ // TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions
+ // we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too
+ // For now, though, since the common cases are chained `+`, leaving it recursive is fine
+ const operator = node.operatorToken.kind;
+ if (operator === SyntaxKind.AmpersandAmpersandToken ||
+ operator === SyntaxKind.BarBarToken ||
+ operator === SyntaxKind.QuestionQuestionToken ||
+ isLogicalOrCoalescingAssignmentOperator(operator)) {
+ if (isTopLevelLogicalExpression(node)) {
+ const postExpressionLabel = createBranchLabel();
+ bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel);
+ currentFlow = finishFlowLabel(postExpressionLabel);
}
else {
- state = {
- stackIndex: 0,
- skip: false,
- inStrictModeStack: [undefined],
- parentStack: [undefined]
- };
- }
- // TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions
- // we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too
- // For now, though, since the common cases are chained `+`, leaving it recursive is fine
- const operator = node.operatorToken.kind;
- if (operator === SyntaxKind.AmpersandAmpersandToken ||
- operator === SyntaxKind.BarBarToken ||
- operator === SyntaxKind.QuestionQuestionToken ||
- isLogicalOrCoalescingAssignmentOperator(operator)) {
- if (isTopLevelLogicalExpression(node)) {
- const postExpressionLabel = createBranchLabel();
- bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel);
- currentFlow = finishFlowLabel(postExpressionLabel);
- }
- else {
- bindLogicalLikeExpression(node, currentTrueTarget!, currentFalseTarget!);
- }
- state.skip = true;
+ bindLogicalLikeExpression(node, currentTrueTarget!, currentFalseTarget!);
}
- return state;
+ state.skip = true;
}
+ return state;
+ }
- function onLeft(left: Expression, state: WorkArea, _node: BinaryExpression) {
- if (!state.skip) {
- return maybeBind(left);
- }
+ function onLeft(left: Expression, state: WorkArea, _node: BinaryExpression) {
+ if (!state.skip) {
+ return maybeBind(left);
}
+ }
- function onOperator(operatorToken: BinaryOperatorToken, state: WorkArea, node: BinaryExpression) {
- if (!state.skip) {
- if (operatorToken.kind === SyntaxKind.CommaToken) {
- maybeBindExpressionFlowIfCall(node.left);
- }
- bind(operatorToken);
+ function onOperator(operatorToken: BinaryOperatorToken, state: WorkArea, node: BinaryExpression) {
+ if (!state.skip) {
+ if (operatorToken.kind === SyntaxKind.CommaToken) {
+ maybeBindExpressionFlowIfCall(node.left);
}
+ bind(operatorToken);
}
+ }
- function onRight(right: Expression, state: WorkArea, _node: BinaryExpression) {
- if (!state.skip) {
- return maybeBind(right);
- }
+ function onRight(right: Expression, state: WorkArea, _node: BinaryExpression) {
+ if (!state.skip) {
+ return maybeBind(right);
}
+ }
- function onExit(node: BinaryExpression, state: WorkArea) {
- if (!state.skip) {
- const operator = node.operatorToken.kind;
- if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) {
- bindAssignmentTargetFlow(node.left);
- if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) {
- const elementAccess = node.left as ElementAccessExpression;
- if (isNarrowableOperand(elementAccess.expression)) {
- currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node);
- }
+ function onExit(node: BinaryExpression, state: WorkArea) {
+ if (!state.skip) {
+ const operator = node.operatorToken.kind;
+ if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) {
+ bindAssignmentTargetFlow(node.left);
+ if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) {
+ const elementAccess = node.left as ElementAccessExpression;
+ if (isNarrowableOperand(elementAccess.expression)) {
+ currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node);
}
}
}
- const savedInStrictMode = state.inStrictModeStack[state.stackIndex];
- const savedParent = state.parentStack[state.stackIndex];
- if (savedInStrictMode !== undefined) {
- inStrictMode = savedInStrictMode;
- }
- if (savedParent !== undefined) {
- parent = savedParent;
- }
- state.skip = false;
- state.stackIndex--;
}
-
- function maybeBind(node: Node) {
- if (node && isBinaryExpression(node) && !isDestructuringAssignment(node)) {
- return node;
- }
- bind(node);
+ const savedInStrictMode = state.inStrictModeStack[state.stackIndex];
+ const savedParent = state.parentStack[state.stackIndex];
+ if (savedInStrictMode !== undefined) {
+ inStrictMode = savedInStrictMode;
+ }
+ if (savedParent !== undefined) {
+ parent = savedParent;
}
+ state.skip = false;
+ state.stackIndex--;
}
- function bindDeleteExpressionFlow(node: DeleteExpression) {
- bindEachChild(node);
- if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
- bindAssignmentTargetFlow(node.expression);
+ function maybeBind(node: Node) {
+ if (node && isBinaryExpression(node) && !isDestructuringAssignment(node)) {
+ return node;
}
+ bind(node);
}
+ }
- function bindConditionalExpressionFlow(node: ConditionalExpression) {
- const trueLabel = createBranchLabel();
- const falseLabel = createBranchLabel();
- const postExpressionLabel = createBranchLabel();
- bindCondition(node.condition, trueLabel, falseLabel);
- currentFlow = finishFlowLabel(trueLabel);
- bind(node.questionToken);
- bind(node.whenTrue);
- addAntecedent(postExpressionLabel, currentFlow);
- currentFlow = finishFlowLabel(falseLabel);
- bind(node.colonToken);
- bind(node.whenFalse);
- addAntecedent(postExpressionLabel, currentFlow);
- currentFlow = finishFlowLabel(postExpressionLabel);
+ function bindDeleteExpressionFlow(node: DeleteExpression) {
+ bindEachChild(node);
+ if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
+ bindAssignmentTargetFlow(node.expression);
}
+ }
- function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) {
- const name = !isOmittedExpression(node) ? node.name : undefined;
- if (isBindingPattern(name)) {
- for (const child of name.elements) {
- bindInitializedVariableFlow(child);
- }
- }
- else {
- currentFlow = createFlowMutation(FlowFlags.Assignment, currentFlow, node);
+ function bindConditionalExpressionFlow(node: ConditionalExpression) {
+ const trueLabel = createBranchLabel();
+ const falseLabel = createBranchLabel();
+ const postExpressionLabel = createBranchLabel();
+ bindCondition(node.condition, trueLabel, falseLabel);
+ currentFlow = finishFlowLabel(trueLabel);
+ bind(node.questionToken);
+ bind(node.whenTrue);
+ addAntecedent(postExpressionLabel, currentFlow);
+ currentFlow = finishFlowLabel(falseLabel);
+ bind(node.colonToken);
+ bind(node.whenFalse);
+ addAntecedent(postExpressionLabel, currentFlow);
+ currentFlow = finishFlowLabel(postExpressionLabel);
+ }
+
+ function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) {
+ const name = !isOmittedExpression(node) ? node.name : undefined;
+ if (isBindingPattern(name)) {
+ for (const child of name.elements) {
+ bindInitializedVariableFlow(child);
}
}
+ else {
+ currentFlow = createFlowMutation(FlowFlags.Assignment, currentFlow, node);
+ }
+ }
+
+ function bindVariableDeclarationFlow(node: VariableDeclaration) {
+ bindEachChild(node);
+ if (node.initializer || isForInOrOfStatement(node.parent.parent)) {
+ bindInitializedVariableFlow(node);
+ }
+ }
- function bindVariableDeclarationFlow(node: VariableDeclaration) {
+ function bindBindingElementFlow(node: BindingElement) {
+ if (isBindingPattern(node.name)) {
+ // When evaluating a binding pattern, the initializer is evaluated before the binding pattern, per:
+ // - https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization
+ // - `BindingElement: BindingPattern Initializer?`
+ // - https://tc39.es/ecma262/#sec-runtime-semantics-keyedbindinginitialization
+ // - `BindingElement: BindingPattern Initializer?`
+ bindEach(node.decorators);
+ bindEach(node.modifiers);
+ bind(node.dotDotDotToken);
+ bind(node.propertyName);
+ bind(node.initializer);
+ bind(node.name);
+ }
+ else {
bindEachChild(node);
- if (node.initializer || isForInOrOfStatement(node.parent.parent)) {
- bindInitializedVariableFlow(node);
- }
- }
-
- function bindBindingElementFlow(node: BindingElement) {
- if (isBindingPattern(node.name)) {
- // When evaluating a binding pattern, the initializer is evaluated before the binding pattern, per:
- // - https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization
- // - `BindingElement: BindingPattern Initializer?`
- // - https://tc39.es/ecma262/#sec-runtime-semantics-keyedbindinginitialization
- // - `BindingElement: BindingPattern Initializer?`
- bindEach(node.decorators);
- bindEach(node.modifiers);
- bind(node.dotDotDotToken);
- bind(node.propertyName);
- bind(node.initializer);
- bind(node.name);
- }
- else {
- bindEachChild(node);
- }
}
+ }
- function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag) {
- bind(node.tagName);
- if (node.kind !== SyntaxKind.JSDocEnumTag && node.fullName) {
- // don't bind the type name yet; that's delayed until delayedBindJSDocTypedefTag
- setParent(node.fullName, node);
- setParentRecursive(node.fullName, /*incremental*/ false);
- }
- if (typeof node.comment !== "string") {
- bindEach(node.comment);
- }
+ function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag) {
+ bind(node.tagName);
+ if (node.kind !== SyntaxKind.JSDocEnumTag && node.fullName) {
+ // don't bind the type name yet; that's delayed until delayedBindJSDocTypedefTag
+ setParent(node.fullName, node);
+ setParentRecursive(node.fullName, /*incremental*/ false);
}
+ if (typeof node.comment !== "string") {
+ bindEach(node.comment);
+ }
+ }
- function bindJSDocClassTag(node: JSDocClassTag) {
- bindEachChild(node);
- const host = getHostSignatureFromJSDoc(node);
- if (host && host.kind !== SyntaxKind.MethodDeclaration) {
- addDeclarationToSymbol(host.symbol, host, SymbolFlags.Class);
- }
+ function bindJSDocClassTag(node: JSDocClassTag) {
+ bindEachChild(node);
+ const host = getHostSignatureFromJSDoc(node);
+ if (host && host.kind !== SyntaxKind.MethodDeclaration) {
+ addDeclarationToSymbol(host.symbol, host, SymbolFlags.Class);
}
+ }
- function bindOptionalExpression(node: Expression, trueTarget: FlowLabel, falseTarget: FlowLabel) {
- doWithConditionalBranches(bind, node, trueTarget, falseTarget);
- if (!isOptionalChain(node) || isOutermostOptionalChain(node)) {
- addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
- addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
- }
+ function bindOptionalExpression(node: Expression, trueTarget: FlowLabel, falseTarget: FlowLabel) {
+ doWithConditionalBranches(bind, node, trueTarget, falseTarget);
+ if (!isOptionalChain(node) || isOutermostOptionalChain(node)) {
+ addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
+ addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
}
+ }
- function bindOptionalChainRest(node: OptionalChain) {
- switch (node.kind) {
- case SyntaxKind.PropertyAccessExpression:
- bind(node.questionDotToken);
- bind(node.name);
- break;
- case SyntaxKind.ElementAccessExpression:
- bind(node.questionDotToken);
- bind(node.argumentExpression);
- break;
- case SyntaxKind.CallExpression:
- bind(node.questionDotToken);
- bindEach(node.typeArguments);
- bindEach(node.arguments);
- break;
- }
+ function bindOptionalChainRest(node: OptionalChain) {
+ switch (node.kind) {
+ case SyntaxKind.PropertyAccessExpression:
+ bind(node.questionDotToken);
+ bind(node.name);
+ break;
+ case SyntaxKind.ElementAccessExpression:
+ bind(node.questionDotToken);
+ bind(node.argumentExpression);
+ break;
+ case SyntaxKind.CallExpression:
+ bind(node.questionDotToken);
+ bindEach(node.typeArguments);
+ bindEach(node.arguments);
+ break;
}
+ }
- function bindOptionalChain(node: OptionalChain, trueTarget: FlowLabel, falseTarget: FlowLabel) {
- // For an optional chain, we emulate the behavior of a logical expression:
- //
- // a?.b -> a && a.b
- // a?.b.c -> a && a.b.c
- // a?.b?.c -> a && a.b && a.b.c
- // a?.[x = 1] -> a && a[x = 1]
- //
- // To do this we descend through the chain until we reach the root of a chain (the expression with a `?.`)
- // and build it's CFA graph as if it were the first condition (`a && ...`). Then we bind the rest
- // of the node as part of the "true" branch, and continue to do so as we ascend back up to the outermost
- // chain node. We then treat the entire node as the right side of the expression.
- const preChainLabel = isOptionalChainRoot(node) ? createBranchLabel() : undefined;
- bindOptionalExpression(node.expression, preChainLabel || trueTarget, falseTarget);
- if (preChainLabel) {
- currentFlow = finishFlowLabel(preChainLabel);
- }
- doWithConditionalBranches(bindOptionalChainRest, node, trueTarget, falseTarget);
- if (isOutermostOptionalChain(node)) {
- addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
- addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
- }
+ function bindOptionalChain(node: OptionalChain, trueTarget: FlowLabel, falseTarget: FlowLabel) {
+ // For an optional chain, we emulate the behavior of a logical expression:
+ //
+ // a?.b -> a && a.b
+ // a?.b.c -> a && a.b.c
+ // a?.b?.c -> a && a.b && a.b.c
+ // a?.[x = 1] -> a && a[x = 1]
+ //
+ // To do this we descend through the chain until we reach the root of a chain (the expression with a `?.`)
+ // and build it's CFA graph as if it were the first condition (`a && ...`). Then we bind the rest
+ // of the node as part of the "true" branch, and continue to do so as we ascend back up to the outermost
+ // chain node. We then treat the entire node as the right side of the expression.
+ const preChainLabel = isOptionalChainRoot(node) ? createBranchLabel() : undefined;
+ bindOptionalExpression(node.expression, preChainLabel || trueTarget, falseTarget);
+ if (preChainLabel) {
+ currentFlow = finishFlowLabel(preChainLabel);
+ }
+ doWithConditionalBranches(bindOptionalChainRest, node, trueTarget, falseTarget);
+ if (isOutermostOptionalChain(node)) {
+ addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
+ addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
}
+ }
- function bindOptionalChainFlow(node: OptionalChain) {
- if (isTopLevelLogicalExpression(node)) {
- const postExpressionLabel = createBranchLabel();
- bindOptionalChain(node, postExpressionLabel, postExpressionLabel);
- currentFlow = finishFlowLabel(postExpressionLabel);
- }
- else {
- bindOptionalChain(node, currentTrueTarget!, currentFalseTarget!);
- }
+ function bindOptionalChainFlow(node: OptionalChain) {
+ if (isTopLevelLogicalExpression(node)) {
+ const postExpressionLabel = createBranchLabel();
+ bindOptionalChain(node, postExpressionLabel, postExpressionLabel);
+ currentFlow = finishFlowLabel(postExpressionLabel);
+ }
+ else {
+ bindOptionalChain(node, currentTrueTarget!, currentFalseTarget!);
}
+ }
- function bindNonNullExpressionFlow(node: NonNullExpression | NonNullChain) {
- if (isOptionalChain(node)) {
- bindOptionalChainFlow(node);
- }
- else {
- bindEachChild(node);
- }
+ function bindNonNullExpressionFlow(node: NonNullExpression | NonNullChain) {
+ if (isOptionalChain(node)) {
+ bindOptionalChainFlow(node);
+ }
+ else {
+ bindEachChild(node);
}
+ }
- function bindAccessExpressionFlow(node: AccessExpression | PropertyAccessChain | ElementAccessChain) {
- if (isOptionalChain(node)) {
- bindOptionalChainFlow(node);
- }
- else {
- bindEachChild(node);
- }
+ function bindAccessExpressionFlow(node: AccessExpression | PropertyAccessChain | ElementAccessChain) {
+ if (isOptionalChain(node)) {
+ bindOptionalChainFlow(node);
+ }
+ else {
+ bindEachChild(node);
}
+ }
- function bindCallExpressionFlow(node: CallExpression | CallChain) {
- if (isOptionalChain(node)) {
- bindOptionalChainFlow(node);
+ function bindCallExpressionFlow(node: CallExpression | CallChain) {
+ if (isOptionalChain(node)) {
+ bindOptionalChainFlow(node);
+ }
+ else {
+ // If the target of the call expression is a function expression or arrow function we have
+ // an immediately invoked function expression (IIFE). Initialize the flowNode property to
+ // the current control flow (which includes evaluation of the IIFE arguments).
+ const expr = skipParentheses(node.expression);
+ if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) {
+ bindEach(node.typeArguments);
+ bindEach(node.arguments);
+ bind(node.expression);
}
else {
- // If the target of the call expression is a function expression or arrow function we have
- // an immediately invoked function expression (IIFE). Initialize the flowNode property to
- // the current control flow (which includes evaluation of the IIFE arguments).
- const expr = skipParentheses(node.expression);
- if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) {
- bindEach(node.typeArguments);
- bindEach(node.arguments);
- bind(node.expression);
- }
- else {
- bindEachChild(node);
- if (node.expression.kind === SyntaxKind.SuperKeyword) {
- currentFlow = createFlowCall(currentFlow, node);
- }
+ bindEachChild(node);
+ if (node.expression.kind === SyntaxKind.SuperKeyword) {
+ currentFlow = createFlowCall(currentFlow, node);
}
}
- if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
- const propertyAccess = node.expression as PropertyAccessExpression;
- if (isIdentifier(propertyAccess.name) && isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
- currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node);
- }
+ }
+ if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
+ const propertyAccess = node.expression as PropertyAccessExpression;
+ if (isIdentifier(propertyAccess.name) && isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
+ currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node);
}
}
+ }
- function getContainerFlags(node: Node): ContainerFlags {
- switch (node.kind) {
- case SyntaxKind.ClassExpression:
- case SyntaxKind.ClassDeclaration:
- case SyntaxKind.EnumDeclaration:
- case SyntaxKind.ObjectLiteralExpression:
- case SyntaxKind.TypeLiteral:
- case SyntaxKind.JSDocTypeLiteral:
- case SyntaxKind.JsxAttributes:
- return ContainerFlags.IsContainer;
+ function getContainerFlags(node: Node): ContainerFlags {
+ switch (node.kind) {
+ case SyntaxKind.ClassExpression:
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.EnumDeclaration:
+ case SyntaxKind.ObjectLiteralExpression:
+ case SyntaxKind.TypeLiteral:
+ case SyntaxKind.JSDocTypeLiteral:
+ case SyntaxKind.JsxAttributes:
+ return ContainerFlags.IsContainer;
- case SyntaxKind.InterfaceDeclaration:
- return ContainerFlags.IsContainer | ContainerFlags.IsInterface;
+ case SyntaxKind.InterfaceDeclaration:
+ return ContainerFlags.IsContainer | ContainerFlags.IsInterface;
- case SyntaxKind.ModuleDeclaration:
- case SyntaxKind.TypeAliasDeclaration:
- case SyntaxKind.MappedType:
- return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
+ case SyntaxKind.ModuleDeclaration:
+ case SyntaxKind.TypeAliasDeclaration:
+ case SyntaxKind.MappedType:
+ return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
+
+ case SyntaxKind.SourceFile:
+ return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals;
+
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ case SyntaxKind.MethodDeclaration:
+ if (isObjectLiteralOrClassExpressionMethodOrAccessor(node)) {
+ return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor;
+ }
+ // falls through
+ case SyntaxKind.Constructor:
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.MethodSignature:
+ case SyntaxKind.CallSignature:
+ case SyntaxKind.JSDocSignature:
+ case SyntaxKind.JSDocFunctionType:
+ case SyntaxKind.FunctionType:
+ case SyntaxKind.ConstructSignature:
+ case SyntaxKind.IndexSignature:
+ case SyntaxKind.ConstructorType:
+ case SyntaxKind.ClassStaticBlockDeclaration:
+ return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;
+
+ case SyntaxKind.FunctionExpression:
+ case SyntaxKind.ArrowFunction:
+ return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression;
+
+ case SyntaxKind.ModuleBlock:
+ return ContainerFlags.IsControlFlowContainer;
+ case SyntaxKind.PropertyDeclaration:
+ return (node as PropertyDeclaration).initializer ? ContainerFlags.IsControlFlowContainer : 0;
+
+ case SyntaxKind.CatchClause:
+ case SyntaxKind.ForStatement:
+ case SyntaxKind.ForInStatement:
+ case SyntaxKind.ForOfStatement:
+ case SyntaxKind.CaseBlock:
+ return ContainerFlags.IsBlockScopedContainer;
+
+ case SyntaxKind.Block:
+ // do not treat blocks directly inside a function as a block-scoped-container.
+ // Locals that reside in this block should go to the function locals. Otherwise 'x'
+ // would not appear to be a redeclaration of a block scoped local in the following
+ // example:
+ //
+ // function foo() {
+ // var x;
+ // let x;
+ // }
+ //
+ // If we placed 'var x' into the function locals and 'let x' into the locals of
+ // the block, then there would be no collision.
+ //
+ // By not creating a new block-scoped-container here, we ensure that both 'var x'
+ // and 'let x' go into the Function-container's locals, and we do get a collision
+ // conflict.
+ return isFunctionLike(node.parent) || isClassStaticBlockDeclaration(node.parent) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer;
+ }
- case SyntaxKind.SourceFile:
- return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals;
+ return ContainerFlags.None;
+ }
- case SyntaxKind.GetAccessor:
- case SyntaxKind.SetAccessor:
- case SyntaxKind.MethodDeclaration:
- if (isObjectLiteralOrClassExpressionMethodOrAccessor(node)) {
- return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor;
- }
- // falls through
- case SyntaxKind.Constructor:
- case SyntaxKind.FunctionDeclaration:
- case SyntaxKind.MethodSignature:
- case SyntaxKind.CallSignature:
- case SyntaxKind.JSDocSignature:
- case SyntaxKind.JSDocFunctionType:
- case SyntaxKind.FunctionType:
- case SyntaxKind.ConstructSignature:
- case SyntaxKind.IndexSignature:
- case SyntaxKind.ConstructorType:
- case SyntaxKind.ClassStaticBlockDeclaration:
- return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;
-
- case SyntaxKind.FunctionExpression:
- case SyntaxKind.ArrowFunction:
- return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression;
-
- case SyntaxKind.ModuleBlock:
- return ContainerFlags.IsControlFlowContainer;
- case SyntaxKind.PropertyDeclaration:
- return (node as PropertyDeclaration).initializer ? ContainerFlags.IsControlFlowContainer : 0;
-
- case SyntaxKind.CatchClause:
- case SyntaxKind.ForStatement:
- case SyntaxKind.ForInStatement:
- case SyntaxKind.ForOfStatement:
- case SyntaxKind.CaseBlock:
- return ContainerFlags.IsBlockScopedContainer;
-
- case SyntaxKind.Block:
- // do not treat blocks directly inside a function as a block-scoped-container.
- // Locals that reside in this block should go to the function locals. Otherwise 'x'
- // would not appear to be a redeclaration of a block scoped local in the following
- // example:
- //
- // function foo() {
- // var x;
- // let x;
- // }
- //
- // If we placed 'var x' into the function locals and 'let x' into the locals of
- // the block, then there would be no collision.
- //
- // By not creating a new block-scoped-container here, we ensure that both 'var x'
- // and 'let x' go into the Function-container's locals, and we do get a collision
- // conflict.
- return isFunctionLike(node.parent) || isClassStaticBlockDeclaration(node.parent) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer;
- }
-
- return ContainerFlags.None;
- }
-
- function addToContainerChain(next: Node) {
- if (lastContainer) {
- lastContainer.nextContainer = next;
- }
-
- lastContainer = next;
- }
-
- function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol | undefined {
- switch (container.kind) {
- // Modules, source files, and classes need specialized handling for how their
- // members are declared (for example, a member of a class will go into a specific
- // symbol table depending on if it is static or not). We defer to specialized
- // handlers to take care of declaring these child members.
- case SyntaxKind.ModuleDeclaration:
- return declareModuleMember(node, symbolFlags, symbolExcludes);
-
- case SyntaxKind.SourceFile:
- return declareSourceFileMember(node, symbolFlags, symbolExcludes);
-
- case SyntaxKind.ClassExpression:
- case SyntaxKind.ClassDeclaration:
- return declareClassMember(node, symbolFlags, symbolExcludes);
-
- case SyntaxKind.EnumDeclaration:
- return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes);
-
- case SyntaxKind.TypeLiteral:
- case SyntaxKind.JSDocTypeLiteral:
- case SyntaxKind.ObjectLiteralExpression:
- case SyntaxKind.InterfaceDeclaration:
- case SyntaxKind.JsxAttributes:
- // Interface/Object-types always have their children added to the 'members' of
- // their container. They are only accessible through an instance of their
- // container, and are never in scope otherwise (even inside the body of the
- // object / type / interface declaring them). An exception is type parameters,
- // which are in scope without qualification (similar to 'locals').
- return declareSymbol(container.symbol.members!, container.symbol, node, symbolFlags, symbolExcludes);
-
- case SyntaxKind.FunctionType:
- case SyntaxKind.ConstructorType:
- case SyntaxKind.CallSignature:
- case SyntaxKind.ConstructSignature:
- case SyntaxKind.JSDocSignature:
- case SyntaxKind.IndexSignature:
- case SyntaxKind.MethodDeclaration:
- case SyntaxKind.MethodSignature:
- case SyntaxKind.Constructor:
- case SyntaxKind.GetAccessor:
- case SyntaxKind.SetAccessor:
- case SyntaxKind.FunctionDeclaration:
- case SyntaxKind.FunctionExpression:
- case SyntaxKind.ArrowFunction:
- case SyntaxKind.JSDocFunctionType:
- case SyntaxKind.JSDocTypedefTag:
- case SyntaxKind.JSDocCallbackTag:
- case SyntaxKind.ClassStaticBlockDeclaration:
- case SyntaxKind.TypeAliasDeclaration:
- case SyntaxKind.MappedType:
- // All the children of these container types are never visible through another
- // symbol (i.e. through another symbol's 'exports' or 'members'). Instead,
- // they're only accessed 'lexically' (i.e. from code that exists underneath
- // their container in the tree). To accomplish this, we simply add their declared
- // symbol to the 'locals' of the container. These symbols can then be found as
- // the type checker walks up the containers, checking them for matching names.
- return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
- }
- }
-
- function declareClassMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
- return isStatic(node)
- ? declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes)
- : declareSymbol(container.symbol.members!, container.symbol, node, symbolFlags, symbolExcludes);
- }
-
- function declareSourceFileMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
- return isExternalModule(file)
- ? declareModuleMember(node, symbolFlags, symbolExcludes)
- : declareSymbol(file.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
- }
-
- function hasExportDeclarations(node: ModuleDeclaration | SourceFile): boolean {
- const body = isSourceFile(node) ? node : tryCast(node.body, isModuleBlock);
- return !!body && body.statements.some(s => isExportDeclaration(s) || isExportAssignment(s));
- }
-
- function setExportContextFlag(node: Mutable) {
- // A declaration source file or ambient module declaration that contains no export declarations (but possibly regular
- // declarations with export modifiers) is an export context in which declarations are implicitly exported.
- if (node.flags & NodeFlags.Ambient && !hasExportDeclarations(node)) {
- node.flags |= NodeFlags.ExportContext;
- }
- else {
- node.flags &= ~NodeFlags.ExportContext;
- }
+ function addToContainerChain(next: Node) {
+ if (lastContainer) {
+ lastContainer.nextContainer = next;
}
- function bindModuleDeclaration(node: ModuleDeclaration) {
- setExportContextFlag(node);
- if (isAmbientModule(node)) {
- if (hasSyntacticModifier(node, ModifierFlags.Export)) {
- errorOnFirstToken(node, Diagnostics.export_modifier_cannot_be_applied_to_ambient_modules_and_module_augmentations_since_they_are_always_visible);
- }
- if (isModuleAugmentationExternal(node)) {
- declareModuleSymbol(node);
- }
- else {
- let pattern: string | Pattern | undefined;
- if (node.name.kind === SyntaxKind.StringLiteral) {
- const { text } = node.name;
- pattern = tryParsePattern(text);
- if (pattern === undefined) {
- errorOnFirstToken(node.name, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, text);
- }
- }
+ lastContainer = next;
+ }
- const symbol = declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes)!;
- file.patternAmbientModules = append(file.patternAmbientModules, pattern && !isString(pattern) ? { pattern, symbol } : undefined);
- }
+ function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): ts.Symbol | undefined {
+ switch (container.kind) {
+ // Modules, source files, and classes need specialized handling for how their
+ // members are declared (for example, a member of a class will go into a specific
+ // symbol table depending on if it is static or not). We defer to specialized
+ // handlers to take care of declaring these child members.
+ case SyntaxKind.ModuleDeclaration:
+ return declareModuleMember(node, symbolFlags, symbolExcludes);
+
+ case SyntaxKind.SourceFile:
+ return declareSourceFileMember(node, symbolFlags, symbolExcludes);
+
+ case SyntaxKind.ClassExpression:
+ case SyntaxKind.ClassDeclaration:
+ return declareClassMember(node, symbolFlags, symbolExcludes);
+
+ case SyntaxKind.EnumDeclaration:
+ return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes);
+
+ case SyntaxKind.TypeLiteral:
+ case SyntaxKind.JSDocTypeLiteral:
+ case SyntaxKind.ObjectLiteralExpression:
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.JsxAttributes:
+ // Interface/Object-types always have their children added to the 'members' of
+ // their container. They are only accessible through an instance of their
+ // container, and are never in scope otherwise (even inside the body of the
+ // object / type / interface declaring them). An exception is type parameters,
+ // which are in scope without qualification (similar to 'locals').
+ return declareSymbol(container.symbol.members!, container.symbol, node, symbolFlags, symbolExcludes);
+
+ case SyntaxKind.FunctionType:
+ case SyntaxKind.ConstructorType:
+ case SyntaxKind.CallSignature:
+ case SyntaxKind.ConstructSignature:
+ case SyntaxKind.JSDocSignature:
+ case SyntaxKind.IndexSignature:
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.MethodSignature:
+ case SyntaxKind.Constructor:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.FunctionExpression:
+ case SyntaxKind.ArrowFunction:
+ case SyntaxKind.JSDocFunctionType:
+ case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocCallbackTag:
+ case SyntaxKind.ClassStaticBlockDeclaration:
+ case SyntaxKind.TypeAliasDeclaration:
+ case SyntaxKind.MappedType:
+ // All the children of these container types are never visible through another
+ // symbol (i.e. through another symbol's 'exports' or 'members'). Instead,
+ // they're only accessed 'lexically' (i.e. from code that exists underneath
+ // their container in the tree). To accomplish this, we simply add their declared
+ // symbol to the 'locals' of the container. These symbols can then be found as
+ // the type checker walks up the containers, checking them for matching names.
+ return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
+ }
+ }
+
+ function declareClassMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
+ return isStatic(node)
+ ? declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes)
+ : declareSymbol(container.symbol.members!, container.symbol, node, symbolFlags, symbolExcludes);
+ }
+
+ function declareSourceFileMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
+ return isExternalModule(file)
+ ? declareModuleMember(node, symbolFlags, symbolExcludes)
+ : declareSymbol(file.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
+ }
+
+ function hasExportDeclarations(node: ModuleDeclaration | SourceFile): boolean {
+ const body = isSourceFile(node) ? node : tryCast(node.body, isModuleBlock);
+ return !!body && body.statements.some(s => isExportDeclaration(s) || isExportAssignment(s));
+ }
+
+ function setExportContextFlag(node: Mutable) {
+ // A declaration source file or ambient module declaration that contains no export declarations (but possibly regular
+ // declarations with export modifiers) is an export context in which declarations are implicitly exported.
+ if (node.flags & NodeFlags.Ambient && !hasExportDeclarations(node)) {
+ node.flags |= NodeFlags.ExportContext;
+ }
+ else {
+ node.flags &= ~NodeFlags.ExportContext;
+ }
+ }
+
+ function bindModuleDeclaration(node: ModuleDeclaration) {
+ setExportContextFlag(node);
+ if (isAmbientModule(node)) {
+ if (hasSyntacticModifier(node, ModifierFlags.Export)) {
+ errorOnFirstToken(node, Diagnostics.export_modifier_cannot_be_applied_to_ambient_modules_and_module_augmentations_since_they_are_always_visible);
+ }
+ if (isModuleAugmentationExternal(node)) {
+ declareModuleSymbol(node);
}
else {
- const state = declareModuleSymbol(node);
- if (state !== ModuleInstanceState.NonInstantiated) {
- const { symbol } = node;
- // if module was already merged with some function, class or non-const enum, treat it as non-const-enum-only
- symbol.constEnumOnlyModule = (!(symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum)))
- // Current must be `const enum` only
- && state === ModuleInstanceState.ConstEnumOnly
- // Can't have been set to 'false' in a previous merged symbol. ('undefined' OK)
- && symbol.constEnumOnlyModule !== false;
+ let pattern: string | Pattern | undefined;
+ if (node.name.kind === SyntaxKind.StringLiteral) {
+ const { text } = node.name;
+ pattern = tryParsePattern(text);
+ if (pattern === undefined) {
+ errorOnFirstToken(node.name, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, text);
+ }
}
+
+ const symbol = declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes)!;
+ file.patternAmbientModules = append(file.patternAmbientModules, pattern && !isString(pattern) ? { pattern, symbol } : undefined);
}
}
-
- function declareModuleSymbol(node: ModuleDeclaration): ModuleInstanceState {
- const state = getModuleInstanceState(node);
- const instantiated = state !== ModuleInstanceState.NonInstantiated;
- declareSymbolAndAddToSymbolTable(node,
- instantiated ? SymbolFlags.ValueModule : SymbolFlags.NamespaceModule,
- instantiated ? SymbolFlags.ValueModuleExcludes : SymbolFlags.NamespaceModuleExcludes);
- return state;
+ else {
+ const state = declareModuleSymbol(node);
+ if (state !== ModuleInstanceState.NonInstantiated) {
+ const { symbol } = node;
+ // if module was already merged with some function, class or non-const enum, treat it as non-const-enum-only
+ symbol.constEnumOnlyModule = (!(symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum)))
+ // Current must be `const enum` only
+ && state === ModuleInstanceState.ConstEnumOnly
+ // Can't have been set to 'false' in a previous merged symbol. ('undefined' OK)
+ && symbol.constEnumOnlyModule !== false;
+ }
}
+ }
- function bindFunctionOrConstructorType(node: SignatureDeclaration | JSDocSignature): void {
- // For a given function symbol "<...>(...) => T" we want to generate a symbol identical
- // to the one we would get for: { <...>(...): T }
- //
- // We do that by making an anonymous type literal symbol, and then setting the function
- // symbol as its sole member. To the rest of the system, this symbol will be indistinguishable
- // from an actual type literal symbol you would have gotten had you used the long form.
- const symbol = createSymbol(SymbolFlags.Signature, getDeclarationName(node)!); // TODO: GH#18217
- addDeclarationToSymbol(symbol, node, SymbolFlags.Signature);
+ function declareModuleSymbol(node: ModuleDeclaration): ModuleInstanceState {
+ const state = getModuleInstanceState(node);
+ const instantiated = state !== ModuleInstanceState.NonInstantiated;
+ declareSymbolAndAddToSymbolTable(node, instantiated ? SymbolFlags.ValueModule : SymbolFlags.NamespaceModule, instantiated ? SymbolFlags.ValueModuleExcludes : SymbolFlags.NamespaceModuleExcludes);
+ return state;
+ }
+
+ function bindFunctionOrConstructorType(node: SignatureDeclaration | JSDocSignature): void {
+ // For a given function symbol "<...>(...) => T" we want to generate a symbol identical
+ // to the one we would get for: { <...>(...): T }
+ //
+ // We do that by making an anonymous type literal symbol, and then setting the function
+ // symbol as its sole member. To the rest of the system, this symbol will be indistinguishable
+ // from an actual type literal symbol you would have gotten had you used the long form.
+ const symbol = createSymbol(SymbolFlags.Signature, getDeclarationName(node)!); // TODO: GH#18217
+ addDeclarationToSymbol(symbol, node, SymbolFlags.Signature);
+
+ const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type);
+ addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral);
+ typeLiteralSymbol.members = createSymbolTable();
+ typeLiteralSymbol.members.set(symbol.escapedName, symbol);
+ }
- const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type);
- addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral);
- typeLiteralSymbol.members = createSymbolTable();
- typeLiteralSymbol.members.set(symbol.escapedName, symbol);
+ function bindObjectLiteralExpression(node: ObjectLiteralExpression) {
+ const enum ElementKind {
+ Property = 1,
+ Accessor = 2
}
- function bindObjectLiteralExpression(node: ObjectLiteralExpression) {
- const enum ElementKind {
- Property = 1,
- Accessor = 2
- }
+ if (inStrictMode && !isAssignmentTarget(node)) {
+ const seen = new ts.Map<__String, ElementKind>();
- if (inStrictMode && !isAssignmentTarget(node)) {
- const seen = new Map<__String, ElementKind>();
+ for (const prop of node.properties) {
+ if (prop.kind === SyntaxKind.SpreadAssignment || prop.name.kind !== SyntaxKind.Identifier) {
+ continue;
+ }
- for (const prop of node.properties) {
- if (prop.kind === SyntaxKind.SpreadAssignment || prop.name.kind !== SyntaxKind.Identifier) {
- continue;
- }
+ const identifier = prop.name;
- const identifier = prop.name;
-
- // ECMA-262 11.1.5 Object Initializer
- // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true
- // a.This production is contained in strict code and IsDataDescriptor(previous) is true and
- // IsDataDescriptor(propId.descriptor) is true.
- // b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true.
- // c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true.
- // d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true
- // and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields
- const currentKind = prop.kind === SyntaxKind.PropertyAssignment || prop.kind === SyntaxKind.ShorthandPropertyAssignment || prop.kind === SyntaxKind.MethodDeclaration
- ? ElementKind.Property
- : ElementKind.Accessor;
-
- const existingKind = seen.get(identifier.escapedText);
- if (!existingKind) {
- seen.set(identifier.escapedText, currentKind);
- continue;
- }
+ // ECMA-262 11.1.5 Object Initializer
+ // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true
+ // a.This production is contained in strict code and IsDataDescriptor(previous) is true and
+ // IsDataDescriptor(propId.descriptor) is true.
+ // b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true.
+ // c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true.
+ // d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true
+ // and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields
+ const currentKind = prop.kind === SyntaxKind.PropertyAssignment || prop.kind === SyntaxKind.ShorthandPropertyAssignment || prop.kind === SyntaxKind.MethodDeclaration
+ ? ElementKind.Property
+ : ElementKind.Accessor;
+
+ const existingKind = seen.get(identifier.escapedText);
+ if (!existingKind) {
+ seen.set(identifier.escapedText, currentKind);
+ continue;
}
}
-
- return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.Object);
}
- function bindJsxAttributes(node: JsxAttributes) {
- return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.JSXAttributes);
- }
+ return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.Object);
+ }
- function bindJsxAttribute(node: JsxAttribute, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
- return declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
- }
+ function bindJsxAttributes(node: JsxAttributes) {
+ return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.JSXAttributes);
+ }
- function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: __String) {
- const symbol = createSymbol(symbolFlags, name);
- if (symbolFlags & (SymbolFlags.EnumMember | SymbolFlags.ClassMember)) {
- symbol.parent = container.symbol;
- }
- addDeclarationToSymbol(symbol, node, symbolFlags);
- return symbol;
+ function bindJsxAttribute(node: JsxAttribute, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
+ return declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
+ }
+
+ function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: __String) {
+ const symbol = createSymbol(symbolFlags, name);
+ if (symbolFlags & (SymbolFlags.EnumMember | SymbolFlags.ClassMember)) {
+ symbol.parent = container.symbol;
}
+ addDeclarationToSymbol(symbol, node, symbolFlags);
+ return symbol;
+ }
- function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
- switch (blockScopeContainer.kind) {
- case SyntaxKind.ModuleDeclaration:
+ function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
+ switch (blockScopeContainer.kind) {
+ case SyntaxKind.ModuleDeclaration:
+ declareModuleMember(node, symbolFlags, symbolExcludes);
+ break;
+ case SyntaxKind.SourceFile:
+ if (isExternalOrCommonJsModule(container as SourceFile)) {
declareModuleMember(node, symbolFlags, symbolExcludes);
break;
- case SyntaxKind.SourceFile:
- if (isExternalOrCommonJsModule(container as SourceFile)) {
- declareModuleMember(node, symbolFlags, symbolExcludes);
- break;
+ }
+ // falls through
+ default:
+ if (!blockScopeContainer.locals) {
+ blockScopeContainer.locals = createSymbolTable();
+ addToContainerChain(blockScopeContainer);
+ }
+ declareSymbol(blockScopeContainer.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
+ }
+ }
+
+ function delayedBindJSDocTypedefTag() {
+ if (!delayedTypeAliases) {
+ return;
+ }
+ const saveContainer = container;
+ const saveLastContainer = lastContainer;
+ const saveBlockScopeContainer = blockScopeContainer;
+ const saveParent = parent;
+ const saveCurrentFlow = currentFlow;
+ for (const typeAlias of delayedTypeAliases) {
+ const host = typeAlias.parent.parent;
+ container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file;
+ blockScopeContainer = getEnclosingBlockScopeContainer(host) || file;
+ currentFlow = initFlowNode({ flags: FlowFlags.Start });
+ parent = typeAlias;
+ bind(typeAlias.typeExpression);
+ const declName = getNameOfDeclaration(typeAlias);
+ if ((isJSDocEnumTag(typeAlias) || !typeAlias.fullName) && declName && isPropertyAccessEntityNameExpression(declName.parent)) {
+ // typedef anchored to an A.B.C assignment - we need to bind into B's namespace under name C
+ const isTopLevel = isTopLevelNamespaceAssignment(declName.parent);
+ if (isTopLevel) {
+ bindPotentiallyMissingNamespaces(file.symbol, declName.parent, isTopLevel, !!findAncestor(declName, d => isPropertyAccessExpression(d) && d.name.escapedText === "prototype"), /*containerIsClass*/ false);
+ const oldContainer = container;
+ switch (getAssignmentDeclarationPropertyAccessKind(declName.parent)) {
+ case AssignmentDeclarationKind.ExportsProperty:
+ case AssignmentDeclarationKind.ModuleExports:
+ if (!isExternalOrCommonJsModule(file)) {
+ container = undefined!;
+ }
+ else {
+ container = file;
+ }
+ break;
+ case AssignmentDeclarationKind.ThisProperty:
+ container = declName.parent.expression;
+ break;
+ case AssignmentDeclarationKind.PrototypeProperty:
+ container = (declName.parent.expression as PropertyAccessExpression).name;
+ break;
+ case AssignmentDeclarationKind.Property:
+ container = isExportsOrModuleExportsOrAlias(file, declName.parent.expression) ? file
+ : isPropertyAccessExpression(declName.parent.expression) ? declName.parent.expression.name
+ : declName.parent.expression;
+ break;
+ case AssignmentDeclarationKind.None:
+ return Debug.fail("Shouldn't have detected typedef or enum on non-assignment declaration");
}
- // falls through
- default:
- if (!blockScopeContainer.locals) {
- blockScopeContainer.locals = createSymbolTable();
- addToContainerChain(blockScopeContainer);
+ if (container) {
+ declareModuleMember(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
}
- declareSymbol(blockScopeContainer.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
+ container = oldContainer;
+ }
+ }
+ else if (isJSDocEnumTag(typeAlias) || !typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) {
+ parent = typeAlias.parent;
+ bindBlockScopedDeclaration(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
+ }
+ else {
+ bind(typeAlias.fullName);
}
}
+ container = saveContainer;
+ lastContainer = saveLastContainer;
+ blockScopeContainer = saveBlockScopeContainer;
+ parent = saveParent;
+ currentFlow = saveCurrentFlow;
+ }
- function delayedBindJSDocTypedefTag() {
- if (!delayedTypeAliases) {
- return;
+ // The binder visits every node in the syntax tree so it is a convenient place to perform a single localized
+ // check for reserved words used as identifiers in strict mode code, as well as `yield` or `await` in
+ // [Yield] or [Await] contexts, respectively.
+ function checkContextualIdentifier(node: Identifier) {
+ // Report error only if there are no parse errors in file
+ if (!file.parseDiagnostics.length &&
+ !(node.flags & NodeFlags.Ambient) &&
+ !(node.flags & NodeFlags.JSDoc) &&
+ !isIdentifierName(node)) {
+
+ // strict mode identifiers
+ if (inStrictMode &&
+ node.originalKeywordKind! >= SyntaxKind.FirstFutureReservedWord &&
+ node.originalKeywordKind! <= SyntaxKind.LastFutureReservedWord) {
+ file.bindDiagnostics.push(createDiagnosticForNode(node, getStrictModeIdentifierMessage(node), declarationNameToString(node)));
}
- const saveContainer = container;
- const saveLastContainer = lastContainer;
- const saveBlockScopeContainer = blockScopeContainer;
- const saveParent = parent;
- const saveCurrentFlow = currentFlow;
- for (const typeAlias of delayedTypeAliases) {
- const host = typeAlias.parent.parent;
- container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file;
- blockScopeContainer = getEnclosingBlockScopeContainer(host) || file;
- currentFlow = initFlowNode({ flags: FlowFlags.Start });
- parent = typeAlias;
- bind(typeAlias.typeExpression);
- const declName = getNameOfDeclaration(typeAlias);
- if ((isJSDocEnumTag(typeAlias) || !typeAlias.fullName) && declName && isPropertyAccessEntityNameExpression(declName.parent)) {
- // typedef anchored to an A.B.C assignment - we need to bind into B's namespace under name C
- const isTopLevel = isTopLevelNamespaceAssignment(declName.parent);
- if (isTopLevel) {
- bindPotentiallyMissingNamespaces(file.symbol, declName.parent, isTopLevel,
- !!findAncestor(declName, d => isPropertyAccessExpression(d) && d.name.escapedText === "prototype"), /*containerIsClass*/ false);
- const oldContainer = container;
- switch (getAssignmentDeclarationPropertyAccessKind(declName.parent)) {
- case AssignmentDeclarationKind.ExportsProperty:
- case AssignmentDeclarationKind.ModuleExports:
- if (!isExternalOrCommonJsModule(file)) {
- container = undefined!;
- }
- else {
- container = file;
- }
- break;
- case AssignmentDeclarationKind.ThisProperty:
- container = declName.parent.expression;
- break;
- case AssignmentDeclarationKind.PrototypeProperty:
- container = (declName.parent.expression as PropertyAccessExpression).name;
- break;
- case AssignmentDeclarationKind.Property:
- container = isExportsOrModuleExportsOrAlias(file, declName.parent.expression) ? file
- : isPropertyAccessExpression(declName.parent.expression) ? declName.parent.expression.name
- : declName.parent.expression;
- break;
- case AssignmentDeclarationKind.None:
- return Debug.fail("Shouldn't have detected typedef or enum on non-assignment declaration");
- }
- if (container) {
- declareModuleMember(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
- }
- container = oldContainer;
- }
+ else if (node.originalKeywordKind === SyntaxKind.AwaitKeyword) {
+ if (isExternalModule(file) && isInTopLevelContext(node)) {
+ file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module, declarationNameToString(node)));
}
- else if (isJSDocEnumTag(typeAlias) || !typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) {
- parent = typeAlias.parent;
- bindBlockScopedDeclaration(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
- }
- else {
- bind(typeAlias.fullName);
+ else if (node.flags & NodeFlags.AwaitContext) {
+ file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, declarationNameToString(node)));
}
}
- container = saveContainer;
- lastContainer = saveLastContainer;
- blockScopeContainer = saveBlockScopeContainer;
- parent = saveParent;
- currentFlow = saveCurrentFlow;
+ else if (node.originalKeywordKind === SyntaxKind.YieldKeyword && node.flags & NodeFlags.YieldContext) {
+ file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, declarationNameToString(node)));
+ }
}
+ }
- // The binder visits every node in the syntax tree so it is a convenient place to perform a single localized
- // check for reserved words used as identifiers in strict mode code, as well as `yield` or `await` in
- // [Yield] or [Await] contexts, respectively.
- function checkContextualIdentifier(node: Identifier) {
- // Report error only if there are no parse errors in file
- if (!file.parseDiagnostics.length &&
- !(node.flags & NodeFlags.Ambient) &&
- !(node.flags & NodeFlags.JSDoc) &&
- !isIdentifierName(node)) {
-
- // strict mode identifiers
- if (inStrictMode &&
- node.originalKeywordKind! >= SyntaxKind.FirstFutureReservedWord &&
- node.originalKeywordKind! <= SyntaxKind.LastFutureReservedWord) {
- file.bindDiagnostics.push(createDiagnosticForNode(node,
- getStrictModeIdentifierMessage(node), declarationNameToString(node)));
- }
- else if (node.originalKeywordKind === SyntaxKind.AwaitKeyword) {
- if (isExternalModule(file) && isInTopLevelContext(node)) {
- file.bindDiagnostics.push(createDiagnosticForNode(node,
- Diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module,
- declarationNameToString(node)));
- }
- else if (node.flags & NodeFlags.AwaitContext) {
- file.bindDiagnostics.push(createDiagnosticForNode(node,
- Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here,
- declarationNameToString(node)));
- }
- }
- else if (node.originalKeywordKind === SyntaxKind.YieldKeyword && node.flags & NodeFlags.YieldContext) {
- file.bindDiagnostics.push(createDiagnosticForNode(node,
- Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here,
- declarationNameToString(node)));
- }
- }
+ function getStrictModeIdentifierMessage(node: Node) {
+ // Provide specialized messages to help the user understand why we think they're in
+ // strict mode.
+ if (getContainingClass(node)) {
+ return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode;
}
- function getStrictModeIdentifierMessage(node: Node) {
- // Provide specialized messages to help the user understand why we think they're in
- // strict mode.
- if (getContainingClass(node)) {
- return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode;
- }
+ if (file.externalModuleIndicator) {
+ return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode;
+ }
- if (file.externalModuleIndicator) {
- return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode;
- }
+ return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode;
+ }
- return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode;
+ // The binder visits every node, so this is a good place to check for
+ // the reserved private name (there is only one)
+ function checkPrivateIdentifier(node: PrivateIdentifier) {
+ if (node.escapedText === "#constructor") {
+ // Report error only if there are no parse errors in file
+ if (!file.parseDiagnostics.length) {
+ file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.constructor_is_a_reserved_word, declarationNameToString(node)));
+ }
}
+ }
- // The binder visits every node, so this is a good place to check for
- // the reserved private name (there is only one)
- function checkPrivateIdentifier(node: PrivateIdentifier) {
- if (node.escapedText === "#constructor") {
- // Report error only if there are no parse errors in file
- if (!file.parseDiagnostics.length) {
- file.bindDiagnostics.push(createDiagnosticForNode(node,
- Diagnostics.constructor_is_a_reserved_word, declarationNameToString(node)));
- }
- }
+ function checkStrictModeBinaryExpression(node: BinaryExpression) {
+ if (inStrictMode && isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operatorToken.kind)) {
+ // ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an
+ // Assignment operator(11.13) or of a PostfixExpression(11.3)
+ checkStrictModeEvalOrArguments(node, node.left as Identifier);
}
+ }
- function checkStrictModeBinaryExpression(node: BinaryExpression) {
- if (inStrictMode && isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operatorToken.kind)) {
- // ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an
- // Assignment operator(11.13) or of a PostfixExpression(11.3)
- checkStrictModeEvalOrArguments(node, node.left as Identifier);
- }
+ function checkStrictModeCatchClause(node: CatchClause) {
+ // It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the
+ // Catch production is eval or arguments
+ if (inStrictMode && node.variableDeclaration) {
+ checkStrictModeEvalOrArguments(node, node.variableDeclaration.name);
}
+ }
- function checkStrictModeCatchClause(node: CatchClause) {
- // It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the
- // Catch production is eval or arguments
- if (inStrictMode && node.variableDeclaration) {
- checkStrictModeEvalOrArguments(node, node.variableDeclaration.name);
- }
+ function checkStrictModeDeleteExpression(node: DeleteExpression) {
+ // Grammar checking
+ if (inStrictMode && node.expression.kind === SyntaxKind.Identifier) {
+ // When a delete operator occurs within strict mode code, a SyntaxError is thrown if its
+ // UnaryExpression is a direct reference to a variable, function argument, or function name
+ const span = getErrorSpanForNode(file, node.expression);
+ file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode));
}
+ }
+
+ function isEvalOrArgumentsIdentifier(node: Node): boolean {
+ return isIdentifier(node) && (node.escapedText === "eval" || node.escapedText === "arguments");
+ }
- function checkStrictModeDeleteExpression(node: DeleteExpression) {
- // Grammar checking
- if (inStrictMode && node.expression.kind === SyntaxKind.Identifier) {
- // When a delete operator occurs within strict mode code, a SyntaxError is thrown if its
- // UnaryExpression is a direct reference to a variable, function argument, or function name
- const span = getErrorSpanForNode(file, node.expression);
- file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode));
+ function checkStrictModeEvalOrArguments(contextNode: Node, name: Node | undefined) {
+ if (name && name.kind === SyntaxKind.Identifier) {
+ const identifier = name as Identifier;
+ if (isEvalOrArgumentsIdentifier(identifier)) {
+ // We check first if the name is inside class declaration or class expression; if so give explicit message
+ // otherwise report generic error message.
+ const span = getErrorSpanForNode(file, name);
+ file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, getStrictModeEvalOrArgumentsMessage(contextNode), idText(identifier)));
}
}
+ }
- function isEvalOrArgumentsIdentifier(node: Node): boolean {
- return isIdentifier(node) && (node.escapedText === "eval" || node.escapedText === "arguments");
+ function getStrictModeEvalOrArgumentsMessage(node: Node) {
+ // Provide specialized messages to help the user understand why we think they're in
+ // strict mode.
+ if (getContainingClass(node)) {
+ return Diagnostics.Code_contained_in_a_class_is_evaluated_in_JavaScript_s_strict_mode_which_does_not_allow_this_use_of_0_For_more_information_see_https_Colon_Slash_Slashdeveloper_mozilla_org_Slashen_US_Slashdocs_SlashWeb_SlashJavaScript_SlashReference_SlashStrict_mode;
}
- function checkStrictModeEvalOrArguments(contextNode: Node, name: Node | undefined) {
- if (name && name.kind === SyntaxKind.Identifier) {
- const identifier = name as Identifier;
- if (isEvalOrArgumentsIdentifier(identifier)) {
- // We check first if the name is inside class declaration or class expression; if so give explicit message
- // otherwise report generic error message.
- const span = getErrorSpanForNode(file, name);
- file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length,
- getStrictModeEvalOrArgumentsMessage(contextNode), idText(identifier)));
- }
- }
+ if (file.externalModuleIndicator) {
+ return Diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode;
}
- function getStrictModeEvalOrArgumentsMessage(node: Node) {
- // Provide specialized messages to help the user understand why we think they're in
- // strict mode.
- if (getContainingClass(node)) {
- return Diagnostics.Code_contained_in_a_class_is_evaluated_in_JavaScript_s_strict_mode_which_does_not_allow_this_use_of_0_For_more_information_see_https_Colon_Slash_Slashdeveloper_mozilla_org_Slashen_US_Slashdocs_SlashWeb_SlashJavaScript_SlashReference_SlashStrict_mode;
- }
+ return Diagnostics.Invalid_use_of_0_in_strict_mode;
+ }
- if (file.externalModuleIndicator) {
- return Diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode;
- }
+ function checkStrictModeFunctionName(node: FunctionLikeDeclaration) {
+ if (inStrictMode) {
+ // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1))
+ checkStrictModeEvalOrArguments(node, node.name);
+ }
+ }
- return Diagnostics.Invalid_use_of_0_in_strict_mode;
+ function getStrictModeBlockScopeFunctionDeclarationMessage(node: Node) {
+ // Provide specialized messages to help the user understand why we think they're in
+ // strict mode.
+ if (getContainingClass(node)) {
+ return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5_Class_definitions_are_automatically_in_strict_mode;
}
- function checkStrictModeFunctionName(node: FunctionLikeDeclaration) {
- if (inStrictMode) {
- // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1))
- checkStrictModeEvalOrArguments(node, node.name);
- }
+ if (file.externalModuleIndicator) {
+ return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5_Modules_are_automatically_in_strict_mode;
}
- function getStrictModeBlockScopeFunctionDeclarationMessage(node: Node) {
- // Provide specialized messages to help the user understand why we think they're in
- // strict mode.
- if (getContainingClass(node)) {
- return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5_Class_definitions_are_automatically_in_strict_mode;
- }
+ return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5;
+ }
- if (file.externalModuleIndicator) {
- return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5_Modules_are_automatically_in_strict_mode;
+ function checkStrictModeFunctionDeclaration(node: FunctionDeclaration) {
+ if (languageVersion < ScriptTarget.ES2015) {
+ // Report error if function is not top level function declaration
+ if (blockScopeContainer.kind !== SyntaxKind.SourceFile &&
+ blockScopeContainer.kind !== SyntaxKind.ModuleDeclaration &&
+ !isFunctionLikeOrClassStaticBlockDeclaration(blockScopeContainer)) {
+ // We check first if the name is inside class declaration or class expression; if so give explicit message
+ // otherwise report generic error message.
+ const errorSpan = getErrorSpanForNode(file, node);
+ file.bindDiagnostics.push(createFileDiagnostic(file, errorSpan.start, errorSpan.length, getStrictModeBlockScopeFunctionDeclarationMessage(node)));
}
-
- return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5;
}
+ }
- function checkStrictModeFunctionDeclaration(node: FunctionDeclaration) {
- if (languageVersion < ScriptTarget.ES2015) {
- // Report error if function is not top level function declaration
- if (blockScopeContainer.kind !== SyntaxKind.SourceFile &&
- blockScopeContainer.kind !== SyntaxKind.ModuleDeclaration &&
- !isFunctionLikeOrClassStaticBlockDeclaration(blockScopeContainer)) {
- // We check first if the name is inside class declaration or class expression; if so give explicit message
- // otherwise report generic error message.
- const errorSpan = getErrorSpanForNode(file, node);
- file.bindDiagnostics.push(createFileDiagnostic(file, errorSpan.start, errorSpan.length,
- getStrictModeBlockScopeFunctionDeclarationMessage(node)));
- }
- }
+ function checkStrictModeNumericLiteral(node: NumericLiteral) {
+ if (languageVersion < ScriptTarget.ES5 && inStrictMode && node.numericLiteralFlags & TokenFlags.Octal) {
+ file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode));
}
+ }
- function checkStrictModeNumericLiteral(node: NumericLiteral) {
- if (languageVersion < ScriptTarget.ES5 && inStrictMode && node.numericLiteralFlags & TokenFlags.Octal) {
- file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode));
- }
+ function checkStrictModePostfixUnaryExpression(node: PostfixUnaryExpression) {
+ // Grammar checking
+ // The identifier eval or arguments may not appear as the LeftHandSideExpression of an
+ // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression
+ // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator.
+ if (inStrictMode) {
+ checkStrictModeEvalOrArguments(node, node.operand as Identifier);
}
+ }
- function checkStrictModePostfixUnaryExpression(node: PostfixUnaryExpression) {
- // Grammar checking
- // The identifier eval or arguments may not appear as the LeftHandSideExpression of an
- // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression
- // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator.
- if (inStrictMode) {
+ function checkStrictModePrefixUnaryExpression(node: PrefixUnaryExpression) {
+ // Grammar checking
+ if (inStrictMode) {
+ if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) {
checkStrictModeEvalOrArguments(node, node.operand as Identifier);
}
}
+ }
- function checkStrictModePrefixUnaryExpression(node: PrefixUnaryExpression) {
- // Grammar checking
- if (inStrictMode) {
- if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) {
- checkStrictModeEvalOrArguments(node, node.operand as Identifier);
- }
- }
+ function checkStrictModeWithStatement(node: WithStatement) {
+ // Grammar checking for withStatement
+ if (inStrictMode) {
+ errorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode);
}
+ }
- function checkStrictModeWithStatement(node: WithStatement) {
- // Grammar checking for withStatement
- if (inStrictMode) {
- errorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode);
+ function checkStrictModeLabeledStatement(node: LabeledStatement) {
+ // Grammar checking for labeledStatement
+ if (inStrictMode && getEmitScriptTarget(options) >= ScriptTarget.ES2015) {
+ if (isDeclarationStatement(node.statement) || isVariableStatement(node.statement)) {
+ errorOnFirstToken(node.label, Diagnostics.A_label_is_not_allowed_here);
}
}
+ }
- function checkStrictModeLabeledStatement(node: LabeledStatement) {
- // Grammar checking for labeledStatement
- if (inStrictMode && getEmitScriptTarget(options) >= ScriptTarget.ES2015) {
- if (isDeclarationStatement(node.statement) || isVariableStatement(node.statement)) {
- errorOnFirstToken(node.label, Diagnostics.A_label_is_not_allowed_here);
- }
- }
- }
+ function errorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) {
+ const span = getSpanOfTokenAtPosition(file, node.pos);
+ file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2));
+ }
- function errorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) {
- const span = getSpanOfTokenAtPosition(file, node.pos);
- file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2));
- }
+ function errorOrSuggestionOnNode(isError: boolean, node: Node, message: DiagnosticMessage): void {
+ errorOrSuggestionOnRange(isError, node, node, message);
+ }
- function errorOrSuggestionOnNode(isError: boolean, node: Node, message: DiagnosticMessage): void {
- errorOrSuggestionOnRange(isError, node, node, message);
- }
+ function errorOrSuggestionOnRange(isError: boolean, startNode: Node, endNode: Node, message: DiagnosticMessage): void {
+ addErrorOrSuggestionDiagnostic(isError, { pos: getTokenPosOfNode(startNode, file), end: endNode.end }, message);
+ }
- function errorOrSuggestionOnRange(isError: boolean, startNode: Node, endNode: Node, message: DiagnosticMessage): void {
- addErrorOrSuggestionDiagnostic(isError, { pos: getTokenPosOfNode(startNode, file), end: endNode.end }, message);
+ function addErrorOrSuggestionDiagnostic(isError: boolean, range: TextRange, message: DiagnosticMessage): void {
+ const diag = createFileDiagnostic(file, range.pos, range.end - range.pos, message);
+ if (isError) {
+ file.bindDiagnostics.push(diag);
}
-
- function addErrorOrSuggestionDiagnostic(isError: boolean, range: TextRange, message: DiagnosticMessage): void {
- const diag = createFileDiagnostic(file, range.pos, range.end - range.pos, message);
- if (isError) {
- file.bindDiagnostics.push(diag);
- }
- else {
- file.bindSuggestionDiagnostics = append(file.bindSuggestionDiagnostics, { ...diag, category: DiagnosticCategory.Suggestion });
- }
+ else {
+ file.bindSuggestionDiagnostics = append(file.bindSuggestionDiagnostics, { ...diag, category: DiagnosticCategory.Suggestion });
}
+ }
- function bind(node: Node | undefined): void {
- if (!node) {
- return;
- }
- setParent(node, parent);
- const saveInStrictMode = inStrictMode;
+ function bind(node: Node | undefined): void {
+ if (!node) {
+ return;
+ }
+ setParent(node, parent);
+ const saveInStrictMode = inStrictMode;
- // Even though in the AST the jsdoc @typedef node belongs to the current node,
- // its symbol might be in the same scope with the current node's symbol. Consider:
- //
- // /** @typedef {string | number} MyType */
- // function foo();
- //
- // Here the current node is "foo", which is a container, but the scope of "MyType" should
- // not be inside "foo". Therefore we always bind @typedef before bind the parent node,
- // and skip binding this tag later when binding all the other jsdoc tags.
+ // Even though in the AST the jsdoc @typedef node belongs to the current node,
+ // its symbol might be in the same scope with the current node's symbol. Consider:
+ //
+ // /** @typedef {string | number} MyType */
+ // function foo();
+ //
+ // Here the current node is "foo", which is a container, but the scope of "MyType" should
+ // not be inside "foo". Therefore we always bind @typedef before bind the parent node,
+ // and skip binding this tag later when binding all the other jsdoc tags.
- // First we bind declaration nodes to a symbol if possible. We'll both create a symbol
- // and then potentially add the symbol to an appropriate symbol table. Possible
- // destination symbol tables are:
- //
- // 1) The 'exports' table of the current container's symbol.
- // 2) The 'members' table of the current container's symbol.
- // 3) The 'locals' table of the current container.
- //
- // However, not all symbols will end up in any of these tables. 'Anonymous' symbols
- // (like TypeLiterals for example) will not be put in any table.
- bindWorker(node);
- // Then we recurse into the children of the node to bind them as well. For certain
- // symbols we do specialized work when we recurse. For example, we'll keep track of
- // the current 'container' node when it changes. This helps us know which symbol table
- // a local should go into for example. Since terminal nodes are known not to have
- // children, as an optimization we don't process those.
- if (node.kind > SyntaxKind.LastToken) {
- const saveParent = parent;
- parent = node;
- const containerFlags = getContainerFlags(node);
- if (containerFlags === ContainerFlags.None) {
- bindChildren(node);
- }
- else {
- bindContainer(node, containerFlags);
- }
- parent = saveParent;
+ // First we bind declaration nodes to a symbol if possible. We'll both create a symbol
+ // and then potentially add the symbol to an appropriate symbol table. Possible
+ // destination symbol tables are:
+ //
+ // 1) The 'exports' table of the current container's symbol.
+ // 2) The 'members' table of the current container's symbol.
+ // 3) The 'locals' table of the current container.
+ //
+ // However, not all symbols will end up in any of these tables. 'Anonymous' symbols
+ // (like TypeLiterals for example) will not be put in any table.
+ bindWorker(node);
+ // Then we recurse into the children of the node to bind them as well. For certain
+ // symbols we do specialized work when we recurse. For example, we'll keep track of
+ // the current 'container' node when it changes. This helps us know which symbol table
+ // a local should go into for example. Since terminal nodes are known not to have
+ // children, as an optimization we don't process those.
+ if (node.kind > SyntaxKind.LastToken) {
+ const saveParent = parent;
+ parent = node;
+ const containerFlags = getContainerFlags(node);
+ if (containerFlags === ContainerFlags.None) {
+ bindChildren(node);
}
else {
- const saveParent = parent;
- if (node.kind === SyntaxKind.EndOfFileToken) parent = node;
- bindJSDoc(node);
- parent = saveParent;
+ bindContainer(node, containerFlags);
}
- inStrictMode = saveInStrictMode;
+ parent = saveParent;
+ }
+ else {
+ const saveParent = parent;
+ if (node.kind === SyntaxKind.EndOfFileToken)
+ parent = node;
+ bindJSDoc(node);
+ parent = saveParent;
}
+ inStrictMode = saveInStrictMode;
+ }
- function bindJSDoc(node: Node) {
- if (hasJSDocNodes(node)) {
- if (isInJSFile(node)) {
- for (const j of node.jsDoc!) {
- bind(j);
- }
+ function bindJSDoc(node: Node) {
+ if (hasJSDocNodes(node)) {
+ if (isInJSFile(node)) {
+ for (const j of node.jsDoc!) {
+ bind(j);
}
- else {
- for (const j of node.jsDoc!) {
- setParent(j, node);
- setParentRecursive(j, /*incremental*/ false);
- }
+ }
+ else {
+ for (const j of node.jsDoc!) {
+ setParent(j, node);
+ setParentRecursive(j, /*incremental*/ false);
}
}
}
+ }
- function updateStrictModeStatementList(statements: NodeArray) {
- if (!inStrictMode) {
- for (const statement of statements) {
- if (!isPrologueDirective(statement)) {
- return;
- }
+ function updateStrictModeStatementList(statements: NodeArray) {
+ if (!inStrictMode) {
+ for (const statement of statements) {
+ if (!isPrologueDirective(statement)) {
+ return;
+ }
- if (isUseStrictPrologueDirective(statement as ExpressionStatement)) {
- inStrictMode = true;
- return;
- }
+ if (isUseStrictPrologueDirective(statement as ExpressionStatement)) {
+ inStrictMode = true;
+ return;
}
}
}
+ }
- /// Should be called only on prologue directives (isPrologueDirective(node) should be true)
- function isUseStrictPrologueDirective(node: ExpressionStatement): boolean {
- const nodeText = getSourceTextOfNodeFromSourceFile(file, node.expression);
+ /// Should be called only on prologue directives (isPrologueDirective(node) should be true)
+ function isUseStrictPrologueDirective(node: ExpressionStatement): boolean {
+ const nodeText = getSourceTextOfNodeFromSourceFile(file, node.expression);
- // Note: the node text must be exactly "use strict" or 'use strict'. It is not ok for the
- // string to contain unicode escapes (as per ES5).
- return nodeText === '"use strict"' || nodeText === "'use strict'";
- }
+ // Note: the node text must be exactly "use strict" or 'use strict'. It is not ok for the
+ // string to contain unicode escapes (as per ES5).
+ return nodeText === '"use strict"' || nodeText === "'use strict'";
+ }
- function bindWorker(node: Node) {
- switch (node.kind) {
- /* Strict mode checks */
- case SyntaxKind.Identifier:
- // for typedef type names with namespaces, bind the new jsdoc type symbol here
- // because it requires all containing namespaces to be in effect, namely the
- // current "blockScopeContainer" needs to be set to its immediate namespace parent.
- if ((node as Identifier).isInJSDocNamespace) {
- let parentNode = node.parent;
- while (parentNode && !isJSDocTypeAlias(parentNode)) {
- parentNode = parentNode.parent;
- }
- bindBlockScopedDeclaration(parentNode as Declaration, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
- break;
- }
- // falls through
- case SyntaxKind.ThisKeyword:
- if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) {
- node.flowNode = currentFlow;
- }
- return checkContextualIdentifier(node as Identifier);
- case SyntaxKind.QualifiedName:
- if (currentFlow && isPartOfTypeQuery(node)) {
- node.flowNode = currentFlow;
+ function bindWorker(node: Node) {
+ switch (node.kind) {
+ /* Strict mode checks */
+ case SyntaxKind.Identifier:
+ // for typedef type names with namespaces, bind the new jsdoc type symbol here
+ // because it requires all containing namespaces to be in effect, namely the
+ // current "blockScopeContainer" needs to be set to its immediate namespace parent.
+ if ((node as Identifier).isInJSDocNamespace) {
+ let parentNode = node.parent;
+ while (parentNode && !isJSDocTypeAlias(parentNode)) {
+ parentNode = parentNode.parent;
}
+ bindBlockScopedDeclaration(parentNode as Declaration, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
break;
- case SyntaxKind.MetaProperty:
- case SyntaxKind.SuperKeyword:
+ }
+ // falls through
+ case SyntaxKind.ThisKeyword:
+ if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) {
node.flowNode = currentFlow;
- break;
- case SyntaxKind.PrivateIdentifier:
- return checkPrivateIdentifier(node as PrivateIdentifier);
- case SyntaxKind.PropertyAccessExpression:
- case SyntaxKind.ElementAccessExpression:
- const expr = node as PropertyAccessExpression | ElementAccessExpression;
- if (currentFlow && isNarrowableReference(expr)) {
- expr.flowNode = currentFlow;
- }
- if (isSpecialPropertyDeclaration(expr)) {
- bindSpecialPropertyDeclaration(expr);
- }
- if (isInJSFile(expr) &&
- file.commonJsModuleIndicator &&
- isModuleExportsAccessExpression(expr) &&
- !lookupSymbolForName(blockScopeContainer, "module" as __String)) {
- declareSymbol(file.locals!, /*parent*/ undefined, expr.expression,
- SymbolFlags.FunctionScopedVariable | SymbolFlags.ModuleExports, SymbolFlags.FunctionScopedVariableExcludes);
- }
- break;
- case SyntaxKind.BinaryExpression:
- const specialKind = getAssignmentDeclarationKind(node as BinaryExpression);
- switch (specialKind) {
- case AssignmentDeclarationKind.ExportsProperty:
- bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression);
- break;
- case AssignmentDeclarationKind.ModuleExports:
- bindModuleExportsAssignment(node as BindablePropertyAssignmentExpression);
- break;
- case AssignmentDeclarationKind.PrototypeProperty:
- bindPrototypePropertyAssignment((node as BindableStaticPropertyAssignmentExpression).left, node);
- break;
- case AssignmentDeclarationKind.Prototype:
- bindPrototypeAssignment(node as BindableStaticPropertyAssignmentExpression);
- break;
- case AssignmentDeclarationKind.ThisProperty:
- bindThisPropertyAssignment(node as BindablePropertyAssignmentExpression);
- break;
- case AssignmentDeclarationKind.Property:
- const expression = ((node as BinaryExpression).left as AccessExpression).expression;
- if (isInJSFile(node) && isIdentifier(expression)) {
- const symbol = lookupSymbolForName(blockScopeContainer, expression.escapedText);
- if (isThisInitializedDeclaration(symbol?.valueDeclaration)) {
- bindThisPropertyAssignment(node as BindablePropertyAssignmentExpression);
- break;
- }
- }
- bindSpecialPropertyAssignment(node as BindablePropertyAssignmentExpression);
- break;
- case AssignmentDeclarationKind.None:
- // Nothing to do
- break;
- default:
- Debug.fail("Unknown binary expression special property assignment kind");
- }
- return checkStrictModeBinaryExpression(node as BinaryExpression);
- case SyntaxKind.CatchClause:
- return checkStrictModeCatchClause(node as CatchClause);
- case SyntaxKind.DeleteExpression:
- return checkStrictModeDeleteExpression(node as DeleteExpression);
- case SyntaxKind.NumericLiteral:
- return checkStrictModeNumericLiteral(node as NumericLiteral);
- case SyntaxKind.PostfixUnaryExpression:
- return checkStrictModePostfixUnaryExpression(node as PostfixUnaryExpression);
- case SyntaxKind.PrefixUnaryExpression:
- return checkStrictModePrefixUnaryExpression(node as PrefixUnaryExpression);
- case SyntaxKind.WithStatement:
- return checkStrictModeWithStatement(node as WithStatement);
- case SyntaxKind.LabeledStatement:
- return checkStrictModeLabeledStatement(node as LabeledStatement);
- case SyntaxKind.ThisType:
- seenThisKeyword = true;
- return;
- case SyntaxKind.TypePredicate:
- break; // Binding the children will handle everything
- case SyntaxKind.TypeParameter:
- return bindTypeParameter(node as TypeParameterDeclaration);
- case SyntaxKind.Parameter:
- return bindParameter(node as ParameterDeclaration);
- case SyntaxKind.VariableDeclaration:
- return bindVariableDeclarationOrBindingElement(node as VariableDeclaration);
- case SyntaxKind.BindingElement:
+ }
+ return checkContextualIdentifier(node as Identifier);
+ case SyntaxKind.QualifiedName:
+ if (currentFlow && isPartOfTypeQuery(node)) {
node.flowNode = currentFlow;
- return bindVariableDeclarationOrBindingElement(node as BindingElement);
- case SyntaxKind.PropertyDeclaration:
- case SyntaxKind.PropertySignature:
- return bindPropertyWorker(node as PropertyDeclaration | PropertySignature);
- case SyntaxKind.PropertyAssignment:
- case SyntaxKind.ShorthandPropertyAssignment:
- return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
- case SyntaxKind.EnumMember:
- return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes);
-
- case SyntaxKind.CallSignature:
- case SyntaxKind.ConstructSignature:
- case SyntaxKind.IndexSignature:
- return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Signature, SymbolFlags.None);
- case SyntaxKind.MethodDeclaration:
- case SyntaxKind.MethodSignature:
- // If this is an ObjectLiteralExpression method, then it sits in the same space
- // as other properties in the object literal. So we use SymbolFlags.PropertyExcludes
- // so that it will conflict with any other object literal members with the same
- // name.
- return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.Method | ((node as MethodDeclaration).questionToken ? SymbolFlags.Optional : SymbolFlags.None),
- isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes);
- case SyntaxKind.FunctionDeclaration:
- return bindFunctionDeclaration(node as FunctionDeclaration);
- case SyntaxKind.Constructor:
- return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Constructor, /*symbolExcludes:*/ SymbolFlags.None);
- case SyntaxKind.GetAccessor:
- return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes);
- case SyntaxKind.SetAccessor:
- return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes);
- case SyntaxKind.FunctionType:
- case SyntaxKind.JSDocFunctionType:
- case SyntaxKind.JSDocSignature:
- case SyntaxKind.ConstructorType:
- return bindFunctionOrConstructorType(node as SignatureDeclaration | JSDocSignature);
- case SyntaxKind.TypeLiteral:
- case SyntaxKind.JSDocTypeLiteral:
- case SyntaxKind.MappedType:
- return bindAnonymousTypeWorker(node as TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral);
- case SyntaxKind.JSDocClassTag:
- return bindJSDocClassTag(node as JSDocClassTag);
- case SyntaxKind.ObjectLiteralExpression:
- return bindObjectLiteralExpression(node as ObjectLiteralExpression);
- case SyntaxKind.FunctionExpression:
- case SyntaxKind.ArrowFunction:
- return bindFunctionExpression(node as FunctionExpression);
-
- case SyntaxKind.CallExpression:
- const assignmentKind = getAssignmentDeclarationKind(node as CallExpression);
- switch (assignmentKind) {
- case AssignmentDeclarationKind.ObjectDefinePropertyValue:
- return bindObjectDefinePropertyAssignment(node as BindableObjectDefinePropertyCall);
- case AssignmentDeclarationKind.ObjectDefinePropertyExports:
- return bindObjectDefinePropertyExport(node as BindableObjectDefinePropertyCall);
- case AssignmentDeclarationKind.ObjectDefinePrototypeProperty:
- return bindObjectDefinePrototypeProperty(node as BindableObjectDefinePropertyCall);
- case AssignmentDeclarationKind.None:
- break; // Nothing to do
- default:
- return Debug.fail("Unknown call expression assignment declaration kind");
- }
- if (isInJSFile(node)) {
- bindCallExpression(node as CallExpression);
- }
- break;
+ }
+ break;
+ case SyntaxKind.MetaProperty:
+ case SyntaxKind.SuperKeyword:
+ node.flowNode = currentFlow;
+ break;
+ case SyntaxKind.PrivateIdentifier:
+ return checkPrivateIdentifier(node as PrivateIdentifier);
+ case SyntaxKind.PropertyAccessExpression:
+ case SyntaxKind.ElementAccessExpression:
+ const expr = node as PropertyAccessExpression | ElementAccessExpression;
+ if (currentFlow && isNarrowableReference(expr)) {
+ expr.flowNode = currentFlow;
+ }
+ if (isSpecialPropertyDeclaration(expr)) {
+ bindSpecialPropertyDeclaration(expr);
+ }
+ if (isInJSFile(expr) &&
+ file.commonJsModuleIndicator &&
+ isModuleExportsAccessExpression(expr) &&
+ !lookupSymbolForName(blockScopeContainer, "module" as __String)) {
+ declareSymbol(file.locals!, /*parent*/ undefined, expr.expression, SymbolFlags.FunctionScopedVariable | SymbolFlags.ModuleExports, SymbolFlags.FunctionScopedVariableExcludes);
+ }
+ break;
+ case SyntaxKind.BinaryExpression:
+ const specialKind = getAssignmentDeclarationKind(node as BinaryExpression);
+ switch (specialKind) {
+ case AssignmentDeclarationKind.ExportsProperty:
+ bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression);
+ break;
+ case AssignmentDeclarationKind.ModuleExports:
+ bindModuleExportsAssignment(node as BindablePropertyAssignmentExpression);
+ break;
+ case AssignmentDeclarationKind.PrototypeProperty:
+ bindPrototypePropertyAssignment((node as BindableStaticPropertyAssignmentExpression).left, node);
+ break;
+ case AssignmentDeclarationKind.Prototype:
+ bindPrototypeAssignment(node as BindableStaticPropertyAssignmentExpression);
+ break;
+ case AssignmentDeclarationKind.ThisProperty:
+ bindThisPropertyAssignment(node as BindablePropertyAssignmentExpression);
+ break;
+ case AssignmentDeclarationKind.Property:
+ const expression = ((node as BinaryExpression).left as AccessExpression).expression;
+ if (isInJSFile(node) && isIdentifier(expression)) {
+ const symbol = lookupSymbolForName(blockScopeContainer, expression.escapedText);
+ if (isThisInitializedDeclaration(symbol?.valueDeclaration)) {
+ bindThisPropertyAssignment(node as BindablePropertyAssignmentExpression);
+ break;
+ }
+ }
+ bindSpecialPropertyAssignment(node as BindablePropertyAssignmentExpression);
+ break;
+ case AssignmentDeclarationKind.None:
+ // Nothing to do
+ break;
+ default:
+ Debug.fail("Unknown binary expression special property assignment kind");
+ }
+ return checkStrictModeBinaryExpression(node as BinaryExpression);
+ case SyntaxKind.CatchClause:
+ return checkStrictModeCatchClause(node as CatchClause);
+ case SyntaxKind.DeleteExpression:
+ return checkStrictModeDeleteExpression(node as DeleteExpression);
+ case SyntaxKind.NumericLiteral:
+ return checkStrictModeNumericLiteral(node as NumericLiteral);
+ case SyntaxKind.PostfixUnaryExpression:
+ return checkStrictModePostfixUnaryExpression(node as PostfixUnaryExpression);
+ case SyntaxKind.PrefixUnaryExpression:
+ return checkStrictModePrefixUnaryExpression(node as PrefixUnaryExpression);
+ case SyntaxKind.WithStatement:
+ return checkStrictModeWithStatement(node as WithStatement);
+ case SyntaxKind.LabeledStatement:
+ return checkStrictModeLabeledStatement(node as LabeledStatement);
+ case SyntaxKind.ThisType:
+ seenThisKeyword = true;
+ return;
+ case SyntaxKind.TypePredicate:
+ break; // Binding the children will handle everything
+ case SyntaxKind.TypeParameter:
+ return bindTypeParameter(node as TypeParameterDeclaration);
+ case SyntaxKind.Parameter:
+ return bindParameter(node as ParameterDeclaration);
+ case SyntaxKind.VariableDeclaration:
+ return bindVariableDeclarationOrBindingElement(node as VariableDeclaration);
+ case SyntaxKind.BindingElement:
+ node.flowNode = currentFlow;
+ return bindVariableDeclarationOrBindingElement(node as BindingElement);
+ case SyntaxKind.PropertyDeclaration:
+ case SyntaxKind.PropertySignature:
+ return bindPropertyWorker(node as PropertyDeclaration | PropertySignature);
+ case SyntaxKind.PropertyAssignment:
+ case SyntaxKind.ShorthandPropertyAssignment:
+ return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
+ case SyntaxKind.EnumMember:
+ return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes);
+
+ case SyntaxKind.CallSignature:
+ case SyntaxKind.ConstructSignature:
+ case SyntaxKind.IndexSignature:
+ return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Signature, SymbolFlags.None);
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.MethodSignature:
+ // If this is an ObjectLiteralExpression method, then it sits in the same space
+ // as other properties in the object literal. So we use SymbolFlags.PropertyExcludes
+ // so that it will conflict with any other object literal members with the same
+ // name.
+ return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.Method | ((node as MethodDeclaration).questionToken ? SymbolFlags.Optional : SymbolFlags.None), isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes);
+ case SyntaxKind.FunctionDeclaration:
+ return bindFunctionDeclaration(node as FunctionDeclaration);
+ case SyntaxKind.Constructor:
+ return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Constructor, /*symbolExcludes:*/ SymbolFlags.None);
+ case SyntaxKind.GetAccessor:
+ return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes);
+ case SyntaxKind.SetAccessor:
+ return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes);
+ case SyntaxKind.FunctionType:
+ case SyntaxKind.JSDocFunctionType:
+ case SyntaxKind.JSDocSignature:
+ case SyntaxKind.ConstructorType:
+ return bindFunctionOrConstructorType(node as SignatureDeclaration | JSDocSignature);
+ case SyntaxKind.TypeLiteral:
+ case SyntaxKind.JSDocTypeLiteral:
+ case SyntaxKind.MappedType:
+ return bindAnonymousTypeWorker(node as TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral);
+ case SyntaxKind.JSDocClassTag:
+ return bindJSDocClassTag(node as JSDocClassTag);
+ case SyntaxKind.ObjectLiteralExpression:
+ return bindObjectLiteralExpression(node as ObjectLiteralExpression);
+ case SyntaxKind.FunctionExpression:
+ case SyntaxKind.ArrowFunction:
+ return bindFunctionExpression(node as FunctionExpression);
+
+ case SyntaxKind.CallExpression:
+ const assignmentKind = getAssignmentDeclarationKind(node as CallExpression);
+ switch (assignmentKind) {
+ case AssignmentDeclarationKind.ObjectDefinePropertyValue:
+ return bindObjectDefinePropertyAssignment(node as BindableObjectDefinePropertyCall);
+ case AssignmentDeclarationKind.ObjectDefinePropertyExports:
+ return bindObjectDefinePropertyExport(node as BindableObjectDefinePropertyCall);
+ case AssignmentDeclarationKind.ObjectDefinePrototypeProperty:
+ return bindObjectDefinePrototypeProperty(node as BindableObjectDefinePropertyCall);
+ case AssignmentDeclarationKind.None:
+ break; // Nothing to do
+ default:
+ return Debug.fail("Unknown call expression assignment declaration kind");
+ }
+ if (isInJSFile(node)) {
+ bindCallExpression(node as CallExpression);
+ }
+ break;
- // Members of classes, interfaces, and modules
- case SyntaxKind.ClassExpression:
- case SyntaxKind.ClassDeclaration:
- // All classes are automatically in strict mode in ES6.
- inStrictMode = true;
- return bindClassLikeDeclaration(node as ClassLikeDeclaration);
- case SyntaxKind.InterfaceDeclaration:
- return bindBlockScopedDeclaration(node as Declaration, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes);
- case SyntaxKind.TypeAliasDeclaration:
- return bindBlockScopedDeclaration(node as Declaration, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
- case SyntaxKind.EnumDeclaration:
- return bindEnumDeclaration(node as EnumDeclaration);
- case SyntaxKind.ModuleDeclaration:
- return bindModuleDeclaration(node as ModuleDeclaration);
- // Jsx-attributes
- case SyntaxKind.JsxAttributes:
- return bindJsxAttributes(node as JsxAttributes);
- case SyntaxKind.JsxAttribute:
- return bindJsxAttribute(node as JsxAttribute, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
-
- // Imports and exports
- case SyntaxKind.ImportEqualsDeclaration:
- case SyntaxKind.NamespaceImport:
- case SyntaxKind.ImportSpecifier:
- case SyntaxKind.ExportSpecifier:
- return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
- case SyntaxKind.NamespaceExportDeclaration:
- return bindNamespaceExportDeclaration(node as NamespaceExportDeclaration);
- case SyntaxKind.ImportClause:
- return bindImportClause(node as ImportClause);
- case SyntaxKind.ExportDeclaration:
- return bindExportDeclaration(node as ExportDeclaration);
- case SyntaxKind.ExportAssignment:
- return bindExportAssignment(node as ExportAssignment);
- case SyntaxKind.SourceFile:
- updateStrictModeStatementList((node as SourceFile).statements);
- return bindSourceFileIfExternalModule();
- case SyntaxKind.Block:
- if (!isFunctionLikeOrClassStaticBlockDeclaration(node.parent)) {
- return;
- }
- // falls through
- case SyntaxKind.ModuleBlock:
- return updateStrictModeStatementList((node as Block | ModuleBlock).statements);
+ // Members of classes, interfaces, and modules
+ case SyntaxKind.ClassExpression:
+ case SyntaxKind.ClassDeclaration:
+ // All classes are automatically in strict mode in ES6.
+ inStrictMode = true;
+ return bindClassLikeDeclaration(node as ClassLikeDeclaration);
+ case SyntaxKind.InterfaceDeclaration:
+ return bindBlockScopedDeclaration(node as Declaration, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes);
+ case SyntaxKind.TypeAliasDeclaration:
+ return bindBlockScopedDeclaration(node as Declaration, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
+ case SyntaxKind.EnumDeclaration:
+ return bindEnumDeclaration(node as EnumDeclaration);
+ case SyntaxKind.ModuleDeclaration:
+ return bindModuleDeclaration(node as ModuleDeclaration);
+ // Jsx-attributes
+ case SyntaxKind.JsxAttributes:
+ return bindJsxAttributes(node as JsxAttributes);
+ case SyntaxKind.JsxAttribute:
+ return bindJsxAttribute(node as JsxAttribute, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
+
+ // Imports and exports
+ case SyntaxKind.ImportEqualsDeclaration:
+ case SyntaxKind.NamespaceImport:
+ case SyntaxKind.ImportSpecifier:
+ case SyntaxKind.ExportSpecifier:
+ return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
+ case SyntaxKind.NamespaceExportDeclaration:
+ return bindNamespaceExportDeclaration(node as NamespaceExportDeclaration);
+ case SyntaxKind.ImportClause:
+ return bindImportClause(node as ImportClause);
+ case SyntaxKind.ExportDeclaration:
+ return bindExportDeclaration(node as ExportDeclaration);
+ case SyntaxKind.ExportAssignment:
+ return bindExportAssignment(node as ExportAssignment);
+ case SyntaxKind.SourceFile:
+ updateStrictModeStatementList((node as SourceFile).statements);
+ return bindSourceFileIfExternalModule();
+ case SyntaxKind.Block:
+ if (!isFunctionLikeOrClassStaticBlockDeclaration(node.parent)) {
+ return;
+ }
+ // falls through
+ case SyntaxKind.ModuleBlock:
+ return updateStrictModeStatementList((node as Block | ModuleBlock).statements);
- case SyntaxKind.JSDocParameterTag:
- if (node.parent.kind === SyntaxKind.JSDocSignature) {
- return bindParameter(node as JSDocParameterTag);
- }
- if (node.parent.kind !== SyntaxKind.JSDocTypeLiteral) {
- break;
- }
- // falls through
- case SyntaxKind.JSDocPropertyTag:
- const propTag = node as JSDocPropertyLikeTag;
- const flags = propTag.isBracketed || propTag.typeExpression && propTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType ?
- SymbolFlags.Property | SymbolFlags.Optional :
- SymbolFlags.Property;
- return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes);
- case SyntaxKind.JSDocTypedefTag:
- case SyntaxKind.JSDocCallbackTag:
- case SyntaxKind.JSDocEnumTag:
- return (delayedTypeAliases || (delayedTypeAliases = [])).push(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag);
- }
+ case SyntaxKind.JSDocParameterTag:
+ if (node.parent.kind === SyntaxKind.JSDocSignature) {
+ return bindParameter(node as JSDocParameterTag);
+ }
+ if (node.parent.kind !== SyntaxKind.JSDocTypeLiteral) {
+ break;
+ }
+ // falls through
+ case SyntaxKind.JSDocPropertyTag:
+ const propTag = node as JSDocPropertyLikeTag;
+ const flags = propTag.isBracketed || propTag.typeExpression && propTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType ?
+ SymbolFlags.Property | SymbolFlags.Optional :
+ SymbolFlags.Property;
+ return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes);
+ case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocCallbackTag:
+ case SyntaxKind.JSDocEnumTag:
+ return (delayedTypeAliases || (delayedTypeAliases = [])).push(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag);
}
+ }
- function bindPropertyWorker(node: PropertyDeclaration | PropertySignature) {
- return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
- }
+ function bindPropertyWorker(node: PropertyDeclaration | PropertySignature) {
+ return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
+ }
- function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral) {
- return bindAnonymousDeclaration(node as Declaration, SymbolFlags.TypeLiteral, InternalSymbolName.Type);
- }
+ function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral) {
+ return bindAnonymousDeclaration(node as Declaration, SymbolFlags.TypeLiteral, InternalSymbolName.Type);
+ }
- function bindSourceFileIfExternalModule() {
- setExportContextFlag(file);
- if (isExternalModule(file)) {
- bindSourceFileAsExternalModule();
- }
- else if (isJsonSourceFile(file)) {
- bindSourceFileAsExternalModule();
- // Create symbol equivalent for the module.exports = {}
- const originalSymbol = file.symbol;
- declareSymbol(file.symbol.exports!, file.symbol, file, SymbolFlags.Property, SymbolFlags.All);
- file.symbol = originalSymbol;
- }
+ function bindSourceFileIfExternalModule() {
+ setExportContextFlag(file);
+ if (isExternalModule(file)) {
+ bindSourceFileAsExternalModule();
}
-
- function bindSourceFileAsExternalModule() {
- bindAnonymousDeclaration(file, SymbolFlags.ValueModule, `"${removeFileExtension(file.fileName)}"` as __String);
+ else if (isJsonSourceFile(file)) {
+ bindSourceFileAsExternalModule();
+ // Create symbol equivalent for the module.exports = {}
+ const originalSymbol = file.symbol;
+ declareSymbol(file.symbol.exports!, file.symbol, file, SymbolFlags.Property, SymbolFlags.All);
+ file.symbol = originalSymbol;
}
+ }
- function bindExportAssignment(node: ExportAssignment) {
- if (!container.symbol || !container.symbol.exports) {
- // Incorrect export assignment in some sort of block construct
- bindAnonymousDeclaration(node, SymbolFlags.Value, getDeclarationName(node)!);
- }
- else {
- const flags = exportAssignmentIsAlias(node)
- // An export default clause with an EntityNameExpression or a class expression exports all meanings of that identifier or expression;
- ? SymbolFlags.Alias
- // An export default clause with any other expression exports a value
- : SymbolFlags.Property;
- // If there is an `export default x;` alias declaration, can't `export default` anything else.
- // (In contrast, you can still have `export default function f() {}` and `export default interface I {}`.)
- const symbol = declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.All);
+ function bindSourceFileAsExternalModule() {
+ bindAnonymousDeclaration(file, SymbolFlags.ValueModule, `"${removeFileExtension(file.fileName)}"` as __String);
+ }
- if (node.isExportEquals) {
- // Will be an error later, since the module already has other exports. Just make sure this has a valueDeclaration set.
- setValueDeclaration(symbol, node);
- }
- }
+ function bindExportAssignment(node: ExportAssignment) {
+ if (!container.symbol || !container.symbol.exports) {
+ // Incorrect export assignment in some sort of block construct
+ bindAnonymousDeclaration(node, SymbolFlags.Value, getDeclarationName(node)!);
}
-
- function bindNamespaceExportDeclaration(node: NamespaceExportDeclaration) {
- if (node.modifiers && node.modifiers.length) {
- file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Modifiers_cannot_appear_here));
- }
- const diag = !isSourceFile(node.parent) ? Diagnostics.Global_module_exports_may_only_appear_at_top_level
- : !isExternalModule(node.parent) ? Diagnostics.Global_module_exports_may_only_appear_in_module_files
- : !node.parent.isDeclarationFile ? Diagnostics.Global_module_exports_may_only_appear_in_declaration_files
- : undefined;
- if (diag) {
- file.bindDiagnostics.push(createDiagnosticForNode(node, diag));
- }
- else {
- file.symbol.globalExports = file.symbol.globalExports || createSymbolTable();
- declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
+ else {
+ const flags = exportAssignmentIsAlias(node)
+ // An export default clause with an EntityNameExpression or a class expression exports all meanings of that identifier or expression;
+ ? SymbolFlags.Alias
+ // An export default clause with any other expression exports a value
+ : SymbolFlags.Property;
+ // If there is an `export default x;` alias declaration, can't `export default` anything else.
+ // (In contrast, you can still have `export default function f() {}` and `export default interface I {}`.)
+ const symbol = declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.All);
+
+ if (node.isExportEquals) {
+ // Will be an error later, since the module already has other exports. Just make sure this has a valueDeclaration set.
+ setValueDeclaration(symbol, node);
}
}
+ }
- function bindExportDeclaration(node: ExportDeclaration) {
- if (!container.symbol || !container.symbol.exports) {
- // Export * in some sort of block construct
- bindAnonymousDeclaration(node, SymbolFlags.ExportStar, getDeclarationName(node)!);
- }
- else if (!node.exportClause) {
- // All export * declarations are collected in an __export symbol
- declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, SymbolFlags.None);
- }
- else if (isNamespaceExport(node.exportClause)) {
- // declareSymbol walks up parents to find name text, parent _must_ be set
- // but won't be set by the normal binder walk until `bindChildren` later on.
- setParent(node.exportClause, node);
- declareSymbol(container.symbol.exports, container.symbol, node.exportClause, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
- }
+ function bindNamespaceExportDeclaration(node: NamespaceExportDeclaration) {
+ if (node.modifiers && node.modifiers.length) {
+ file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Modifiers_cannot_appear_here));
}
-
- function bindImportClause(node: ImportClause) {
- if (node.name) {
- declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
- }
+ const diag = !isSourceFile(node.parent) ? Diagnostics.Global_module_exports_may_only_appear_at_top_level
+ : !isExternalModule(node.parent) ? Diagnostics.Global_module_exports_may_only_appear_in_module_files
+ : !node.parent.isDeclarationFile ? Diagnostics.Global_module_exports_may_only_appear_in_declaration_files
+ : undefined;
+ if (diag) {
+ file.bindDiagnostics.push(createDiagnosticForNode(node, diag));
+ }
+ else {
+ file.symbol.globalExports = file.symbol.globalExports || createSymbolTable();
+ declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
}
+ }
- function setCommonJsModuleIndicator(node: Node) {
- if (file.externalModuleIndicator) {
- return false;
- }
- if (!file.commonJsModuleIndicator) {
- file.commonJsModuleIndicator = node;
- bindSourceFileAsExternalModule();
- }
- return true;
+ function bindExportDeclaration(node: ExportDeclaration) {
+ if (!container.symbol || !container.symbol.exports) {
+ // Export * in some sort of block construct
+ bindAnonymousDeclaration(node, SymbolFlags.ExportStar, getDeclarationName(node)!);
}
+ else if (!node.exportClause) {
+ // All export * declarations are collected in an __export symbol
+ declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, SymbolFlags.None);
+ }
+ else if (isNamespaceExport(node.exportClause)) {
+ // declareSymbol walks up parents to find name text, parent _must_ be set
+ // but won't be set by the normal binder walk until `bindChildren` later on.
+ setParent(node.exportClause, node);
+ declareSymbol(container.symbol.exports, container.symbol, node.exportClause, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
+ }
+ }
- function bindObjectDefinePropertyExport(node: BindableObjectDefinePropertyCall) {
- if (!setCommonJsModuleIndicator(node)) {
- return;
- }
- const symbol = forEachIdentifierInEntityName(node.arguments[0], /*parent*/ undefined, (id, symbol) => {
- if (symbol) {
- addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment);
- }
- return symbol;
- });
- if (symbol) {
- const flags = SymbolFlags.Property | SymbolFlags.ExportValue;
- declareSymbol(symbol.exports!, symbol, node, flags, SymbolFlags.None);
- }
+ function bindImportClause(node: ImportClause) {
+ if (node.name) {
+ declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
+ }
+ }
+
+ function setCommonJsModuleIndicator(node: Node) {
+ if (file.externalModuleIndicator) {
+ return false;
+ }
+ if (!file.commonJsModuleIndicator) {
+ file.commonJsModuleIndicator = node;
+ bindSourceFileAsExternalModule();
}
+ return true;
+ }
- function bindExportsPropertyAssignment(node: BindableStaticPropertyAssignmentExpression) {
- // When we create a property via 'exports.foo = bar', the 'exports.foo' property access
- // expression is the declaration
- if (!setCommonJsModuleIndicator(node)) {
- return;
- }
- const symbol = forEachIdentifierInEntityName(node.left.expression, /*parent*/ undefined, (id, symbol) => {
- if (symbol) {
- addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment);
- }
- return symbol;
- });
+ function bindObjectDefinePropertyExport(node: BindableObjectDefinePropertyCall) {
+ if (!setCommonJsModuleIndicator(node)) {
+ return;
+ }
+ const symbol = forEachIdentifierInEntityName(node.arguments[0], /*parent*/ undefined, (id, symbol) => {
if (symbol) {
- const isAlias = isAliasableExpression(node.right) && (isExportsIdentifier(node.left.expression) || isModuleExportsAccessExpression(node.left.expression));
- const flags = isAlias ? SymbolFlags.Alias : SymbolFlags.Property | SymbolFlags.ExportValue;
- setParent(node.left, node);
- declareSymbol(symbol.exports!, symbol, node.left, flags, SymbolFlags.None);
+ addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment);
}
+ return symbol;
+ });
+ if (symbol) {
+ const flags = SymbolFlags.Property | SymbolFlags.ExportValue;
+ declareSymbol(symbol.exports!, symbol, node, flags, SymbolFlags.None);
}
+ }
- function bindModuleExportsAssignment(node: BindablePropertyAssignmentExpression) {
- // A common practice in node modules is to set 'export = module.exports = {}', this ensures that 'exports'
- // is still pointing to 'module.exports'.
- // We do not want to consider this as 'export=' since a module can have only one of these.
- // Similarly we do not want to treat 'module.exports = exports' as an 'export='.
- if (!setCommonJsModuleIndicator(node)) {
- return;
- }
- const assignedExpression = getRightMostAssignedExpression(node.right);
- if (isEmptyObjectLiteral(assignedExpression) || container === file && isExportsOrModuleExportsOrAlias(file, assignedExpression)) {
- return;
- }
-
- if (isObjectLiteralExpression(assignedExpression) && every(assignedExpression.properties, isShorthandPropertyAssignment)) {
- forEach(assignedExpression.properties, bindExportAssignedObjectMemberAlias);
- return;
+ function bindExportsPropertyAssignment(node: BindableStaticPropertyAssignmentExpression) {
+ // When we create a property via 'exports.foo = bar', the 'exports.foo' property access
+ // expression is the declaration
+ if (!setCommonJsModuleIndicator(node)) {
+ return;
+ }
+ const symbol = forEachIdentifierInEntityName(node.left.expression, /*parent*/ undefined, (id, symbol) => {
+ if (symbol) {
+ addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment);
}
+ return symbol;
+ });
+ if (symbol) {
+ const isAlias = isAliasableExpression(node.right) && (isExportsIdentifier(node.left.expression) || isModuleExportsAccessExpression(node.left.expression));
+ const flags = isAlias ? SymbolFlags.Alias : SymbolFlags.Property | SymbolFlags.ExportValue;
+ setParent(node.left, node);
+ declareSymbol(symbol.exports!, symbol, node.left, flags, SymbolFlags.None);
+ }
+ }
- // 'module.exports = expr' assignment
- const flags = exportAssignmentIsAlias(node)
- ? SymbolFlags.Alias // An export= with an EntityNameExpression or a ClassExpression exports all meanings of that identifier or class
- : SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule;
- const symbol = declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None);
- setValueDeclaration(symbol, node);
+ function bindModuleExportsAssignment(node: BindablePropertyAssignmentExpression) {
+ // A common practice in node modules is to set 'export = module.exports = {}', this ensures that 'exports'
+ // is still pointing to 'module.exports'.
+ // We do not want to consider this as 'export=' since a module can have only one of these.
+ // Similarly we do not want to treat 'module.exports = exports' as an 'export='.
+ if (!setCommonJsModuleIndicator(node)) {
+ return;
+ }
+ const assignedExpression = getRightMostAssignedExpression(node.right);
+ if (isEmptyObjectLiteral(assignedExpression) || container === file && isExportsOrModuleExportsOrAlias(file, assignedExpression)) {
+ return;
}
- function bindExportAssignedObjectMemberAlias(node: ShorthandPropertyAssignment) {
- declareSymbol(file.symbol.exports!, file.symbol, node, SymbolFlags.Alias | SymbolFlags.Assignment, SymbolFlags.None);
+ if (isObjectLiteralExpression(assignedExpression) && every(assignedExpression.properties, isShorthandPropertyAssignment)) {
+ forEach(assignedExpression.properties, bindExportAssignedObjectMemberAlias);
+ return;
}
- function bindThisPropertyAssignment(node: BindablePropertyAssignmentExpression | PropertyAccessExpression | LiteralLikeElementAccessExpression) {
- Debug.assert(isInJSFile(node));
- // private identifiers *must* be declared (even in JS files)
- const hasPrivateIdentifier = (isBinaryExpression(node) && isPropertyAccessExpression(node.left) && isPrivateIdentifier(node.left.name))
- || (isPropertyAccessExpression(node) && isPrivateIdentifier(node.name));
- if (hasPrivateIdentifier) {
- return;
- }
- const thisContainer = getThisContainer(node, /*includeArrowFunctions*/ false);
- switch (thisContainer.kind) {
- case SyntaxKind.FunctionDeclaration:
- case SyntaxKind.FunctionExpression:
- let constructorSymbol: Symbol | undefined = thisContainer.symbol;
- // For `f.prototype.m = function() { this.x = 0; }`, `this.x = 0` should modify `f`'s members, not the function expression.
- if (isBinaryExpression(thisContainer.parent) && thisContainer.parent.operatorToken.kind === SyntaxKind.EqualsToken) {
- const l = thisContainer.parent.left;
- if (isBindableStaticAccessExpression(l) && isPrototypeAccess(l.expression)) {
- constructorSymbol = lookupSymbolForPropertyAccess(l.expression.expression, thisParentContainer);
- }
- }
+ // 'module.exports = expr' assignment
+ const flags = exportAssignmentIsAlias(node)
+ ? SymbolFlags.Alias // An export= with an EntityNameExpression or a ClassExpression exports all meanings of that identifier or class
+ : SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule;
+ const symbol = declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None);
+ setValueDeclaration(symbol, node);
+ }
- if (constructorSymbol && constructorSymbol.valueDeclaration) {
- // Declare a 'member' if the container is an ES5 class or ES6 constructor
- constructorSymbol.members = constructorSymbol.members || createSymbolTable();
- // It's acceptable for multiple 'this' assignments of the same identifier to occur
- if (hasDynamicName(node)) {
- bindDynamicallyNamedThisPropertyAssignment(node, constructorSymbol, constructorSymbol.members);
- }
- else {
- declareSymbol(constructorSymbol.members, constructorSymbol, node, SymbolFlags.Property | SymbolFlags.Assignment, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
- }
- addDeclarationToSymbol(constructorSymbol, constructorSymbol.valueDeclaration, SymbolFlags.Class);
- }
- break;
+ function bindExportAssignedObjectMemberAlias(node: ShorthandPropertyAssignment) {
+ declareSymbol(file.symbol.exports!, file.symbol, node, SymbolFlags.Alias | SymbolFlags.Assignment, SymbolFlags.None);
+ }
- case SyntaxKind.Constructor:
- case SyntaxKind.PropertyDeclaration:
- case SyntaxKind.MethodDeclaration:
- case SyntaxKind.GetAccessor:
- case SyntaxKind.SetAccessor:
- case SyntaxKind.ClassStaticBlockDeclaration:
- // this.foo assignment in a JavaScript class
- // Bind this property to the containing class
- const containingClass = thisContainer.parent;
- const symbolTable = isStatic(thisContainer) ? containingClass.symbol.exports! : containingClass.symbol.members!;
+ function bindThisPropertyAssignment(node: BindablePropertyAssignmentExpression | PropertyAccessExpression | LiteralLikeElementAccessExpression) {
+ Debug.assert(isInJSFile(node));
+ // private identifiers *must* be declared (even in JS files)
+ const hasPrivateIdentifier = (isBinaryExpression(node) && isPropertyAccessExpression(node.left) && isPrivateIdentifier(node.left.name))
+ || (isPropertyAccessExpression(node) && isPrivateIdentifier(node.name));
+ if (hasPrivateIdentifier) {
+ return;
+ }
+ const thisContainer = getThisContainer(node, /*includeArrowFunctions*/ false);
+ switch (thisContainer.kind) {
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.FunctionExpression:
+ let constructorSymbol: ts.Symbol | undefined = thisContainer.symbol;
+ // For `f.prototype.m = function() { this.x = 0; }`, `this.x = 0` should modify `f`'s members, not the function expression.
+ if (isBinaryExpression(thisContainer.parent) && thisContainer.parent.operatorToken.kind === SyntaxKind.EqualsToken) {
+ const l = thisContainer.parent.left;
+ if (isBindableStaticAccessExpression(l) && isPrototypeAccess(l.expression)) {
+ constructorSymbol = lookupSymbolForPropertyAccess(l.expression.expression, thisParentContainer);
+ }
+ }
+
+ if (constructorSymbol && constructorSymbol.valueDeclaration) {
+ // Declare a 'member' if the container is an ES5 class or ES6 constructor
+ constructorSymbol.members = constructorSymbol.members || createSymbolTable();
+ // It's acceptable for multiple 'this' assignments of the same identifier to occur
if (hasDynamicName(node)) {
- bindDynamicallyNamedThisPropertyAssignment(node, containingClass.symbol, symbolTable);
+ bindDynamicallyNamedThisPropertyAssignment(node, constructorSymbol, constructorSymbol.members);
}
else {
- declareSymbol(symbolTable, containingClass.symbol, node, SymbolFlags.Property | SymbolFlags.Assignment, SymbolFlags.None, /*isReplaceableByMethod*/ true);
- }
- break;
- case SyntaxKind.SourceFile:
- // this.property = assignment in a source file -- declare symbol in exports for a module, in locals for a script
- if (hasDynamicName(node)) {
- break;
- }
- else if ((thisContainer as SourceFile).commonJsModuleIndicator) {
- declareSymbol(thisContainer.symbol.exports!, thisContainer.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None);
- }
- else {
- declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
+ declareSymbol(constructorSymbol.members, constructorSymbol, node, SymbolFlags.Property | SymbolFlags.Assignment, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
}
+ addDeclarationToSymbol(constructorSymbol, constructorSymbol.valueDeclaration, SymbolFlags.Class);
+ }
+ break;
+
+ case SyntaxKind.Constructor:
+ case SyntaxKind.PropertyDeclaration:
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ case SyntaxKind.ClassStaticBlockDeclaration:
+ // this.foo assignment in a JavaScript class
+ // Bind this property to the containing class
+ const containingClass = thisContainer.parent;
+ const symbolTable = isStatic(thisContainer) ? containingClass.symbol.exports! : containingClass.symbol.members!;
+ if (hasDynamicName(node)) {
+ bindDynamicallyNamedThisPropertyAssignment(node, containingClass.symbol, symbolTable);
+ }
+ else {
+ declareSymbol(symbolTable, containingClass.symbol, node, SymbolFlags.Property | SymbolFlags.Assignment, SymbolFlags.None, /*isReplaceableByMethod*/ true);
+ }
+ break;
+ case SyntaxKind.SourceFile:
+ // this.property = assignment in a source file -- declare symbol in exports for a module, in locals for a script
+ if (hasDynamicName(node)) {
break;
+ }
+ else if ((thisContainer as SourceFile).commonJsModuleIndicator) {
+ declareSymbol(thisContainer.symbol.exports!, thisContainer.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None);
+ }
+ else {
+ declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
+ }
+ break;
- default:
- Debug.failBadSyntaxKind(thisContainer);
- }
+ default:
+ Debug.failBadSyntaxKind(thisContainer);
}
+ }
- function bindDynamicallyNamedThisPropertyAssignment(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol, symbolTable: SymbolTable) {
- declareSymbol(symbolTable, symbol, node, SymbolFlags.Property, SymbolFlags.None, /*isReplaceableByMethod*/ true, /*isComputedName*/ true);
- addLateBoundAssignmentDeclarationToSymbol(node, symbol);
- }
+ function bindDynamicallyNamedThisPropertyAssignment(node: BinaryExpression | DynamicNamedDeclaration, symbol: ts.Symbol, symbolTable: SymbolTable) {
+ declareSymbol(symbolTable, symbol, node, SymbolFlags.Property, SymbolFlags.None, /*isReplaceableByMethod*/ true, /*isComputedName*/ true);
+ addLateBoundAssignmentDeclarationToSymbol(node, symbol);
+ }
- function addLateBoundAssignmentDeclarationToSymbol(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol | undefined) {
- if (symbol) {
- (symbol.assignmentDeclarationMembers || (symbol.assignmentDeclarationMembers = new Map())).set(getNodeId(node), node);
- }
+ function addLateBoundAssignmentDeclarationToSymbol(node: BinaryExpression | DynamicNamedDeclaration, symbol: ts.Symbol | undefined) {
+ if (symbol) {
+ (symbol.assignmentDeclarationMembers || (symbol.assignmentDeclarationMembers = new ts.Map())).set(getNodeId(node), node);
}
+ }
- function bindSpecialPropertyDeclaration(node: PropertyAccessExpression | LiteralLikeElementAccessExpression) {
- if (node.expression.kind === SyntaxKind.ThisKeyword) {
- bindThisPropertyAssignment(node);
+ function bindSpecialPropertyDeclaration(node: PropertyAccessExpression | LiteralLikeElementAccessExpression) {
+ if (node.expression.kind === SyntaxKind.ThisKeyword) {
+ bindThisPropertyAssignment(node);
+ }
+ else if (isBindableStaticAccessExpression(node) && node.parent.parent.kind === SyntaxKind.SourceFile) {
+ if (isPrototypeAccess(node.expression)) {
+ bindPrototypePropertyAssignment(node, node.parent);
}
- else if (isBindableStaticAccessExpression(node) && node.parent.parent.kind === SyntaxKind.SourceFile) {
- if (isPrototypeAccess(node.expression)) {
- bindPrototypePropertyAssignment(node, node.parent);
- }
- else {
- bindStaticPropertyAssignment(node);
- }
+ else {
+ bindStaticPropertyAssignment(node);
}
}
+ }
- /** For `x.prototype = { p, ... }`, declare members p,... if `x` is function/class/{}, or not declared. */
- function bindPrototypeAssignment(node: BindableStaticPropertyAssignmentExpression) {
- setParent(node.left, node);
- setParent(node.right, node);
- bindPropertyAssignment(node.left.expression, node.left, /*isPrototypeProperty*/ false, /*containerIsClass*/ true);
- }
+ /** For `x.prototype = { p, ... }`, declare members p,... if `x` is function/class/{}, or not declared. */
+ function bindPrototypeAssignment(node: BindableStaticPropertyAssignmentExpression) {
+ setParent(node.left, node);
+ setParent(node.right, node);
+ bindPropertyAssignment(node.left.expression, node.left, /*isPrototypeProperty*/ false, /*containerIsClass*/ true);
+ }
- function bindObjectDefinePrototypeProperty(node: BindableObjectDefinePropertyCall) {
- const namespaceSymbol = lookupSymbolForPropertyAccess((node.arguments[0] as PropertyAccessExpression).expression as EntityNameExpression);
- if (namespaceSymbol && namespaceSymbol.valueDeclaration) {
- // Ensure the namespace symbol becomes class-like
- addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class);
- }
- bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ true);
+ function bindObjectDefinePrototypeProperty(node: BindableObjectDefinePropertyCall) {
+ const namespaceSymbol = lookupSymbolForPropertyAccess((node.arguments[0] as PropertyAccessExpression).expression as EntityNameExpression);
+ if (namespaceSymbol && namespaceSymbol.valueDeclaration) {
+ // Ensure the namespace symbol becomes class-like
+ addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class);
}
+ bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ true);
+ }
- /**
- * For `x.prototype.y = z`, declare a member `y` on `x` if `x` is a function or class, or not declared.
- * Note that jsdoc preceding an ExpressionStatement like `x.prototype.y;` is also treated as a declaration.
- */
- function bindPrototypePropertyAssignment(lhs: BindableStaticAccessExpression, parent: Node) {
- // Look up the function in the local scope, since prototype assignments should
- // follow the function declaration
- const classPrototype = lhs.expression as BindableStaticAccessExpression;
- const constructorFunction = classPrototype.expression;
+ /**
+ * For `x.prototype.y = z`, declare a member `y` on `x` if `x` is a function or class, or not declared.
+ * Note that jsdoc preceding an ExpressionStatement like `x.prototype.y;` is also treated as a declaration.
+ */
+ function bindPrototypePropertyAssignment(lhs: BindableStaticAccessExpression, parent: Node) {
+ // Look up the function in the local scope, since prototype assignments should
+ // follow the function declaration
+ const classPrototype = lhs.expression as BindableStaticAccessExpression;
+ const constructorFunction = classPrototype.expression;
+
+ // Fix up parent pointers since we're going to use these nodes before we bind into them
+ setParent(constructorFunction, classPrototype);
+ setParent(classPrototype, lhs);
+ setParent(lhs, parent);
+
+ bindPropertyAssignment(constructorFunction, lhs, /*isPrototypeProperty*/ true, /*containerIsClass*/ true);
+ }
- // Fix up parent pointers since we're going to use these nodes before we bind into them
- setParent(constructorFunction, classPrototype);
- setParent(classPrototype, lhs);
- setParent(lhs, parent);
+ function bindObjectDefinePropertyAssignment(node: BindableObjectDefinePropertyCall) {
+ let namespaceSymbol = lookupSymbolForPropertyAccess(node.arguments[0]);
+ const isToplevel = node.parent.parent.kind === SyntaxKind.SourceFile;
+ namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, node.arguments[0], isToplevel, /*isPrototypeProperty*/ false, /*containerIsClass*/ false);
+ bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ false);
+ }
- bindPropertyAssignment(constructorFunction, lhs, /*isPrototypeProperty*/ true, /*containerIsClass*/ true);
+ function bindSpecialPropertyAssignment(node: BindablePropertyAssignmentExpression) {
+ // Class declarations in Typescript do not allow property declarations
+ const parentSymbol = lookupSymbolForPropertyAccess(node.left.expression, container) || lookupSymbolForPropertyAccess(node.left.expression, blockScopeContainer) ;
+ if (!isInJSFile(node) && !isFunctionSymbol(parentSymbol)) {
+ return;
+ }
+ const rootExpr = getLeftmostAccessExpression(node.left);
+ if (isIdentifier(rootExpr) && lookupSymbolForName(container, rootExpr.escapedText)!?.flags & SymbolFlags.Alias) {
+ return;
+ }
+ // Fix up parent pointers since we're going to use these nodes before we bind into them
+ setParent(node.left, node);
+ setParent(node.right, node);
+ if (isIdentifier(node.left.expression) && container === file && isExportsOrModuleExportsOrAlias(file, node.left.expression)) {
+ // This can be an alias for the 'exports' or 'module.exports' names, e.g.
+ // var util = module.exports;
+ // util.property = function ...
+ bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression);
+ }
+ else if (hasDynamicName(node)) {
+ bindAnonymousDeclaration(node, SymbolFlags.Property | SymbolFlags.Assignment, InternalSymbolName.Computed);
+ const sym = bindPotentiallyMissingNamespaces(parentSymbol, node.left.expression, isTopLevelNamespaceAssignment(node.left), /*isPrototype*/ false, /*containerIsClass*/ false);
+ addLateBoundAssignmentDeclarationToSymbol(node, sym);
}
-
- function bindObjectDefinePropertyAssignment(node: BindableObjectDefinePropertyCall) {
- let namespaceSymbol = lookupSymbolForPropertyAccess(node.arguments[0]);
- const isToplevel = node.parent.parent.kind === SyntaxKind.SourceFile;
- namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, node.arguments[0], isToplevel, /*isPrototypeProperty*/ false, /*containerIsClass*/ false);
- bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ false);
+ else {
+ bindStaticPropertyAssignment(cast(node.left, isBindableStaticNameExpression));
}
+ }
- function bindSpecialPropertyAssignment(node: BindablePropertyAssignmentExpression) {
- // Class declarations in Typescript do not allow property declarations
- const parentSymbol = lookupSymbolForPropertyAccess(node.left.expression, container) || lookupSymbolForPropertyAccess(node.left.expression, blockScopeContainer) ;
- if (!isInJSFile(node) && !isFunctionSymbol(parentSymbol)) {
- return;
- }
- const rootExpr = getLeftmostAccessExpression(node.left);
- if (isIdentifier(rootExpr) && lookupSymbolForName(container, rootExpr.escapedText)!?.flags & SymbolFlags.Alias) {
- return;
- }
- // Fix up parent pointers since we're going to use these nodes before we bind into them
- setParent(node.left, node);
- setParent(node.right, node);
- if (isIdentifier(node.left.expression) && container === file && isExportsOrModuleExportsOrAlias(file, node.left.expression)) {
- // This can be an alias for the 'exports' or 'module.exports' names, e.g.
- // var util = module.exports;
- // util.property = function ...
- bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression);
- }
- else if (hasDynamicName(node)) {
- bindAnonymousDeclaration(node, SymbolFlags.Property | SymbolFlags.Assignment, InternalSymbolName.Computed);
- const sym = bindPotentiallyMissingNamespaces(parentSymbol, node.left.expression, isTopLevelNamespaceAssignment(node.left), /*isPrototype*/ false, /*containerIsClass*/ false);
- addLateBoundAssignmentDeclarationToSymbol(node, sym);
- }
- else {
- bindStaticPropertyAssignment(cast(node.left, isBindableStaticNameExpression));
- }
+ /**
+ * For nodes like `x.y = z`, declare a member 'y' on 'x' if x is a function (or IIFE) or class or {}, or not declared.
+ * Also works for expression statements preceded by JSDoc, like / ** @type number * / x.y;
+ */
+ function bindStaticPropertyAssignment(node: BindableStaticNameExpression) {
+ Debug.assert(!isIdentifier(node));
+ setParent(node.expression, node);
+ bindPropertyAssignment(node.expression, node, /*isPrototypeProperty*/ false, /*containerIsClass*/ false);
+ }
+
+ function bindPotentiallyMissingNamespaces(namespaceSymbol: ts.Symbol | undefined, entityName: BindableStaticNameExpression, isToplevel: boolean, isPrototypeProperty: boolean, containerIsClass: boolean) {
+ if (namespaceSymbol?.flags! & SymbolFlags.Alias) {
+ return namespaceSymbol;
+ }
+ if (isToplevel && !isPrototypeProperty) {
+ // make symbols or add declarations for intermediate containers
+ const flags = SymbolFlags.Module | SymbolFlags.Assignment;
+ const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.Assignment;
+ namespaceSymbol = forEachIdentifierInEntityName(entityName, namespaceSymbol, (id, symbol, parent) => {
+ if (symbol) {
+ addDeclarationToSymbol(symbol, id, flags);
+ return symbol;
+ }
+ else {
+ const table = parent ? parent.exports! :
+ file.jsGlobalAugmentations || (file.jsGlobalAugmentations = createSymbolTable());
+ return declareSymbol(table, parent, id, flags, excludeFlags);
+ }
+ });
}
+ if (containerIsClass && namespaceSymbol && namespaceSymbol.valueDeclaration) {
+ addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class);
+ }
+ return namespaceSymbol;
+ }
- /**
- * For nodes like `x.y = z`, declare a member 'y' on 'x' if x is a function (or IIFE) or class or {}, or not declared.
- * Also works for expression statements preceded by JSDoc, like / ** @type number * / x.y;
- */
- function bindStaticPropertyAssignment(node: BindableStaticNameExpression) {
- Debug.assert(!isIdentifier(node));
- setParent(node.expression, node);
- bindPropertyAssignment(node.expression, node, /*isPrototypeProperty*/ false, /*containerIsClass*/ false);
+ function bindPotentiallyNewExpandoMemberToNamespace(declaration: BindableStaticAccessExpression | CallExpression, namespaceSymbol: ts.Symbol | undefined, isPrototypeProperty: boolean) {
+ if (!namespaceSymbol || !isExpandoSymbol(namespaceSymbol)) {
+ return;
}
- function bindPotentiallyMissingNamespaces(namespaceSymbol: Symbol | undefined, entityName: BindableStaticNameExpression, isToplevel: boolean, isPrototypeProperty: boolean, containerIsClass: boolean) {
- if (namespaceSymbol?.flags! & SymbolFlags.Alias) {
- return namespaceSymbol;
- }
- if (isToplevel && !isPrototypeProperty) {
- // make symbols or add declarations for intermediate containers
- const flags = SymbolFlags.Module | SymbolFlags.Assignment;
- const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.Assignment;
- namespaceSymbol = forEachIdentifierInEntityName(entityName, namespaceSymbol, (id, symbol, parent) => {
- if (symbol) {
- addDeclarationToSymbol(symbol, id, flags);
- return symbol;
- }
- else {
- const table = parent ? parent.exports! :
- file.jsGlobalAugmentations || (file.jsGlobalAugmentations = createSymbolTable());
- return declareSymbol(table, parent, id, flags, excludeFlags);
- }
- });
+ // Set up the members collection if it doesn't exist already
+ const symbolTable = isPrototypeProperty ?
+ (namespaceSymbol.members || (namespaceSymbol.members = createSymbolTable())) :
+ (namespaceSymbol.exports || (namespaceSymbol.exports = createSymbolTable()));
+
+ let includes = SymbolFlags.None;
+ let excludes = SymbolFlags.None;
+ // Method-like
+ if (isFunctionLikeDeclaration(getAssignedExpandoInitializer(declaration)!)) {
+ includes = SymbolFlags.Method;
+ excludes = SymbolFlags.MethodExcludes;
+ }
+ // Maybe accessor-like
+ else if (isCallExpression(declaration) && isBindableObjectDefinePropertyCall(declaration)) {
+ if (some(declaration.arguments[2].properties, p => {
+ const id = getNameOfDeclaration(p);
+ return !!id && isIdentifier(id) && idText(id) === "set";
+ })) {
+ // We mix in `SymbolFLags.Property` so in the checker `getTypeOfVariableParameterOrProperty` is used for this
+ // symbol, instead of `getTypeOfAccessor` (which will assert as there is no real accessor declaration)
+ includes |= SymbolFlags.SetAccessor | SymbolFlags.Property;
+ excludes |= SymbolFlags.SetAccessorExcludes;
}
- if (containerIsClass && namespaceSymbol && namespaceSymbol.valueDeclaration) {
- addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class);
+ if (some(declaration.arguments[2].properties, p => {
+ const id = getNameOfDeclaration(p);
+ return !!id && isIdentifier(id) && idText(id) === "get";
+ })) {
+ includes |= SymbolFlags.GetAccessor | SymbolFlags.Property;
+ excludes |= SymbolFlags.GetAccessorExcludes;
}
- return namespaceSymbol;
}
- function bindPotentiallyNewExpandoMemberToNamespace(declaration: BindableStaticAccessExpression | CallExpression, namespaceSymbol: Symbol | undefined, isPrototypeProperty: boolean) {
- if (!namespaceSymbol || !isExpandoSymbol(namespaceSymbol)) {
- return;
- }
+ if (includes === SymbolFlags.None) {
+ includes = SymbolFlags.Property;
+ excludes = SymbolFlags.PropertyExcludes;
+ }
- // Set up the members collection if it doesn't exist already
- const symbolTable = isPrototypeProperty ?
- (namespaceSymbol.members || (namespaceSymbol.members = createSymbolTable())) :
- (namespaceSymbol.exports || (namespaceSymbol.exports = createSymbolTable()));
-
- let includes = SymbolFlags.None;
- let excludes = SymbolFlags.None;
- // Method-like
- if (isFunctionLikeDeclaration(getAssignedExpandoInitializer(declaration)!)) {
- includes = SymbolFlags.Method;
- excludes = SymbolFlags.MethodExcludes;
- }
- // Maybe accessor-like
- else if (isCallExpression(declaration) && isBindableObjectDefinePropertyCall(declaration)) {
- if (some(declaration.arguments[2].properties, p => {
- const id = getNameOfDeclaration(p);
- return !!id && isIdentifier(id) && idText(id) === "set";
- })) {
- // We mix in `SymbolFLags.Property` so in the checker `getTypeOfVariableParameterOrProperty` is used for this
- // symbol, instead of `getTypeOfAccessor` (which will assert as there is no real accessor declaration)
- includes |= SymbolFlags.SetAccessor | SymbolFlags.Property;
- excludes |= SymbolFlags.SetAccessorExcludes;
- }
- if (some(declaration.arguments[2].properties, p => {
- const id = getNameOfDeclaration(p);
- return !!id && isIdentifier(id) && idText(id) === "get";
- })) {
- includes |= SymbolFlags.GetAccessor | SymbolFlags.Property;
- excludes |= SymbolFlags.GetAccessorExcludes;
- }
- }
-
- if (includes === SymbolFlags.None) {
- includes = SymbolFlags.Property;
- excludes = SymbolFlags.PropertyExcludes;
- }
-
- declareSymbol(symbolTable, namespaceSymbol, declaration, includes | SymbolFlags.Assignment, excludes & ~SymbolFlags.Assignment);
- }
-
- function isTopLevelNamespaceAssignment(propertyAccess: BindableAccessExpression) {
- return isBinaryExpression(propertyAccess.parent)
- ? getParentOfBinaryExpression(propertyAccess.parent).parent.kind === SyntaxKind.SourceFile
- : propertyAccess.parent.parent.kind === SyntaxKind.SourceFile;
- }
-
- function bindPropertyAssignment(name: BindableStaticNameExpression, propertyAccess: BindableStaticAccessExpression, isPrototypeProperty: boolean, containerIsClass: boolean) {
- let namespaceSymbol = lookupSymbolForPropertyAccess(name, container) || lookupSymbolForPropertyAccess(name, blockScopeContainer);
- const isToplevel = isTopLevelNamespaceAssignment(propertyAccess);
- namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, propertyAccess.expression, isToplevel, isPrototypeProperty, containerIsClass);
- bindPotentiallyNewExpandoMemberToNamespace(propertyAccess, namespaceSymbol, isPrototypeProperty);
- }
-
- /**
- * Javascript expando values are:
- * - Functions
- * - classes
- * - namespaces
- * - variables initialized with function expressions
- * - with class expressions
- * - with empty object literals
- * - with non-empty object literals if assigned to the prototype property
- */
- function isExpandoSymbol(symbol: Symbol): boolean {
- if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.NamespaceModule)) {
- return true;
- }
- const node = symbol.valueDeclaration;
- if (node && isCallExpression(node)) {
- return !!getAssignedExpandoInitializer(node);
- }
- let init = !node ? undefined :
- isVariableDeclaration(node) ? node.initializer :
- isBinaryExpression(node) ? node.right :
- isPropertyAccessExpression(node) && isBinaryExpression(node.parent) ? node.parent.right :
- undefined;
- init = init && getRightMostAssignedExpression(init);
- if (init) {
- const isPrototypeAssignment = isPrototypeAccess(isVariableDeclaration(node!) ? node.name : isBinaryExpression(node!) ? node.left : node!);
- return !!getExpandoInitializer(isBinaryExpression(init) && (init.operatorToken.kind === SyntaxKind.BarBarToken || init.operatorToken.kind === SyntaxKind.QuestionQuestionToken) ? init.right : init, isPrototypeAssignment);
- }
- return false;
+ declareSymbol(symbolTable, namespaceSymbol, declaration, includes | SymbolFlags.Assignment, excludes & ~SymbolFlags.Assignment);
+ }
+
+ function isTopLevelNamespaceAssignment(propertyAccess: BindableAccessExpression) {
+ return isBinaryExpression(propertyAccess.parent)
+ ? getParentOfBinaryExpression(propertyAccess.parent).parent.kind === SyntaxKind.SourceFile
+ : propertyAccess.parent.parent.kind === SyntaxKind.SourceFile;
+ }
+
+ function bindPropertyAssignment(name: BindableStaticNameExpression, propertyAccess: BindableStaticAccessExpression, isPrototypeProperty: boolean, containerIsClass: boolean) {
+ let namespaceSymbol = lookupSymbolForPropertyAccess(name, container) || lookupSymbolForPropertyAccess(name, blockScopeContainer);
+ const isToplevel = isTopLevelNamespaceAssignment(propertyAccess);
+ namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, propertyAccess.expression, isToplevel, isPrototypeProperty, containerIsClass);
+ bindPotentiallyNewExpandoMemberToNamespace(propertyAccess, namespaceSymbol, isPrototypeProperty);
+ }
+
+ /**
+ * Javascript expando values are:
+ * - Functions
+ * - classes
+ * - namespaces
+ * - variables initialized with function expressions
+ * - with class expressions
+ * - with empty object literals
+ * - with non-empty object literals if assigned to the prototype property
+ */
+ function isExpandoSymbol(symbol: ts.Symbol): boolean {
+ if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.NamespaceModule)) {
+ return true;
+ }
+ const node = symbol.valueDeclaration;
+ if (node && isCallExpression(node)) {
+ return !!getAssignedExpandoInitializer(node);
}
+ let init = !node ? undefined :
+ isVariableDeclaration(node) ? node.initializer :
+ isBinaryExpression(node) ? node.right :
+ isPropertyAccessExpression(node) && isBinaryExpression(node.parent) ? node.parent.right :
+ undefined;
+ init = init && getRightMostAssignedExpression(init);
+ if (init) {
+ const isPrototypeAssignment = isPrototypeAccess(isVariableDeclaration(node!) ? node.name : isBinaryExpression(node!) ? node.left : node!);
+ return !!getExpandoInitializer(isBinaryExpression(init) && (init.operatorToken.kind === SyntaxKind.BarBarToken || init.operatorToken.kind === SyntaxKind.QuestionQuestionToken) ? init.right : init, isPrototypeAssignment);
+ }
+ return false;
+ }
- function getParentOfBinaryExpression(expr: Node) {
- while (isBinaryExpression(expr.parent)) {
- expr = expr.parent;
- }
- return expr.parent;
+ function getParentOfBinaryExpression(expr: Node) {
+ while (isBinaryExpression(expr.parent)) {
+ expr = expr.parent;
}
+ return expr.parent;
+ }
- function lookupSymbolForPropertyAccess(node: BindableStaticNameExpression, lookupContainer: Node = container): Symbol | undefined {
- if (isIdentifier(node)) {
- return lookupSymbolForName(lookupContainer, node.escapedText);
- }
- else {
- const symbol = lookupSymbolForPropertyAccess(node.expression);
- return symbol && symbol.exports && symbol.exports.get(getElementOrPropertyAccessName(node));
- }
+ function lookupSymbolForPropertyAccess(node: BindableStaticNameExpression, lookupContainer: Node = container): ts.Symbol | undefined {
+ if (isIdentifier(node)) {
+ return lookupSymbolForName(lookupContainer, node.escapedText);
}
+ else {
+ const symbol = lookupSymbolForPropertyAccess(node.expression);
+ return symbol && symbol.exports && symbol.exports.get(getElementOrPropertyAccessName(node));
+ }
+ }
- function forEachIdentifierInEntityName(e: BindableStaticNameExpression, parent: Symbol | undefined, action: (e: Declaration, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
- if (isExportsOrModuleExportsOrAlias(file, e)) {
- return file.symbol;
- }
- else if (isIdentifier(e)) {
- return action(e, lookupSymbolForPropertyAccess(e), parent);
- }
- else {
- const s = forEachIdentifierInEntityName(e.expression, parent, action);
- const name = getNameOrArgument(e);
- // unreachable
- if (isPrivateIdentifier(name)) {
- Debug.fail("unexpected PrivateIdentifier");
- }
- return action(name, s && s.exports && s.exports.get(getElementOrPropertyAccessName(e)), s);
+ function forEachIdentifierInEntityName(e: BindableStaticNameExpression, parent: ts.Symbol | undefined, action: (e: Declaration, symbol: ts.Symbol | undefined, parent: ts.Symbol | undefined) => ts.Symbol | undefined): ts.Symbol | undefined {
+ if (isExportsOrModuleExportsOrAlias(file, e)) {
+ return file.symbol;
+ }
+ else if (isIdentifier(e)) {
+ return action(e, lookupSymbolForPropertyAccess(e), parent);
+ }
+ else {
+ const s = forEachIdentifierInEntityName(e.expression, parent, action);
+ const name = getNameOrArgument(e);
+ // unreachable
+ if (isPrivateIdentifier(name)) {
+ Debug.fail("unexpected PrivateIdentifier");
}
+ return action(name, s && s.exports && s.exports.get(getElementOrPropertyAccessName(e)), s);
}
+ }
- function bindCallExpression(node: CallExpression) {
- // We're only inspecting call expressions to detect CommonJS modules, so we can skip
- // this check if we've already seen the module indicator
- if (!file.commonJsModuleIndicator && isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ false)) {
- setCommonJsModuleIndicator(node);
- }
+ function bindCallExpression(node: CallExpression) {
+ // We're only inspecting call expressions to detect CommonJS modules, so we can skip
+ // this check if we've already seen the module indicator
+ if (!file.commonJsModuleIndicator && isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ false)) {
+ setCommonJsModuleIndicator(node);
}
+ }
- function bindClassLikeDeclaration(node: ClassLikeDeclaration) {
- if (node.kind === SyntaxKind.ClassDeclaration) {
- bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes);
- }
- else {
- const bindingName = node.name ? node.name.escapedText : InternalSymbolName.Class;
- bindAnonymousDeclaration(node, SymbolFlags.Class, bindingName);
- // Add name of class expression into the map for semantic classifier
- if (node.name) {
- classifiableNames.add(node.name.escapedText);
- }
+ function bindClassLikeDeclaration(node: ClassLikeDeclaration) {
+ if (node.kind === SyntaxKind.ClassDeclaration) {
+ bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes);
+ }
+ else {
+ const bindingName = node.name ? node.name.escapedText : InternalSymbolName.Class;
+ bindAnonymousDeclaration(node, SymbolFlags.Class, bindingName);
+ // Add name of class expression into the map for semantic classifier
+ if (node.name) {
+ classifiableNames.add(node.name.escapedText);
}
+ }
- const { symbol } = node;
+ const { symbol } = node;
- // TypeScript 1.0 spec (April 2014): 8.4
- // Every class automatically contains a static property member named 'prototype', the
- // type of which is an instantiation of the class type with type Any supplied as a type
- // argument for each type parameter. It is an error to explicitly declare a static
- // property member with the name 'prototype'.
- //
- // Note: we check for this here because this class may be merging into a module. The
- // module might have an exported variable called 'prototype'. We can't allow that as
- // that would clash with the built-in 'prototype' for the class.
- const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype" as __String);
- const symbolExport = symbol.exports!.get(prototypeSymbol.escapedName);
- if (symbolExport) {
- if (node.name) {
- setParent(node.name, node);
- }
- file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations![0], Diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol)));
+ // TypeScript 1.0 spec (April 2014): 8.4
+ // Every class automatically contains a static property member named 'prototype', the
+ // type of which is an instantiation of the class type with type Any supplied as a type
+ // argument for each type parameter. It is an error to explicitly declare a static
+ // property member with the name 'prototype'.
+ //
+ // Note: we check for this here because this class may be merging into a module. The
+ // module might have an exported variable called 'prototype'. We can't allow that as
+ // that would clash with the built-in 'prototype' for the class.
+ const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype" as __String);
+ const symbolExport = symbol.exports!.get(prototypeSymbol.escapedName);
+ if (symbolExport) {
+ if (node.name) {
+ setParent(node.name, node);
}
- symbol.exports!.set(prototypeSymbol.escapedName, prototypeSymbol);
- prototypeSymbol.parent = symbol;
- }
-
- function bindEnumDeclaration(node: EnumDeclaration) {
- return isEnumConst(node)
- ? bindBlockScopedDeclaration(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes)
- : bindBlockScopedDeclaration(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes);
+ file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations![0], Diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol)));
}
+ symbol.exports!.set(prototypeSymbol.escapedName, prototypeSymbol);
+ prototypeSymbol.parent = symbol;
+ }
- function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) {
- if (inStrictMode) {
- checkStrictModeEvalOrArguments(node, node.name);
- }
+ function bindEnumDeclaration(node: EnumDeclaration) {
+ return isEnumConst(node)
+ ? bindBlockScopedDeclaration(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes)
+ : bindBlockScopedDeclaration(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes);
+ }
- if (!isBindingPattern(node.name)) {
- if (isInJSFile(node) && isRequireVariableDeclaration(node) && !getJSDocTypeTag(node)) {
- declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
- }
- else if (isBlockOrCatchScoped(node)) {
- bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes);
- }
- else if (isParameterDeclaration(node)) {
- // It is safe to walk up parent chain to find whether the node is a destructuring parameter declaration
- // because its parent chain has already been set up, since parents are set before descending into children.
- //
- // If node is a binding element in parameter declaration, we need to use ParameterExcludes.
- // Using ParameterExcludes flag allows the compiler to report an error on duplicate identifiers in Parameter Declaration
- // For example:
- // function foo([a,a]) {} // Duplicate Identifier error
- // function bar(a,a) {} // Duplicate Identifier error, parameter declaration in this case is handled in bindParameter
- // // which correctly set excluded symbols
- declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes);
- }
- else {
- declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
- }
- }
+ function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) {
+ if (inStrictMode) {
+ checkStrictModeEvalOrArguments(node, node.name);
}
- function bindParameter(node: ParameterDeclaration | JSDocParameterTag) {
- if (node.kind === SyntaxKind.JSDocParameterTag && container.kind !== SyntaxKind.JSDocSignature) {
- return;
- }
- if (inStrictMode && !(node.flags & NodeFlags.Ambient)) {
- // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
- // strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
- checkStrictModeEvalOrArguments(node, node.name);
+ if (!isBindingPattern(node.name)) {
+ if (isInJSFile(node) && isRequireVariableDeclaration(node) && !getJSDocTypeTag(node)) {
+ declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
}
-
- if (isBindingPattern(node.name)) {
- bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, "__" + (node as ParameterDeclaration).parent.parameters.indexOf(node as ParameterDeclaration) as __String);
+ else if (isBlockOrCatchScoped(node)) {
+ bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes);
}
- else {
+ else if (isParameterDeclaration(node)) {
+ // It is safe to walk up parent chain to find whether the node is a destructuring parameter declaration
+ // because its parent chain has already been set up, since parents are set before descending into children.
+ //
+ // If node is a binding element in parameter declaration, we need to use ParameterExcludes.
+ // Using ParameterExcludes flag allows the compiler to report an error on duplicate identifiers in Parameter Declaration
+ // For example:
+ // function foo([a,a]) {} // Duplicate Identifier error
+ // function bar(a,a) {} // Duplicate Identifier error, parameter declaration in this case is handled in bindParameter
+ // // which correctly set excluded symbols
declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes);
}
-
- // If this is a property-parameter, then also declare the property symbol into the
- // containing class.
- if (isParameterPropertyDeclaration(node, node.parent)) {
- const classDeclaration = node.parent.parent;
- declareSymbol(classDeclaration.symbol.members!, classDeclaration.symbol, node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
+ else {
+ declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
}
}
+ }
- function bindFunctionDeclaration(node: FunctionDeclaration) {
- if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient)) {
- if (isAsyncFunction(node)) {
- emitFlags |= NodeFlags.HasAsyncFunctions;
- }
- }
+ function bindParameter(node: ParameterDeclaration | JSDocParameterTag) {
+ if (node.kind === SyntaxKind.JSDocParameterTag && container.kind !== SyntaxKind.JSDocSignature) {
+ return;
+ }
+ if (inStrictMode && !(node.flags & NodeFlags.Ambient)) {
+ // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
+ // strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
+ checkStrictModeEvalOrArguments(node, node.name);
+ }
- checkStrictModeFunctionName(node);
- if (inStrictMode) {
- checkStrictModeFunctionDeclaration(node);
- bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);
- }
- else {
- declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);
- }
+ if (isBindingPattern(node.name)) {
+ bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, "__" + (node as ParameterDeclaration).parent.parameters.indexOf(node as ParameterDeclaration) as __String);
+ }
+ else {
+ declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes);
}
- function bindFunctionExpression(node: FunctionExpression) {
- if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient)) {
- if (isAsyncFunction(node)) {
- emitFlags |= NodeFlags.HasAsyncFunctions;
- }
- }
- if (currentFlow) {
- node.flowNode = currentFlow;
- }
- checkStrictModeFunctionName(node);
- const bindingName = node.name ? node.name.escapedText : InternalSymbolName.Function;
- return bindAnonymousDeclaration(node, SymbolFlags.Function, bindingName);
+ // If this is a property-parameter, then also declare the property symbol into the
+ // containing class.
+ if (isParameterPropertyDeclaration(node, node.parent)) {
+ const classDeclaration = node.parent.parent;
+ declareSymbol(classDeclaration.symbol.members!, classDeclaration.symbol, node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
}
+ }
- function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
- if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient) && isAsyncFunction(node)) {
+ function bindFunctionDeclaration(node: FunctionDeclaration) {
+ if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient)) {
+ if (isAsyncFunction(node)) {
emitFlags |= NodeFlags.HasAsyncFunctions;
}
+ }
- if (currentFlow && isObjectLiteralOrClassExpressionMethodOrAccessor(node)) {
- node.flowNode = currentFlow;
+ checkStrictModeFunctionName(node);
+ if (inStrictMode) {
+ checkStrictModeFunctionDeclaration(node);
+ bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);
+ }
+ else {
+ declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);
+ }
+ }
+
+ function bindFunctionExpression(node: FunctionExpression) {
+ if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient)) {
+ if (isAsyncFunction(node)) {
+ emitFlags |= NodeFlags.HasAsyncFunctions;
}
+ }
+ if (currentFlow) {
+ node.flowNode = currentFlow;
+ }
+ checkStrictModeFunctionName(node);
+ const bindingName = node.name ? node.name.escapedText : InternalSymbolName.Function;
+ return bindAnonymousDeclaration(node, SymbolFlags.Function, bindingName);
+ }
- return hasDynamicName(node)
- ? bindAnonymousDeclaration(node, symbolFlags, InternalSymbolName.Computed)
- : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
+ function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
+ if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient) && isAsyncFunction(node)) {
+ emitFlags |= NodeFlags.HasAsyncFunctions;
}
- function getInferTypeContainer(node: Node): ConditionalTypeNode | undefined {
- const extendsType = findAncestor(node, n => n.parent && isConditionalTypeNode(n.parent) && n.parent.extendsType === n);
- return extendsType && extendsType.parent as ConditionalTypeNode;
+ if (currentFlow && isObjectLiteralOrClassExpressionMethodOrAccessor(node)) {
+ node.flowNode = currentFlow;
}
- function bindTypeParameter(node: TypeParameterDeclaration) {
- if (isJSDocTemplateTag(node.parent)) {
- const container = getEffectiveContainerForJSDocTemplateTag(node.parent);
- if (container) {
- if (!container.locals) {
- container.locals = createSymbolTable();
- }
- declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
- }
- else {
- declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
- }
- }
- else if (node.parent.kind === SyntaxKind.InferType) {
- const container = getInferTypeContainer(node.parent);
- if (container) {
- if (!container.locals) {
- container.locals = createSymbolTable();
- }
- declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
- }
- else {
- bindAnonymousDeclaration(node, SymbolFlags.TypeParameter, getDeclarationName(node)!); // TODO: GH#18217
+ return hasDynamicName(node)
+ ? bindAnonymousDeclaration(node, symbolFlags, InternalSymbolName.Computed)
+ : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
+ }
+
+ function getInferTypeContainer(node: Node): ConditionalTypeNode | undefined {
+ const extendsType = findAncestor(node, n => n.parent && isConditionalTypeNode(n.parent) && n.parent.extendsType === n);
+ return extendsType && extendsType.parent as ConditionalTypeNode;
+ }
+
+ function bindTypeParameter(node: TypeParameterDeclaration) {
+ if (isJSDocTemplateTag(node.parent)) {
+ const container = getEffectiveContainerForJSDocTemplateTag(node.parent);
+ if (container) {
+ if (!container.locals) {
+ container.locals = createSymbolTable();
}
+ declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
}
else {
declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
}
}
-
- // reachability checks
-
- function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean {
- const instanceState = getModuleInstanceState(node);
- return instanceState === ModuleInstanceState.Instantiated || (instanceState === ModuleInstanceState.ConstEnumOnly && shouldPreserveConstEnums(options));
- }
-
- function checkUnreachable(node: Node): boolean {
- if (!(currentFlow.flags & FlowFlags.Unreachable)) {
- return false;
- }
- if (currentFlow === unreachableFlow) {
- const reportError =
- // report error on all statements except empty ones
- (isStatementButNotDeclaration(node) && node.kind !== SyntaxKind.EmptyStatement) ||
- // report error on class declarations
- node.kind === SyntaxKind.ClassDeclaration ||
- // report error on instantiated modules or const-enums only modules if preserveConstEnums is set
- (node.kind === SyntaxKind.ModuleDeclaration && shouldReportErrorOnModuleDeclaration(node as ModuleDeclaration));
-
- if (reportError) {
- currentFlow = reportedUnreachableFlow;
-
- if (!options.allowUnreachableCode) {
- // unreachable code is reported if
- // - user has explicitly asked about it AND
- // - statement is in not ambient context (statements in ambient context is already an error
- // so we should not report extras) AND
- // - node is not variable statement OR
- // - node is block scoped variable statement OR
- // - node is not block scoped variable statement and at least one variable declaration has initializer
- // Rationale: we don't want to report errors on non-initialized var's since they are hoisted
- // On the other side we do want to report errors on non-initialized 'lets' because of TDZ
- const isError =
- unreachableCodeIsError(options) &&
- !(node.flags & NodeFlags.Ambient) &&
- (
- !isVariableStatement(node) ||
- !!(getCombinedNodeFlags(node.declarationList) & NodeFlags.BlockScoped) ||
- node.declarationList.declarations.some(d => !!d.initializer)
- );
-
- eachUnreachableRange(node, (start, end) => errorOrSuggestionOnRange(isError, start, end, Diagnostics.Unreachable_code_detected));
- }
+ else if (node.parent.kind === SyntaxKind.InferType) {
+ const container = getInferTypeContainer(node.parent);
+ if (container) {
+ if (!container.locals) {
+ container.locals = createSymbolTable();
}
+ declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
+ }
+ else {
+ bindAnonymousDeclaration(node, SymbolFlags.TypeParameter, getDeclarationName(node)!); // TODO: GH#18217
}
- return true;
+ }
+ else {
+ declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
}
}
- function eachUnreachableRange(node: Node, cb: (start: Node, last: Node) => void): void {
- if (isStatement(node) && isExecutableStatement(node) && isBlock(node.parent)) {
- const { statements } = node.parent;
- const slice = sliceAfter(statements, node);
- getRangesWhere(slice, isExecutableStatement, (start, afterEnd) => cb(slice[start], slice[afterEnd - 1]));
- }
- else {
- cb(node, node);
+ // reachability checks
+
+ function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean {
+ const instanceState = getModuleInstanceState(node);
+ return instanceState === ModuleInstanceState.Instantiated || (instanceState === ModuleInstanceState.ConstEnumOnly && shouldPreserveConstEnums(options));
+ }
+
+ function checkUnreachable(node: Node): boolean {
+ if (!(currentFlow.flags & FlowFlags.Unreachable)) {
+ return false;
}
+ if (currentFlow === unreachableFlow) {
+ const reportError =
+ // report error on all statements except empty ones
+ (isStatementButNotDeclaration(node) && node.kind !== SyntaxKind.EmptyStatement) ||
+ // report error on class declarations
+ node.kind === SyntaxKind.ClassDeclaration ||
+ // report error on instantiated modules or const-enums only modules if preserveConstEnums is set
+ (node.kind === SyntaxKind.ModuleDeclaration && shouldReportErrorOnModuleDeclaration(node as ModuleDeclaration));
+
+ if (reportError) {
+ currentFlow = reportedUnreachableFlow;
+
+ if (!options.allowUnreachableCode) {
+ // unreachable code is reported if
+ // - user has explicitly asked about it AND
+ // - statement is in not ambient context (statements in ambient context is already an error
+ // so we should not report extras) AND
+ // - node is not variable statement OR
+ // - node is block scoped variable statement OR
+ // - node is not block scoped variable statement and at least one variable declaration has initializer
+ // Rationale: we don't want to report errors on non-initialized var's since they are hoisted
+ // On the other side we do want to report errors on non-initialized 'lets' because of TDZ
+ const isError = unreachableCodeIsError(options) &&
+ !(node.flags & NodeFlags.Ambient) &&
+ (!isVariableStatement(node) ||
+ !!(getCombinedNodeFlags(node.declarationList) & NodeFlags.BlockScoped) ||
+ node.declarationList.declarations.some(d => !!d.initializer));
+
+ eachUnreachableRange(node, (start, end) => errorOrSuggestionOnRange(isError, start, end, Diagnostics.Unreachable_code_detected));
+ }
+ }
+ }
+ return true;
+ }
+}
+
+/* @internal */
+function eachUnreachableRange(node: Node, cb: (start: Node, last: Node) => void): void {
+ if (isStatement(node) && isExecutableStatement(node) && isBlock(node.parent)) {
+ const { statements } = node.parent;
+ const slice = sliceAfter(statements, node);
+ getRangesWhere(slice, isExecutableStatement, (start, afterEnd) => cb(slice[start], slice[afterEnd - 1]));
}
- // As opposed to a pure declaration like an `interface`
- function isExecutableStatement(s: Statement): boolean {
- // Don't remove statements that can validly be used before they appear.
- return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) && !isEnumDeclaration(s) &&
- // `var x;` may declare a variable used above
- !(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.Let | NodeFlags.Const)) && s.declarationList.declarations.some(d => !d.initializer));
+ else {
+ cb(node, node);
}
+}
+// As opposed to a pure declaration like an `interface`
+/* @internal */
+function isExecutableStatement(s: Statement): boolean {
+ // Don't remove statements that can validly be used before they appear.
+ return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) && !isEnumDeclaration(s) &&
+ // `var x;` may declare a variable used above
+ !(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.Let | NodeFlags.Const)) && s.declarationList.declarations.some(d => !d.initializer));
+}
- function isPurelyTypeDeclaration(s: Statement): boolean {
- switch (s.kind) {
- case SyntaxKind.InterfaceDeclaration:
- case SyntaxKind.TypeAliasDeclaration:
- return true;
- case SyntaxKind.ModuleDeclaration:
- return getModuleInstanceState(s as ModuleDeclaration) !== ModuleInstanceState.Instantiated;
- case SyntaxKind.EnumDeclaration:
- return hasSyntacticModifier(s, ModifierFlags.Const);
- default:
- return false;
- }
- }
-
- export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean {
- let i = 0;
- const q = [node];
- while (q.length && i < 100) {
- i++;
- node = q.shift()!;
- if (isExportsIdentifier(node) || isModuleExportsAccessExpression(node)) {
- return true;
- }
- else if (isIdentifier(node)) {
- const symbol = lookupSymbolForName(sourceFile, node.escapedText);
- if (!!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && !!symbol.valueDeclaration.initializer) {
- const init = symbol.valueDeclaration.initializer;
- q.push(init);
- if (isAssignmentExpression(init, /*excludeCompoundAssignment*/ true)) {
- q.push(init.left);
- q.push(init.right);
- }
+/* @internal */
+function isPurelyTypeDeclaration(s: Statement): boolean {
+ switch (s.kind) {
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.TypeAliasDeclaration:
+ return true;
+ case SyntaxKind.ModuleDeclaration:
+ return getModuleInstanceState(s as ModuleDeclaration) !== ModuleInstanceState.Instantiated;
+ case SyntaxKind.EnumDeclaration:
+ return hasSyntacticModifier(s, ModifierFlags.Const);
+ default:
+ return false;
+ }
+}
+
+/* @internal */
+export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean {
+ let i = 0;
+ const q = [node];
+ while (q.length && i < 100) {
+ i++;
+ node = q.shift()!;
+ if (isExportsIdentifier(node) || isModuleExportsAccessExpression(node)) {
+ return true;
+ }
+ else if (isIdentifier(node)) {
+ const symbol = lookupSymbolForName(sourceFile, node.escapedText);
+ if (!!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && !!symbol.valueDeclaration.initializer) {
+ const init = symbol.valueDeclaration.initializer;
+ q.push(init);
+ if (isAssignmentExpression(init, /*excludeCompoundAssignment*/ true)) {
+ q.push(init.left);
+ q.push(init.right);
}
}
}
- return false;
}
+ return false;
+}
- function lookupSymbolForName(container: Node, name: __String): Symbol | undefined {
- const local = container.locals && container.locals.get(name);
- if (local) {
- return local.exportSymbol || local;
- }
- if (isSourceFile(container) && container.jsGlobalAugmentations && container.jsGlobalAugmentations.has(name)) {
- return container.jsGlobalAugmentations.get(name);
- }
- return container.symbol && container.symbol.exports && container.symbol.exports.get(name);
+/* @internal */
+function lookupSymbolForName(container: Node, name: __String): Symbol | undefined {
+ const local = container.locals && container.locals.get(name);
+ if (local) {
+ return local.exportSymbol || local;
+ }
+ if (isSourceFile(container) && container.jsGlobalAugmentations && container.jsGlobalAugmentations.has(name)) {
+ return container.jsGlobalAugmentations.get(name);
}
+ return container.symbol && container.symbol.exports && container.symbol.exports.get(name);
}
diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts
index aec8cae3cea12..2b94434d9e2cd 100644
--- a/src/compiler/builder.ts
+++ b/src/compiler/builder.ts
@@ -1,1361 +1,1364 @@
+import { CompilerOptions, DiagnosticCategory, DiagnosticMessageChain, ReusableBuilderState, ReadonlyESMap, Path, Diagnostic, SourceFile, BuilderState, Program, ESMap, ReadonlyCollection, forEachKey, GetCanonicalFileName, outFile, compilerOptionsAffectSemanticDiagnostics, Debug, forEachEntry, compilerOptionsAffectEmit, emptyArray, getDirectoryPath, getNormalizedAbsolutePath, getTsBuildInfoEmitOutputFilePath, DiagnosticRelatedInformation, CancellationToken, forEach, skipTypeChecking, getEmitDeclarations, tryAddToSet, AffectedFileResult, EmitResult, concatenate, filterSemanticDiagnostics, arrayFrom, compareStringsCaseSensitive, mapDefined, ensurePathIsNonModuleName, getRelativePathFromDirectory, compareValues, getOptionsNameMap, getOwnKeys, CompilerOptionsValue, CommandLineOption, BuilderProgramHost, BuilderProgram, CompilerHost, ProjectReference, isArray, createProgram, SemanticDiagnosticsBuilderProgram, EmitAndSemanticDiagnosticsBuilderProgram, createGetCanonicalFileName, maybeBind, notImplemented, WriteFileCallback, emitSkippedWithNoDiagnostics, CustomTransformers, handleNoEmitOptions, SourceMapEmitResult, addRange, isString, ReadBuildProgramHost, convertToOptionsWithAbsolutePaths, arrayToMap, isNumber, map, noop, returnUndefined } from "./ts";
+import * as ts from "./ts";
/*@internal*/
-namespace ts {
- export interface ReusableDiagnostic extends ReusableDiagnosticRelatedInformation {
- /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */
- reportsUnnecessary?: {};
- reportDeprecated?: {}
- source?: string;
- relatedInformation?: ReusableDiagnosticRelatedInformation[];
- skippedOn?: keyof CompilerOptions;
- }
-
- export interface ReusableDiagnosticRelatedInformation {
- category: DiagnosticCategory;
- code: number;
- file: string | undefined;
- start: number | undefined;
- length: number | undefined;
- messageText: string | ReusableDiagnosticMessageChain;
- }
+export interface ReusableDiagnostic extends ReusableDiagnosticRelatedInformation {
+ /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */
+ reportsUnnecessary?: {};
+ reportDeprecated?: {};
+ source?: string;
+ relatedInformation?: ReusableDiagnosticRelatedInformation[];
+ skippedOn?: keyof CompilerOptions;
+}
- export type ReusableDiagnosticMessageChain = DiagnosticMessageChain;
-
- export interface ReusableBuilderProgramState extends ReusableBuilderState {
- /**
- * Cache of bind and check diagnostics for files with their Path being the key
- */
- semanticDiagnosticsPerFile?: ReadonlyESMap | undefined;
- /**
- * The map has key by source file's path that has been changed
- */
- changedFilesSet?: ReadonlySet;
- /**
- * Set of affected files being iterated
- */
- affectedFiles?: readonly SourceFile[] | undefined;
- /**
- * Current changed file for iterating over affected files
- */
- currentChangedFilePath?: Path | undefined;
- /**
- * Map of file signatures, with key being file path, calculated while getting current changed file's affected files
- * These will be committed whenever the iteration through affected files of current changed file is complete
- */
- currentAffectedFilesSignatures?: ReadonlyESMap | undefined;
- /**
- * Newly computed visible to outside referencedSet
- */
- currentAffectedFilesExportedModulesMap?: BuilderState.ReadonlyManyToManyPathMap | undefined;
- /**
- * True if the semantic diagnostics were copied from the old state
- */
- semanticDiagnosticsFromOldState?: Set;
- /**
- * program corresponding to this state
- */
- program?: Program | undefined;
- /**
- * compilerOptions for the program
- */
- compilerOptions: CompilerOptions;
- /**
- * Files pending to be emitted
- */
- affectedFilesPendingEmit?: readonly Path[] | undefined;
- /**
- * Files pending to be emitted kind.
- */
- affectedFilesPendingEmitKind?: ReadonlyESMap | undefined;
- /**
- * Current index to retrieve pending affected file
- */
- affectedFilesPendingEmitIndex?: number | undefined;
- /*
- * true if semantic diagnostics are ReusableDiagnostic instead of Diagnostic
- */
- hasReusableDiagnostic?: true;
- }
+/* @internal */
+export interface ReusableDiagnosticRelatedInformation {
+ category: DiagnosticCategory;
+ code: number;
+ file: string | undefined;
+ start: number | undefined;
+ length: number | undefined;
+ messageText: string | ReusableDiagnosticMessageChain;
+}
- export const enum BuilderFileEmit {
- DtsOnly,
- Full
- }
+/* @internal */
+export type ReusableDiagnosticMessageChain = DiagnosticMessageChain;
+/* @internal */
+export interface ReusableBuilderProgramState extends ReusableBuilderState {
/**
- * State to store the changed files, affected files and cache semantic diagnostics
+ * Cache of bind and check diagnostics for files with their Path being the key
*/
- // TODO: GH#18217 Properties of this interface are frequently asserted to be defined.
- export interface BuilderProgramState extends BuilderState {
- /**
- * Cache of bind and check diagnostics for files with their Path being the key
- */
- semanticDiagnosticsPerFile: ESMap | undefined;
- /**
- * The map has key by source file's path that has been changed
- */
- changedFilesSet: Set;
- /**
- * Set of affected files being iterated
- */
- affectedFiles: readonly SourceFile[] | undefined;
- /**
- * Current index to retrieve affected file from
- */
- affectedFilesIndex: number | undefined;
- /**
- * Current changed file for iterating over affected files
- */
- currentChangedFilePath: Path | undefined;
- /**
- * Map of file signatures, with key being file path, calculated while getting current changed file's affected files
- * These will be committed whenever the iteration through affected files of current changed file is complete
- */
- currentAffectedFilesSignatures: ESMap | undefined;
- /**
- * Newly computed visible to outside referencedSet
- * We need to store the updates separately in case the in-progress build is cancelled
- * and we need to roll back.
- */
- currentAffectedFilesExportedModulesMap: BuilderState.ManyToManyPathMap | undefined;
- /**
- * Already seen affected files
- */
- seenAffectedFiles: Set | undefined;
- /**
- * whether this program has cleaned semantic diagnostics cache for lib files
- */
- cleanedDiagnosticsOfLibFiles?: boolean;
- /**
- * True if the semantic diagnostics were copied from the old state
- */
- semanticDiagnosticsFromOldState?: Set;
- /**
- * program corresponding to this state
- */
- program: Program | undefined;
- /**
- * compilerOptions for the program
- */
- compilerOptions: CompilerOptions;
- /**
- * Files pending to be emitted
- */
- affectedFilesPendingEmit: Path[] | undefined;
- /**
- * Files pending to be emitted kind.
- */
- affectedFilesPendingEmitKind: ESMap | undefined;
- /**
- * Current index to retrieve pending affected file
- */
- affectedFilesPendingEmitIndex: number | undefined;
- /**
- * true if build info is emitted
- */
- buildInfoEmitPending: boolean;
- /**
- * Already seen emitted files
- */
- seenEmittedFiles: ESMap | undefined;
- /**
- * true if program has been emitted
- */
- programEmitComplete?: true;
- }
+ semanticDiagnosticsPerFile?: ReadonlyESMap | undefined;
+ /**
+ * The map has key by source file's path that has been changed
+ */
+ changedFilesSet?: ts.ReadonlySet;
+ /**
+ * Set of affected files being iterated
+ */
+ affectedFiles?: readonly SourceFile[] | undefined;
+ /**
+ * Current changed file for iterating over affected files
+ */
+ currentChangedFilePath?: Path | undefined;
+ /**
+ * Map of file signatures, with key being file path, calculated while getting current changed file's affected files
+ * These will be committed whenever the iteration through affected files of current changed file is complete
+ */
+ currentAffectedFilesSignatures?: ReadonlyESMap | undefined;
+ /**
+ * Newly computed visible to outside referencedSet
+ */
+ currentAffectedFilesExportedModulesMap?: BuilderState.ReadonlyManyToManyPathMap | undefined;
+ /**
+ * True if the semantic diagnostics were copied from the old state
+ */
+ semanticDiagnosticsFromOldState?: ts.Set;
+ /**
+ * program corresponding to this state
+ */
+ program?: Program | undefined;
+ /**
+ * compilerOptions for the program
+ */
+ compilerOptions: CompilerOptions;
+ /**
+ * Files pending to be emitted
+ */
+ affectedFilesPendingEmit?: readonly Path[] | undefined;
+ /**
+ * Files pending to be emitted kind.
+ */
+ affectedFilesPendingEmitKind?: ReadonlyESMap | undefined;
+ /**
+ * Current index to retrieve pending affected file
+ */
+ affectedFilesPendingEmitIndex?: number | undefined;
+ /*
+ * true if semantic diagnostics are ReusableDiagnostic instead of Diagnostic
+ */
+ hasReusableDiagnostic?: true;
+}
- function hasSameKeys(map1: ReadonlyCollection | undefined, map2: ReadonlyCollection | undefined): boolean {
- // Has same size and every key is present in both maps
- return map1 === map2 || map1 !== undefined && map2 !== undefined && map1.size === map2.size && !forEachKey(map1, key => !map2.has(key));
- }
+/* @internal */
+export const enum BuilderFileEmit {
+ DtsOnly,
+ Full
+}
+/**
+ * State to store the changed files, affected files and cache semantic diagnostics
+ */
+// TODO: GH#18217 Properties of this interface are frequently asserted to be defined.
+/* @internal */
+export interface BuilderProgramState extends BuilderState {
/**
- * Create the state so that we can iterate on changedFiles/affected files
+ * Cache of bind and check diagnostics for files with their Path being the key
*/
- function createBuilderProgramState(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState: Readonly | undefined, disableUseFileVersionAsSignature: boolean | undefined): BuilderProgramState {
- const state = BuilderState.create(newProgram, getCanonicalFileName, oldState, disableUseFileVersionAsSignature) as BuilderProgramState;
- state.program = newProgram;
- const compilerOptions = newProgram.getCompilerOptions();
- state.compilerOptions = compilerOptions;
- // With --out or --outFile, any change affects all semantic diagnostics so no need to cache them
- if (!outFile(compilerOptions)) {
- state.semanticDiagnosticsPerFile = new Map();
- }
- state.changedFilesSet = new Set();
-
- const useOldState = BuilderState.canReuseOldState(state.referencedMap, oldState);
- const oldCompilerOptions = useOldState ? oldState!.compilerOptions : undefined;
- const canCopySemanticDiagnostics = useOldState && oldState!.semanticDiagnosticsPerFile && !!state.semanticDiagnosticsPerFile &&
- !compilerOptionsAffectSemanticDiagnostics(compilerOptions, oldCompilerOptions!);
- if (useOldState) {
- // Verify the sanity of old state
- if (!oldState!.currentChangedFilePath) {
- const affectedSignatures = oldState!.currentAffectedFilesSignatures;
- Debug.assert(!oldState!.affectedFiles && (!affectedSignatures || !affectedSignatures.size), "Cannot reuse if only few affected files of currentChangedFile were iterated");
- }
- const changedFilesSet = oldState!.changedFilesSet;
- if (canCopySemanticDiagnostics) {
- Debug.assert(!changedFilesSet || !forEachKey(changedFilesSet, path => oldState!.semanticDiagnosticsPerFile!.has(path)), "Semantic diagnostics shouldnt be available for changed files");
- }
+ semanticDiagnosticsPerFile: ESMap | undefined;
+ /**
+ * The map has key by source file's path that has been changed
+ */
+ changedFilesSet: ts.Set;
+ /**
+ * Set of affected files being iterated
+ */
+ affectedFiles: readonly SourceFile[] | undefined;
+ /**
+ * Current index to retrieve affected file from
+ */
+ affectedFilesIndex: number | undefined;
+ /**
+ * Current changed file for iterating over affected files
+ */
+ currentChangedFilePath: Path | undefined;
+ /**
+ * Map of file signatures, with key being file path, calculated while getting current changed file's affected files
+ * These will be committed whenever the iteration through affected files of current changed file is complete
+ */
+ currentAffectedFilesSignatures: ESMap | undefined;
+ /**
+ * Newly computed visible to outside referencedSet
+ * We need to store the updates separately in case the in-progress build is cancelled
+ * and we need to roll back.
+ */
+ currentAffectedFilesExportedModulesMap: BuilderState.ManyToManyPathMap | undefined;
+ /**
+ * Already seen affected files
+ */
+ seenAffectedFiles: ts.Set | undefined;
+ /**
+ * whether this program has cleaned semantic diagnostics cache for lib files
+ */
+ cleanedDiagnosticsOfLibFiles?: boolean;
+ /**
+ * True if the semantic diagnostics were copied from the old state
+ */
+ semanticDiagnosticsFromOldState?: ts.Set;
+ /**
+ * program corresponding to this state
+ */
+ program: Program | undefined;
+ /**
+ * compilerOptions for the program
+ */
+ compilerOptions: CompilerOptions;
+ /**
+ * Files pending to be emitted
+ */
+ affectedFilesPendingEmit: Path[] | undefined;
+ /**
+ * Files pending to be emitted kind.
+ */
+ affectedFilesPendingEmitKind: ESMap | undefined;
+ /**
+ * Current index to retrieve pending affected file
+ */
+ affectedFilesPendingEmitIndex: number | undefined;
+ /**
+ * true if build info is emitted
+ */
+ buildInfoEmitPending: boolean;
+ /**
+ * Already seen emitted files
+ */
+ seenEmittedFiles: ESMap | undefined;
+ /**
+ * true if program has been emitted
+ */
+ programEmitComplete?: true;
+}
- // Copy old state's changed files set
- changedFilesSet?.forEach(value => state.changedFilesSet.add(value));
- if (!outFile(compilerOptions) && oldState!.affectedFilesPendingEmit) {
- state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit.slice();
- state.affectedFilesPendingEmitKind = oldState!.affectedFilesPendingEmitKind && new Map(oldState!.affectedFilesPendingEmitKind);
- state.affectedFilesPendingEmitIndex = oldState!.affectedFilesPendingEmitIndex;
- state.seenAffectedFiles = new Set();
- }
- }
+/* @internal */
+function hasSameKeys(map1: ReadonlyCollection | undefined, map2: ReadonlyCollection | undefined): boolean {
+ // Has same size and every key is present in both maps
+ return map1 === map2 || map1 !== undefined && map2 !== undefined && map1.size === map2.size && !forEachKey(map1, key => !map2.has(key));
+}
- // Update changed files and copy semantic diagnostics if we can
- const referencedMap = state.referencedMap;
- const oldReferencedMap = useOldState ? oldState!.referencedMap : undefined;
- const copyDeclarationFileDiagnostics = canCopySemanticDiagnostics && !compilerOptions.skipLibCheck === !oldCompilerOptions!.skipLibCheck;
- const copyLibFileDiagnostics = copyDeclarationFileDiagnostics && !compilerOptions.skipDefaultLibCheck === !oldCompilerOptions!.skipDefaultLibCheck;
- state.fileInfos.forEach((info, sourceFilePath) => {
- let oldInfo: Readonly | undefined;
- let newReferences: ReadonlySet | undefined;
-
- // if not using old state, every file is changed
- if (!useOldState ||
- // File wasn't present in old state
- !(oldInfo = oldState!.fileInfos.get(sourceFilePath)) ||
- // versions dont match
- oldInfo.version !== info.version ||
- // Referenced files changed
- !hasSameKeys(newReferences = referencedMap && referencedMap.getValues(sourceFilePath), oldReferencedMap && oldReferencedMap.getValues(sourceFilePath)) ||
- // Referenced file was deleted in the new program
- newReferences && forEachKey(newReferences, path => !state.fileInfos.has(path) && oldState!.fileInfos.has(path))) {
- // Register file as changed file and do not copy semantic diagnostics, since all changed files need to be re-evaluated
- state.changedFilesSet.add(sourceFilePath);
- }
- else if (canCopySemanticDiagnostics) {
- const sourceFile = newProgram.getSourceFileByPath(sourceFilePath)!;
-
- if (sourceFile.isDeclarationFile && !copyDeclarationFileDiagnostics) return;
- if (sourceFile.hasNoDefaultLib && !copyLibFileDiagnostics) return;
-
- // Unchanged file copy diagnostics
- const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
- if (diagnostics) {
- state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as readonly ReusableDiagnostic[], newProgram, getCanonicalFileName) : diagnostics as readonly Diagnostic[]);
- if (!state.semanticDiagnosticsFromOldState) {
- state.semanticDiagnosticsFromOldState = new Set();
- }
- state.semanticDiagnosticsFromOldState.add(sourceFilePath);
- }
- }
- });
+/**
+ * Create the state so that we can iterate on changedFiles/affected files
+ */
+/* @internal */
+function createBuilderProgramState(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState: Readonly | undefined, disableUseFileVersionAsSignature: boolean | undefined): BuilderProgramState {
+ const state = BuilderState.create(newProgram, getCanonicalFileName, oldState, disableUseFileVersionAsSignature) as BuilderProgramState;
+ state.program = newProgram;
+ const compilerOptions = newProgram.getCompilerOptions();
+ state.compilerOptions = compilerOptions;
+ // With --out or --outFile, any change affects all semantic diagnostics so no need to cache them
+ if (!outFile(compilerOptions)) {
+ state.semanticDiagnosticsPerFile = new ts.Map();
+ }
+ state.changedFilesSet = new ts.Set();
+
+ const useOldState = BuilderState.canReuseOldState(state.referencedMap, oldState);
+ const oldCompilerOptions = useOldState ? oldState!.compilerOptions : undefined;
+ const canCopySemanticDiagnostics = useOldState && oldState!.semanticDiagnosticsPerFile && !!state.semanticDiagnosticsPerFile &&
+ !compilerOptionsAffectSemanticDiagnostics(compilerOptions, oldCompilerOptions!);
+ if (useOldState) {
+ // Verify the sanity of old state
+ if (!oldState!.currentChangedFilePath) {
+ const affectedSignatures = oldState!.currentAffectedFilesSignatures;
+ Debug.assert(!oldState!.affectedFiles && (!affectedSignatures || !affectedSignatures.size), "Cannot reuse if only few affected files of currentChangedFile were iterated");
+ }
+ const changedFilesSet = oldState!.changedFilesSet;
+ if (canCopySemanticDiagnostics) {
+ Debug.assert(!changedFilesSet || !forEachKey(changedFilesSet, path => oldState!.semanticDiagnosticsPerFile!.has(path)), "Semantic diagnostics shouldnt be available for changed files");
+ }
- // If the global file is removed, add all files as changed
- if (useOldState && forEachEntry(oldState!.fileInfos, (info, sourceFilePath) => info.affectsGlobalScope && !state.fileInfos.has(sourceFilePath))) {
- BuilderState.getAllFilesExcludingDefaultLibraryFile(state, newProgram, /*firstSourceFile*/ undefined)
- .forEach(file => state.changedFilesSet.add(file.resolvedPath));
+ // Copy old state's changed files set
+ changedFilesSet?.forEach(value => state.changedFilesSet.add(value));
+ if (!outFile(compilerOptions) && oldState!.affectedFilesPendingEmit) {
+ state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit.slice();
+ state.affectedFilesPendingEmitKind = oldState!.affectedFilesPendingEmitKind && new ts.Map(oldState!.affectedFilesPendingEmitKind);
+ state.affectedFilesPendingEmitIndex = oldState!.affectedFilesPendingEmitIndex;
+ state.seenAffectedFiles = new ts.Set();
}
- else if (oldCompilerOptions && !outFile(compilerOptions) && compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions)) {
- // Add all files to affectedFilesPendingEmit since emit changed
- newProgram.getSourceFiles().forEach(f => addToAffectedFilesPendingEmit(state, f.resolvedPath, BuilderFileEmit.Full));
- Debug.assert(!state.seenAffectedFiles || !state.seenAffectedFiles.size);
- state.seenAffectedFiles = state.seenAffectedFiles || new Set();
+ }
+
+ // Update changed files and copy semantic diagnostics if we can
+ const referencedMap = state.referencedMap;
+ const oldReferencedMap = useOldState ? oldState!.referencedMap : undefined;
+ const copyDeclarationFileDiagnostics = canCopySemanticDiagnostics && !compilerOptions.skipLibCheck === !oldCompilerOptions!.skipLibCheck;
+ const copyLibFileDiagnostics = copyDeclarationFileDiagnostics && !compilerOptions.skipDefaultLibCheck === !oldCompilerOptions!.skipDefaultLibCheck;
+ state.fileInfos.forEach((info, sourceFilePath) => {
+ let oldInfo: Readonly | undefined;
+ let newReferences: ts.ReadonlySet | undefined;
+
+ // if not using old state, every file is changed
+ if (!useOldState ||
+ // File wasn't present in old state
+ !(oldInfo = oldState!.fileInfos.get(sourceFilePath)) ||
+ // versions dont match
+ oldInfo.version !== info.version ||
+ // Referenced files changed
+ !hasSameKeys(newReferences = referencedMap && referencedMap.getValues(sourceFilePath), oldReferencedMap && oldReferencedMap.getValues(sourceFilePath)) ||
+ // Referenced file was deleted in the new program
+ newReferences && forEachKey(newReferences, path => !state.fileInfos.has(path) && oldState!.fileInfos.has(path))) {
+ // Register file as changed file and do not copy semantic diagnostics, since all changed files need to be re-evaluated
+ state.changedFilesSet.add(sourceFilePath);
}
- if (useOldState) {
- // Any time the interpretation of a source file changes, mark it as changed
- forEachEntry(oldState!.fileInfos, (info, sourceFilePath) => {
- if (state.fileInfos.has(sourceFilePath) && state.fileInfos.get(sourceFilePath)!.impliedFormat !== info.impliedFormat) {
- state.changedFilesSet.add(sourceFilePath);
+ else if (canCopySemanticDiagnostics) {
+ const sourceFile = newProgram.getSourceFileByPath(sourceFilePath)!;
+
+ if (sourceFile.isDeclarationFile && !copyDeclarationFileDiagnostics)
+ return;
+ if (sourceFile.hasNoDefaultLib && !copyLibFileDiagnostics)
+ return;
+
+ // Unchanged file copy diagnostics
+ const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
+ if (diagnostics) {
+ state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as readonly ReusableDiagnostic[], newProgram, getCanonicalFileName) : diagnostics as readonly Diagnostic[]);
+ if (!state.semanticDiagnosticsFromOldState) {
+ state.semanticDiagnosticsFromOldState = new ts.Set();
}
- });
+ state.semanticDiagnosticsFromOldState.add(sourceFilePath);
+ }
}
+ });
- state.buildInfoEmitPending = !!state.changedFilesSet.size;
- return state;
+ // If the global file is removed, add all files as changed
+ if (useOldState && forEachEntry(oldState!.fileInfos, (info, sourceFilePath) => info.affectsGlobalScope && !state.fileInfos.has(sourceFilePath))) {
+ BuilderState.getAllFilesExcludingDefaultLibraryFile(state, newProgram, /*firstSourceFile*/ undefined)
+ .forEach(file => state.changedFilesSet.add(file.resolvedPath));
}
-
- function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newProgram: Program, getCanonicalFileName: GetCanonicalFileName): readonly Diagnostic[] {
- if (!diagnostics.length) return emptyArray;
- const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(newProgram.getCompilerOptions())!, newProgram.getCurrentDirectory()));
- return diagnostics.map(diagnostic => {
- const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram, toPath);
- result.reportsUnnecessary = diagnostic.reportsUnnecessary;
- result.reportsDeprecated = diagnostic.reportDeprecated;
- result.source = diagnostic.source;
- result.skippedOn = diagnostic.skippedOn;
- const { relatedInformation } = diagnostic;
- result.relatedInformation = relatedInformation ?
- relatedInformation.length ?
- relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram, toPath)) :
- [] :
- undefined;
- return result;
+ else if (oldCompilerOptions && !outFile(compilerOptions) && compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions)) {
+ // Add all files to affectedFilesPendingEmit since emit changed
+ newProgram.getSourceFiles().forEach(f => addToAffectedFilesPendingEmit(state, f.resolvedPath, BuilderFileEmit.Full));
+ Debug.assert(!state.seenAffectedFiles || !state.seenAffectedFiles.size);
+ state.seenAffectedFiles = state.seenAffectedFiles || new ts.Set();
+ }
+ if (useOldState) {
+ // Any time the interpretation of a source file changes, mark it as changed
+ forEachEntry(oldState!.fileInfos, (info, sourceFilePath) => {
+ if (state.fileInfos.has(sourceFilePath) && state.fileInfos.get(sourceFilePath)!.impliedFormat !== info.impliedFormat) {
+ state.changedFilesSet.add(sourceFilePath);
+ }
});
-
- function toPath(path: string) {
- return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
- }
}
- function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program, toPath: (path: string) => Path): DiagnosticRelatedInformation {
- const { file } = diagnostic;
- return {
- ...diagnostic,
- file: file ? newProgram.getSourceFileByPath(toPath(file)) : undefined
- };
- }
+ state.buildInfoEmitPending = !!state.changedFilesSet.size;
+ return state;
+}
- /**
- * Releases program and other related not needed properties
- */
- function releaseCache(state: BuilderProgramState) {
- BuilderState.releaseCache(state);
- state.program = undefined;
- }
+/* @internal */
+function convertToDiagnostics(diagnostics: readonly ReusableDiagnostic[], newProgram: Program, getCanonicalFileName: GetCanonicalFileName): readonly Diagnostic[] {
+ if (!diagnostics.length)
+ return emptyArray;
+ const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(newProgram.getCompilerOptions())!, newProgram.getCurrentDirectory()));
+ return diagnostics.map(diagnostic => {
+ const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram, toPath);
+ result.reportsUnnecessary = diagnostic.reportsUnnecessary;
+ result.reportsDeprecated = diagnostic.reportDeprecated;
+ result.source = diagnostic.source;
+ result.skippedOn = diagnostic.skippedOn;
+ const { relatedInformation } = diagnostic;
+ result.relatedInformation = relatedInformation ?
+ relatedInformation.length ?
+ relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram, toPath)) :
+ [] :
+ undefined;
+ return result;
+ });
- /**
- * Creates a clone of the state
- */
- function cloneBuilderProgramState(state: Readonly): BuilderProgramState {
- const newState = BuilderState.clone(state) as BuilderProgramState;
- newState.semanticDiagnosticsPerFile = state.semanticDiagnosticsPerFile && new Map(state.semanticDiagnosticsPerFile);
- newState.changedFilesSet = new Set(state.changedFilesSet);
- newState.affectedFiles = state.affectedFiles;
- newState.affectedFilesIndex = state.affectedFilesIndex;
- newState.currentChangedFilePath = state.currentChangedFilePath;
- newState.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures && new Map(state.currentAffectedFilesSignatures);
- newState.currentAffectedFilesExportedModulesMap = state.currentAffectedFilesExportedModulesMap?.clone();
- newState.seenAffectedFiles = state.seenAffectedFiles && new Set(state.seenAffectedFiles);
- newState.cleanedDiagnosticsOfLibFiles = state.cleanedDiagnosticsOfLibFiles;
- newState.semanticDiagnosticsFromOldState = state.semanticDiagnosticsFromOldState && new Set(state.semanticDiagnosticsFromOldState);
- newState.program = state.program;
- newState.compilerOptions = state.compilerOptions;
- newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice();
- newState.affectedFilesPendingEmitKind = state.affectedFilesPendingEmitKind && new Map(state.affectedFilesPendingEmitKind);
- newState.affectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex;
- newState.seenEmittedFiles = state.seenEmittedFiles && new Map(state.seenEmittedFiles);
- newState.programEmitComplete = state.programEmitComplete;
- return newState;
+ function toPath(path: string) {
+ return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
}
+}
- /**
- * Verifies that source file is ok to be used in calls that arent handled by next
- */
- function assertSourceFileOkWithoutNextAffectedCall(state: BuilderProgramState, sourceFile: SourceFile | undefined) {
- Debug.assert(!sourceFile || !state.affectedFiles || state.affectedFiles[state.affectedFilesIndex! - 1] !== sourceFile || !state.semanticDiagnosticsPerFile!.has(sourceFile.resolvedPath));
- }
+/* @internal */
+function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program, toPath: (path: string) => Path): DiagnosticRelatedInformation {
+ const { file } = diagnostic;
+ return {
+ ...diagnostic,
+ file: file ? newProgram.getSourceFileByPath(toPath(file)) : undefined
+ };
+}
- /**
- * This function returns the next affected file to be processed.
- * Note that until doneAffected is called it would keep reporting same result
- * This is to allow the callers to be able to actually remove affected file only when the operation is complete
- * eg. if during diagnostics check cancellation token ends up cancelling the request, the affected file should be retained
- */
- function getNextAffectedFile(state: BuilderProgramState, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash): SourceFile | Program | undefined {
- while (true) {
- const { affectedFiles } = state;
- if (affectedFiles) {
- const seenAffectedFiles = state.seenAffectedFiles!;
- let affectedFilesIndex = state.affectedFilesIndex!; // TODO: GH#18217
- while (affectedFilesIndex < affectedFiles.length) {
- const affectedFile = affectedFiles[affectedFilesIndex];
- if (!seenAffectedFiles.has(affectedFile.resolvedPath)) {
- // Set the next affected file as seen and remove the cached semantic diagnostics
- state.affectedFilesIndex = affectedFilesIndex;
- handleDtsMayChangeOfAffectedFile(state, affectedFile, cancellationToken, computeHash);
- return affectedFile;
- }
- affectedFilesIndex++;
- }
+/**
+ * Releases program and other related not needed properties
+ */
+/* @internal */
+function releaseCache(state: BuilderProgramState) {
+ BuilderState.releaseCache(state);
+ state.program = undefined;
+}
- // Remove the changed file from the change set
- state.changedFilesSet.delete(state.currentChangedFilePath!);
- state.currentChangedFilePath = undefined;
- // Commit the changes in file signature
- BuilderState.updateSignaturesFromCache(state, state.currentAffectedFilesSignatures!);
- state.currentAffectedFilesSignatures!.clear();
- BuilderState.updateExportedFilesMapFromCache(state, state.currentAffectedFilesExportedModulesMap);
- state.affectedFiles = undefined;
- }
+/**
+ * Creates a clone of the state
+ */
+/* @internal */
+function cloneBuilderProgramState(state: Readonly): BuilderProgramState {
+ const newState = BuilderState.clone(state) as BuilderProgramState;
+ newState.semanticDiagnosticsPerFile = state.semanticDiagnosticsPerFile && new ts.Map(state.semanticDiagnosticsPerFile);
+ newState.changedFilesSet = new ts.Set(state.changedFilesSet);
+ newState.affectedFiles = state.affectedFiles;
+ newState.affectedFilesIndex = state.affectedFilesIndex;
+ newState.currentChangedFilePath = state.currentChangedFilePath;
+ newState.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures && new ts.Map(state.currentAffectedFilesSignatures);
+ newState.currentAffectedFilesExportedModulesMap = state.currentAffectedFilesExportedModulesMap?.clone();
+ newState.seenAffectedFiles = state.seenAffectedFiles && new ts.Set(state.seenAffectedFiles);
+ newState.cleanedDiagnosticsOfLibFiles = state.cleanedDiagnosticsOfLibFiles;
+ newState.semanticDiagnosticsFromOldState = state.semanticDiagnosticsFromOldState && new ts.Set(state.semanticDiagnosticsFromOldState);
+ newState.program = state.program;
+ newState.compilerOptions = state.compilerOptions;
+ newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice();
+ newState.affectedFilesPendingEmitKind = state.affectedFilesPendingEmitKind && new ts.Map(state.affectedFilesPendingEmitKind);
+ newState.affectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex;
+ newState.seenEmittedFiles = state.seenEmittedFiles && new ts.Map(state.seenEmittedFiles);
+ newState.programEmitComplete = state.programEmitComplete;
+ return newState;
+}
- // Get next changed file
- const nextKey = state.changedFilesSet.keys().next();
- if (nextKey.done) {
- // Done
- return undefined;
- }
+/**
+ * Verifies that source file is ok to be used in calls that arent handled by next
+ */
+/* @internal */
+function assertSourceFileOkWithoutNextAffectedCall(state: BuilderProgramState, sourceFile: SourceFile | undefined) {
+ Debug.assert(!sourceFile || !state.affectedFiles || state.affectedFiles[state.affectedFilesIndex! - 1] !== sourceFile || !state.semanticDiagnosticsPerFile!.has(sourceFile.resolvedPath));
+}
- // With --out or --outFile all outputs go into single file
- // so operations are performed directly on program, return program
- const program = Debug.checkDefined(state.program);
- const compilerOptions = program.getCompilerOptions();
- if (outFile(compilerOptions)) {
- Debug.assert(!state.semanticDiagnosticsPerFile);
- return program;
+/**
+ * This function returns the next affected file to be processed.
+ * Note that until doneAffected is called it would keep reporting same result
+ * This is to allow the callers to be able to actually remove affected file only when the operation is complete
+ * eg. if during diagnostics check cancellation token ends up cancelling the request, the affected file should be retained
+ */
+/* @internal */
+function getNextAffectedFile(state: BuilderProgramState, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash): SourceFile | Program | undefined {
+ while (true) {
+ const { affectedFiles } = state;
+ if (affectedFiles) {
+ const seenAffectedFiles = state.seenAffectedFiles!;
+ let affectedFilesIndex = state.affectedFilesIndex!; // TODO: GH#18217
+ while (affectedFilesIndex < affectedFiles.length) {
+ const affectedFile = affectedFiles[affectedFilesIndex];
+ if (!seenAffectedFiles.has(affectedFile.resolvedPath)) {
+ // Set the next affected file as seen and remove the cached semantic diagnostics
+ state.affectedFilesIndex = affectedFilesIndex;
+ handleDtsMayChangeOfAffectedFile(state, affectedFile, cancellationToken, computeHash);
+ return affectedFile;
+ }
+ affectedFilesIndex++;
}
- // Get next batch of affected files
- if (!state.currentAffectedFilesSignatures) state.currentAffectedFilesSignatures = new Map();
- if (state.exportedModulesMap) {
- state.currentAffectedFilesExportedModulesMap ||= BuilderState.createManyToManyPathMap();
- }
- state.affectedFiles = BuilderState.getFilesAffectedBy(state, program, nextKey.value, cancellationToken, computeHash, state.currentAffectedFilesSignatures, state.currentAffectedFilesExportedModulesMap);
- state.currentChangedFilePath = nextKey.value;
- state.affectedFilesIndex = 0;
- if (!state.seenAffectedFiles) state.seenAffectedFiles = new Set();
+ // Remove the changed file from the change set
+ state.changedFilesSet.delete(state.currentChangedFilePath!);
+ state.currentChangedFilePath = undefined;
+ // Commit the changes in file signature
+ BuilderState.updateSignaturesFromCache(state, state.currentAffectedFilesSignatures!);
+ state.currentAffectedFilesSignatures!.clear();
+ BuilderState.updateExportedFilesMapFromCache(state, state.currentAffectedFilesExportedModulesMap);
+ state.affectedFiles = undefined;
}
- }
- /**
- * Returns next file to be emitted from files that retrieved semantic diagnostics but did not emit yet
- */
- function getNextAffectedFilePendingEmit(state: BuilderProgramState) {
- const { affectedFilesPendingEmit } = state;
- if (affectedFilesPendingEmit) {
- const seenEmittedFiles = (state.seenEmittedFiles || (state.seenEmittedFiles = new Map()));
- for (let i = state.affectedFilesPendingEmitIndex!; i < affectedFilesPendingEmit.length; i++) {
- const affectedFile = Debug.checkDefined(state.program).getSourceFileByPath(affectedFilesPendingEmit[i]);
- if (affectedFile) {
- const seenKind = seenEmittedFiles.get(affectedFile.resolvedPath);
- const emitKind = Debug.checkDefined(Debug.checkDefined(state.affectedFilesPendingEmitKind).get(affectedFile.resolvedPath));
- if (seenKind === undefined || seenKind < emitKind) {
- // emit this file
- state.affectedFilesPendingEmitIndex = i;
- return { affectedFile, emitKind };
- }
- }
- }
- state.affectedFilesPendingEmit = undefined;
- state.affectedFilesPendingEmitKind = undefined;
- state.affectedFilesPendingEmitIndex = undefined;
+ // Get next changed file
+ const nextKey = state.changedFilesSet.keys().next();
+ if (nextKey.done) {
+ // Done
+ return undefined;
}
- return undefined;
- }
-
- /**
- * Handles semantic diagnostics and dts emit for affectedFile and files, that are referencing modules that export entities from affected file
- * This is because even though js emit doesnt change, dts emit / type used can change resulting in need for dts emit and js change
- */
- function handleDtsMayChangeOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash) {
- removeSemanticDiagnosticsOf(state, affectedFile.resolvedPath);
- // If affected files is everything except default library, then nothing more to do
- if (state.allFilesExcludingDefaultLibraryFile === state.affectedFiles) {
- if (!state.cleanedDiagnosticsOfLibFiles) {
- state.cleanedDiagnosticsOfLibFiles = true;
- const program = Debug.checkDefined(state.program);
- const options = program.getCompilerOptions();
- forEach(program.getSourceFiles(), f =>
- program.isSourceFileDefaultLibrary(f) &&
- !skipTypeChecking(f, options, program) &&
- removeSemanticDiagnosticsOf(state, f.resolvedPath)
- );
- }
- // When a change affects the global scope, all files are considered to be affected without updating their signature
- // That means when affected file is handled, its signature can be out of date
- // To avoid this, ensure that we update the signature for any affected file in this scenario.
- BuilderState.updateShapeSignature(
- state,
- Debug.checkDefined(state.program),
- affectedFile,
- Debug.checkDefined(state.currentAffectedFilesSignatures),
- cancellationToken,
- computeHash,
- state.currentAffectedFilesExportedModulesMap
- );
- return;
- }
- else {
- Debug.assert(state.hasCalledUpdateShapeSignature.has(affectedFile.resolvedPath) || state.currentAffectedFilesSignatures?.has(affectedFile.resolvedPath), `Signature not updated for affected file: ${affectedFile.fileName}`);
+ // With --out or --outFile all outputs go into single file
+ // so operations are performed directly on program, return program
+ const program = Debug.checkDefined(state.program);
+ const compilerOptions = program.getCompilerOptions();
+ if (outFile(compilerOptions)) {
+ Debug.assert(!state.semanticDiagnosticsPerFile);
+ return program;
}
- if (!state.compilerOptions.assumeChangesOnlyAffectDirectDependencies) {
- forEachReferencingModulesOfExportOfAffectedFile(state, affectedFile, (state, path) => handleDtsMayChangeOf(state, path, cancellationToken, computeHash));
+ // Get next batch of affected files
+ if (!state.currentAffectedFilesSignatures)
+ state.currentAffectedFilesSignatures = new ts.Map();
+ if (state.exportedModulesMap) {
+ state.currentAffectedFilesExportedModulesMap ||= BuilderState.createManyToManyPathMap();
}
+ state.affectedFiles = BuilderState.getFilesAffectedBy(state, program, nextKey.value, cancellationToken, computeHash, state.currentAffectedFilesSignatures, state.currentAffectedFilesExportedModulesMap);
+ state.currentChangedFilePath = nextKey.value;
+ state.affectedFilesIndex = 0;
+ if (!state.seenAffectedFiles)
+ state.seenAffectedFiles = new ts.Set();
}
+}
- /**
- * Handle the dts may change, so they need to be added to pending emit if dts emit is enabled,
- * Also we need to make sure signature is updated for these files
- */
- function handleDtsMayChangeOf(state: BuilderProgramState, path: Path, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash): void {
- removeSemanticDiagnosticsOf(state, path);
-
- if (!state.changedFilesSet.has(path)) {
- const program = Debug.checkDefined(state.program);
- const sourceFile = program.getSourceFileByPath(path);
- if (sourceFile) {
- // Even though the js emit doesnt change and we are already handling dts emit and semantic diagnostics
- // we need to update the signature to reflect correctness of the signature(which is output d.ts emit) of this file
- // This ensures that we dont later during incremental builds considering wrong signature.
- // Eg where this also is needed to ensure that .tsbuildinfo generated by incremental build should be same as if it was first fresh build
- // But we avoid expensive full shape computation, as using file version as shape is enough for correctness.
- BuilderState.updateShapeSignature(
- state,
- program,
- sourceFile,
- Debug.checkDefined(state.currentAffectedFilesSignatures),
- cancellationToken,
- computeHash,
- state.currentAffectedFilesExportedModulesMap,
- /* useFileVersionAsSignature */ true
- );
- // If not dts emit, nothing more to do
- if (getEmitDeclarations(state.compilerOptions)) {
- addToAffectedFilesPendingEmit(state, path, BuilderFileEmit.DtsOnly);
+/**
+ * Returns next file to be emitted from files that retrieved semantic diagnostics but did not emit yet
+ */
+/* @internal */
+function getNextAffectedFilePendingEmit(state: BuilderProgramState) {
+ const { affectedFilesPendingEmit } = state;
+ if (affectedFilesPendingEmit) {
+ const seenEmittedFiles = (state.seenEmittedFiles || (state.seenEmittedFiles = new ts.Map()));
+ for (let i = state.affectedFilesPendingEmitIndex!; i < affectedFilesPendingEmit.length; i++) {
+ const affectedFile = Debug.checkDefined(state.program).getSourceFileByPath(affectedFilesPendingEmit[i]);
+ if (affectedFile) {
+ const seenKind = seenEmittedFiles.get(affectedFile.resolvedPath);
+ const emitKind = Debug.checkDefined(Debug.checkDefined(state.affectedFilesPendingEmitKind).get(affectedFile.resolvedPath));
+ if (seenKind === undefined || seenKind < emitKind) {
+ // emit this file
+ state.affectedFilesPendingEmitIndex = i;
+ return { affectedFile, emitKind };
}
}
}
+ state.affectedFilesPendingEmit = undefined;
+ state.affectedFilesPendingEmitKind = undefined;
+ state.affectedFilesPendingEmitIndex = undefined;
}
+ return undefined;
+}
- /**
- * Removes semantic diagnostics for path and
- * returns true if there are no more semantic diagnostics from the old state
- */
- function removeSemanticDiagnosticsOf(state: BuilderProgramState, path: Path) {
- if (!state.semanticDiagnosticsFromOldState) {
- return true;
+/**
+ * Handles semantic diagnostics and dts emit for affectedFile and files, that are referencing modules that export entities from affected file
+ * This is because even though js emit doesnt change, dts emit / type used can change resulting in need for dts emit and js change
+ */
+/* @internal */
+function handleDtsMayChangeOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash) {
+ removeSemanticDiagnosticsOf(state, affectedFile.resolvedPath);
+
+ // If affected files is everything except default library, then nothing more to do
+ if (state.allFilesExcludingDefaultLibraryFile === state.affectedFiles) {
+ if (!state.cleanedDiagnosticsOfLibFiles) {
+ state.cleanedDiagnosticsOfLibFiles = true;
+ const program = Debug.checkDefined(state.program);
+ const options = program.getCompilerOptions();
+ forEach(program.getSourceFiles(), f => program.isSourceFileDefaultLibrary(f) &&
+ !skipTypeChecking(f, options, program) &&
+ removeSemanticDiagnosticsOf(state, f.resolvedPath));
}
- state.semanticDiagnosticsFromOldState.delete(path);
- state.semanticDiagnosticsPerFile!.delete(path);
- return !state.semanticDiagnosticsFromOldState.size;
+ // When a change affects the global scope, all files are considered to be affected without updating their signature
+ // That means when affected file is handled, its signature can be out of date
+ // To avoid this, ensure that we update the signature for any affected file in this scenario.
+ BuilderState.updateShapeSignature(state, Debug.checkDefined(state.program), affectedFile, Debug.checkDefined(state.currentAffectedFilesSignatures), cancellationToken, computeHash, state.currentAffectedFilesExportedModulesMap);
+ return;
}
-
- function isChangedSignature(state: BuilderProgramState, path: Path) {
- const newSignature = Debug.checkDefined(state.currentAffectedFilesSignatures).get(path);
- const oldSignature = Debug.checkDefined(state.fileInfos.get(path)).signature;
- return newSignature !== oldSignature;
+ else {
+ Debug.assert(state.hasCalledUpdateShapeSignature.has(affectedFile.resolvedPath) || state.currentAffectedFilesSignatures?.has(affectedFile.resolvedPath), `Signature not updated for affected file: ${affectedFile.fileName}`);
}
- /**
- * Iterate on referencing modules that export entities from affected file
- */
- function forEachReferencingModulesOfExportOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile, fn: (state: BuilderProgramState, filePath: Path) => void) {
- // If there was change in signature (dts output) for the changed file,
- // then only we need to handle pending file emit
- if (!state.exportedModulesMap || !state.changedFilesSet.has(affectedFile.resolvedPath)) {
- return;
- }
+ if (!state.compilerOptions.assumeChangesOnlyAffectDirectDependencies) {
+ forEachReferencingModulesOfExportOfAffectedFile(state, affectedFile, (state, path) => handleDtsMayChangeOf(state, path, cancellationToken, computeHash));
+ }
+}
- if (!isChangedSignature(state, affectedFile.resolvedPath)) return;
-
- // Since isolated modules dont change js files, files affected by change in signature is itself
- // But we need to cleanup semantic diagnostics and queue dts emit for affected files
- if (state.compilerOptions.isolatedModules) {
- const seenFileNamesMap = new Map();
- seenFileNamesMap.set(affectedFile.resolvedPath, true);
- const queue = BuilderState.getReferencedByPaths(state, affectedFile.resolvedPath);
- while (queue.length > 0) {
- const currentPath = queue.pop()!;
- if (!seenFileNamesMap.has(currentPath)) {
- seenFileNamesMap.set(currentPath, true);
- fn(state, currentPath);
- if (isChangedSignature(state, currentPath)) {
- const currentSourceFile = Debug.checkDefined(state.program).getSourceFileByPath(currentPath)!;
- queue.push(...BuilderState.getReferencedByPaths(state, currentSourceFile.resolvedPath));
- }
- }
+/**
+ * Handle the dts may change, so they need to be added to pending emit if dts emit is enabled,
+ * Also we need to make sure signature is updated for these files
+ */
+/* @internal */
+function handleDtsMayChangeOf(state: BuilderProgramState, path: Path, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash): void {
+ removeSemanticDiagnosticsOf(state, path);
+
+ if (!state.changedFilesSet.has(path)) {
+ const program = Debug.checkDefined(state.program);
+ const sourceFile = program.getSourceFileByPath(path);
+ if (sourceFile) {
+ // Even though the js emit doesnt change and we are already handling dts emit and semantic diagnostics
+ // we need to update the signature to reflect correctness of the signature(which is output d.ts emit) of this file
+ // This ensures that we dont later during incremental builds considering wrong signature.
+ // Eg where this also is needed to ensure that .tsbuildinfo generated by incremental build should be same as if it was first fresh build
+ // But we avoid expensive full shape computation, as using file version as shape is enough for correctness.
+ BuilderState.updateShapeSignature(state, program, sourceFile, Debug.checkDefined(state.currentAffectedFilesSignatures), cancellationToken, computeHash, state.currentAffectedFilesExportedModulesMap,
+ /* useFileVersionAsSignature */ true);
+ // If not dts emit, nothing more to do
+ if (getEmitDeclarations(state.compilerOptions)) {
+ addToAffectedFilesPendingEmit(state, path, BuilderFileEmit.DtsOnly);
}
}
+ }
+}
- Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
-
- const seenFileAndExportsOfFile = new Set();
- // Go through exported modules from cache first
- // If exported modules has path, all files referencing file exported from are affected
- state.currentAffectedFilesExportedModulesMap.getKeys(affectedFile.resolvedPath)?.forEach(exportedFromPath =>
- forEachFilesReferencingPath(state, exportedFromPath, seenFileAndExportsOfFile, fn)
- );
-
- // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
- state.exportedModulesMap.getKeys(affectedFile.resolvedPath)?.forEach(exportedFromPath =>
- // If the cache had an updated value, skip
- !state.currentAffectedFilesExportedModulesMap!.hasKey(exportedFromPath) &&
- !state.currentAffectedFilesExportedModulesMap!.deletedKeys()?.has(exportedFromPath) &&
- forEachFilesReferencingPath(state, exportedFromPath, seenFileAndExportsOfFile, fn)
- );
+/**
+ * Removes semantic diagnostics for path and
+ * returns true if there are no more semantic diagnostics from the old state
+ */
+/* @internal */
+function removeSemanticDiagnosticsOf(state: BuilderProgramState, path: Path) {
+ if (!state.semanticDiagnosticsFromOldState) {
+ return true;
}
+ state.semanticDiagnosticsFromOldState.delete(path);
+ state.semanticDiagnosticsPerFile!.delete(path);
+ return !state.semanticDiagnosticsFromOldState.size;
+}
- /**
- * Iterate on files referencing referencedPath
- */
- function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => void): void {
- state.referencedMap!.getKeys(referencedPath)?.forEach(filePath =>
- forEachFileAndExportsOfFile(state, filePath, seenFileAndExportsOfFile, fn)
- );
+/* @internal */
+function isChangedSignature(state: BuilderProgramState, path: Path) {
+ const newSignature = Debug.checkDefined(state.currentAffectedFilesSignatures).get(path);
+ const oldSignature = Debug.checkDefined(state.fileInfos.get(path)).signature;
+ return newSignature !== oldSignature;
+}
+
+/**
+ * Iterate on referencing modules that export entities from affected file
+ */
+/* @internal */
+function forEachReferencingModulesOfExportOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile, fn: (state: BuilderProgramState, filePath: Path) => void) {
+ // If there was change in signature (dts output) for the changed file,
+ // then only we need to handle pending file emit
+ if (!state.exportedModulesMap || !state.changedFilesSet.has(affectedFile.resolvedPath)) {
+ return;
}
- /**
- * fn on file and iterate on anything that exports this file
- */
- function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => void): void {
- if (!tryAddToSet(seenFileAndExportsOfFile, filePath)) {
- return;
+ if (!isChangedSignature(state, affectedFile.resolvedPath))
+ return;
+
+ // Since isolated modules dont change js files, files affected by change in signature is itself
+ // But we need to cleanup semantic diagnostics and queue dts emit for affected files
+ if (state.compilerOptions.isolatedModules) {
+ const seenFileNamesMap = new ts.Map();
+ seenFileNamesMap.set(affectedFile.resolvedPath, true);
+ const queue = BuilderState.getReferencedByPaths(state, affectedFile.resolvedPath);
+ while (queue.length > 0) {
+ const currentPath = queue.pop()!;
+ if (!seenFileNamesMap.has(currentPath)) {
+ seenFileNamesMap.set(currentPath, true);
+ fn(state, currentPath);
+ if (isChangedSignature(state, currentPath)) {
+ const currentSourceFile = Debug.checkDefined(state.program).getSourceFileByPath(currentPath)!;
+ queue.push(...BuilderState.getReferencedByPaths(state, currentSourceFile.resolvedPath));
+ }
+ }
}
+ }
+
+ Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
+
+ const seenFileAndExportsOfFile = new ts.Set();
+ // Go through exported modules from cache first
+ // If exported modules has path, all files referencing file exported from are affected
+ state.currentAffectedFilesExportedModulesMap.getKeys(affectedFile.resolvedPath)?.forEach(exportedFromPath => forEachFilesReferencingPath(state, exportedFromPath, seenFileAndExportsOfFile, fn));
- fn(state, filePath);
-
- Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
- // Go through exported modules from cache first
- // If exported modules has path, all files referencing file exported from are affected
- state.currentAffectedFilesExportedModulesMap.getKeys(filePath)?.forEach(exportedFromPath =>
- forEachFileAndExportsOfFile(state, exportedFromPath, seenFileAndExportsOfFile, fn)
- );
-
- // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
- state.exportedModulesMap!.getKeys(filePath)?.forEach(exportedFromPath =>
- // If the cache had an updated value, skip
- !state.currentAffectedFilesExportedModulesMap!.hasKey(exportedFromPath) &&
- !state.currentAffectedFilesExportedModulesMap!.deletedKeys()?.has(exportedFromPath) &&
- forEachFileAndExportsOfFile(state, exportedFromPath, seenFileAndExportsOfFile, fn)
- );
-
- // Remove diagnostics of files that import this file (without going to exports of referencing files)
- state.referencedMap!.getKeys(filePath)?.forEach(referencingFilePath =>
- !seenFileAndExportsOfFile.has(referencingFilePath) && // Not already removed diagnostic file
- fn(state, referencingFilePath) // Dont add to seen since this is not yet done with the export removal
- );
+ // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
+ state.exportedModulesMap.getKeys(affectedFile.resolvedPath)?.forEach(exportedFromPath =>
+ // If the cache had an updated value, skip
+ !state.currentAffectedFilesExportedModulesMap!.hasKey(exportedFromPath) &&
+ !state.currentAffectedFilesExportedModulesMap!.deletedKeys()?.has(exportedFromPath) &&
+ forEachFilesReferencingPath(state, exportedFromPath, seenFileAndExportsOfFile, fn));
+}
+
+/**
+ * Iterate on files referencing referencedPath
+ */
+/* @internal */
+function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: ts.Set, fn: (state: BuilderProgramState, filePath: Path) => void): void {
+ state.referencedMap!.getKeys(referencedPath)?.forEach(filePath => forEachFileAndExportsOfFile(state, filePath, seenFileAndExportsOfFile, fn));
+}
+
+/**
+ * fn on file and iterate on anything that exports this file
+ */
+/* @internal */
+function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: ts.Set, fn: (state: BuilderProgramState, filePath: Path) => void): void {
+ if (!tryAddToSet(seenFileAndExportsOfFile, filePath)) {
+ return;
}
+ fn(state, filePath);
- /**
- * This is called after completing operation on the next affected file.
- * The operations here are postponed to ensure that cancellation during the iteration is handled correctly
- */
- function doneWithAffectedFile(
- state: BuilderProgramState,
- affected: SourceFile | Program,
- emitKind?: BuilderFileEmit,
- isPendingEmit?: boolean,
- isBuildInfoEmit?: boolean
- ) {
- if (isBuildInfoEmit) {
- state.buildInfoEmitPending = false;
+ Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
+ // Go through exported modules from cache first
+ // If exported modules has path, all files referencing file exported from are affected
+ state.currentAffectedFilesExportedModulesMap.getKeys(filePath)?.forEach(exportedFromPath => forEachFileAndExportsOfFile(state, exportedFromPath, seenFileAndExportsOfFile, fn));
+
+ // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
+ state.exportedModulesMap!.getKeys(filePath)?.forEach(exportedFromPath =>
+ // If the cache had an updated value, skip
+ !state.currentAffectedFilesExportedModulesMap!.hasKey(exportedFromPath) &&
+ !state.currentAffectedFilesExportedModulesMap!.deletedKeys()?.has(exportedFromPath) &&
+ forEachFileAndExportsOfFile(state, exportedFromPath, seenFileAndExportsOfFile, fn));
+
+ // Remove diagnostics of files that import this file (without going to exports of referencing files)
+ state.referencedMap!.getKeys(filePath)?.forEach(referencingFilePath => !seenFileAndExportsOfFile.has(referencingFilePath) && // Not already removed diagnostic file
+ fn(state, referencingFilePath) // Dont add to seen since this is not yet done with the export removal
+ );
+}
+
+
+/**
+ * This is called after completing operation on the next affected file.
+ * The operations here are postponed to ensure that cancellation during the iteration is handled correctly
+ */
+/* @internal */
+function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program, emitKind?: BuilderFileEmit, isPendingEmit?: boolean, isBuildInfoEmit?: boolean) {
+ if (isBuildInfoEmit) {
+ state.buildInfoEmitPending = false;
+ }
+ else if (affected === state.program) {
+ state.changedFilesSet.clear();
+ state.programEmitComplete = true;
+ }
+ else {
+ state.seenAffectedFiles!.add((affected as SourceFile).resolvedPath);
+ if (emitKind !== undefined) {
+ (state.seenEmittedFiles || (state.seenEmittedFiles = new ts.Map())).set((affected as SourceFile).resolvedPath, emitKind);
}
- else if (affected === state.program) {
- state.changedFilesSet.clear();
- state.programEmitComplete = true;
+ if (isPendingEmit) {
+ state.affectedFilesPendingEmitIndex!++;
+ state.buildInfoEmitPending = true;
}
else {
- state.seenAffectedFiles!.add((affected as SourceFile).resolvedPath);
- if (emitKind !== undefined) {
- (state.seenEmittedFiles || (state.seenEmittedFiles = new Map())).set((affected as SourceFile).resolvedPath, emitKind);
- }
- if (isPendingEmit) {
- state.affectedFilesPendingEmitIndex!++;
- state.buildInfoEmitPending = true;
- }
- else {
- state.affectedFilesIndex!++;
- }
+ state.affectedFilesIndex!++;
}
}
+}
- /**
- * Returns the result with affected file
- */
- function toAffectedFileResult(state: BuilderProgramState, result: T, affected: SourceFile | Program): AffectedFileResult {
- doneWithAffectedFile(state, affected);
- return { result, affected };
- }
-
- /**
- * Returns the result with affected file
- */
- function toAffectedFileEmitResult(
- state: BuilderProgramState,
- result: EmitResult,
- affected: SourceFile | Program,
- emitKind: BuilderFileEmit,
- isPendingEmit?: boolean,
- isBuildInfoEmit?: boolean
- ): AffectedFileResult {
- doneWithAffectedFile(state, affected, emitKind, isPendingEmit, isBuildInfoEmit);
- return { result, affected };
- }
+/**
+ * Returns the result with affected file
+ */
+/* @internal */
+function toAffectedFileResult(state: BuilderProgramState, result: T, affected: SourceFile | Program): AffectedFileResult {
+ doneWithAffectedFile(state, affected);
+ return { result, affected };
+}
- /**
- * Gets semantic diagnostics for the file which are
- * bindAndCheckDiagnostics (from cache) and program diagnostics
- */
- function getSemanticDiagnosticsOfFile(state: BuilderProgramState, sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
- return concatenate(
- getBinderAndCheckerDiagnosticsOfFile(state, sourceFile, cancellationToken),
- Debug.checkDefined(state.program).getProgramDiagnostics(sourceFile)
- );
- }
+/**
+ * Returns the result with affected file
+ */
+/* @internal */
+function toAffectedFileEmitResult(state: BuilderProgramState, result: EmitResult, affected: SourceFile | Program, emitKind: BuilderFileEmit, isPendingEmit?: boolean, isBuildInfoEmit?: boolean): AffectedFileResult {
+ doneWithAffectedFile(state, affected, emitKind, isPendingEmit, isBuildInfoEmit);
+ return { result, affected };
+}
- /**
- * Gets the binder and checker diagnostics either from cache if present, or otherwise from program and caches it
- * Note that it is assumed that when asked about binder and checker diagnostics, the file has been taken out of affected files/changed file set
- */
- function getBinderAndCheckerDiagnosticsOfFile(state: BuilderProgramState, sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
- const path = sourceFile.resolvedPath;
- if (state.semanticDiagnosticsPerFile) {
- const cachedDiagnostics = state.semanticDiagnosticsPerFile.get(path);
- // Report the bind and check diagnostics from the cache if we already have those diagnostics present
- if (cachedDiagnostics) {
- return filterSemanticDiagnostics(cachedDiagnostics, state.compilerOptions);
- }
- }
+/**
+ * Gets semantic diagnostics for the file which are
+ * bindAndCheckDiagnostics (from cache) and program diagnostics
+ */
+/* @internal */
+function getSemanticDiagnosticsOfFile(state: BuilderProgramState, sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
+ return concatenate(getBinderAndCheckerDiagnosticsOfFile(state, sourceFile, cancellationToken), Debug.checkDefined(state.program).getProgramDiagnostics(sourceFile));
+}
- // Diagnostics werent cached, get them from program, and cache the result
- const diagnostics = Debug.checkDefined(state.program).getBindAndCheckDiagnostics(sourceFile, cancellationToken);
- if (state.semanticDiagnosticsPerFile) {
- state.semanticDiagnosticsPerFile.set(path, diagnostics);
+/**
+ * Gets the binder and checker diagnostics either from cache if present, or otherwise from program and caches it
+ * Note that it is assumed that when asked about binder and checker diagnostics, the file has been taken out of affected files/changed file set
+ */
+/* @internal */
+function getBinderAndCheckerDiagnosticsOfFile(state: BuilderProgramState, sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
+ const path = sourceFile.resolvedPath;
+ if (state.semanticDiagnosticsPerFile) {
+ const cachedDiagnostics = state.semanticDiagnosticsPerFile.get(path);
+ // Report the bind and check diagnostics from the cache if we already have those diagnostics present
+ if (cachedDiagnostics) {
+ return filterSemanticDiagnostics(cachedDiagnostics, state.compilerOptions);
}
- return filterSemanticDiagnostics(diagnostics, state.compilerOptions);
}
- export type ProgramBuildInfoFileId = number & { __programBuildInfoFileIdBrand: any };
- export type ProgramBuildInfoFileIdListId = number & { __programBuildInfoFileIdListIdBrand: any };
- export type ProgramBuildInfoDiagnostic = ProgramBuildInfoFileId | [fileId: ProgramBuildInfoFileId, diagnostics: readonly ReusableDiagnostic[]];
- export type ProgramBuilderInfoFilePendingEmit = [fileId: ProgramBuildInfoFileId, emitKind: BuilderFileEmit];
- export type ProgramBuildInfoReferencedMap = [fileId: ProgramBuildInfoFileId, fileIdListId: ProgramBuildInfoFileIdListId][];
- export type ProgramBuildInfoBuilderStateFileInfo = Omit & {
- /**
- * Signature is
- * - undefined if FileInfo.version === FileInfo.signature
- * - false if FileInfo has signature as undefined (not calculated)
- * - string actual signature
- */
- signature: string | false | undefined;
- };
- /**
- * ProgramBuildInfoFileInfo is string if FileInfo.version === FileInfo.signature && !FileInfo.affectsGlobalScope otherwise encoded FileInfo
- */
- export type ProgramBuildInfoFileInfo = string | ProgramBuildInfoBuilderStateFileInfo;
- export interface ProgramBuildInfo {
- fileNames: readonly string[];
- fileInfos: readonly ProgramBuildInfoFileInfo[];
- options: CompilerOptions | undefined;
- fileIdsList?: readonly (readonly ProgramBuildInfoFileId[])[];
- referencedMap?: ProgramBuildInfoReferencedMap;
- exportedModulesMap?: ProgramBuildInfoReferencedMap;
- semanticDiagnosticsPerFile?: ProgramBuildInfoDiagnostic[];
- affectedFilesPendingEmit?: ProgramBuilderInfoFilePendingEmit[];
+ // Diagnostics werent cached, get them from program, and cache the result
+ const diagnostics = Debug.checkDefined(state.program).getBindAndCheckDiagnostics(sourceFile, cancellationToken);
+ if (state.semanticDiagnosticsPerFile) {
+ state.semanticDiagnosticsPerFile.set(path, diagnostics);
}
+ return filterSemanticDiagnostics(diagnostics, state.compilerOptions);
+}
+/* @internal */
+export type ProgramBuildInfoFileId = number & {
+ __programBuildInfoFileIdBrand: any;
+};
+/* @internal */
+export type ProgramBuildInfoFileIdListId = number & {
+ __programBuildInfoFileIdListIdBrand: any;
+};
+/* @internal */
+export type ProgramBuildInfoDiagnostic = ProgramBuildInfoFileId | [
+ fileId: ProgramBuildInfoFileId,
+ diagnostics: readonly ReusableDiagnostic[]
+];
+/* @internal */
+export type ProgramBuilderInfoFilePendingEmit = [
+ fileId: ProgramBuildInfoFileId,
+ emitKind: BuilderFileEmit
+];
+/* @internal */
+export type ProgramBuildInfoReferencedMap = [
+ fileId: ProgramBuildInfoFileId,
+ fileIdListId: ProgramBuildInfoFileIdListId
+][];
+/* @internal */
+export type ProgramBuildInfoBuilderStateFileInfo = Omit & {
/**
- * Gets the program information to be emitted in buildInfo so that we can use it to create new program
+ * Signature is
+ * - undefined if FileInfo.version === FileInfo.signature
+ * - false if FileInfo has signature as undefined (not calculated)
+ * - string actual signature
*/
- function getProgramBuildInfo(state: Readonly, getCanonicalFileName: GetCanonicalFileName): ProgramBuildInfo | undefined {
- if (outFile(state.compilerOptions)) return undefined;
- const currentDirectory = Debug.checkDefined(state.program).getCurrentDirectory();
- const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(state.compilerOptions)!, currentDirectory));
- const fileNames: string[] = [];
- const fileNameToFileId = new Map();
- let fileIdsList: (readonly ProgramBuildInfoFileId[])[] | undefined;
- let fileNamesToFileIdListId: ESMap | undefined;
- const fileInfos = arrayFrom(state.fileInfos.entries(), ([key, value]): ProgramBuildInfoFileInfo => {
- // Ensure fileId
- const fileId = toFileId(key);
- Debug.assert(fileNames[fileId - 1] === relativeToBuildInfo(key));
- const signature = state.currentAffectedFilesSignatures && state.currentAffectedFilesSignatures.get(key);
- const actualSignature = signature ?? value.signature;
- return value.version === actualSignature ?
- value.affectsGlobalScope ?
- { version: value.version, signature: undefined, affectsGlobalScope: true, impliedFormat: value.impliedFormat } :
- value.version :
- actualSignature !== undefined ?
- signature === undefined ?
- value :
- { version: value.version, signature, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat } :
- { version: value.version, signature: false, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat };
- });
+ signature: string | false | undefined;
+};
+/**
+ * ProgramBuildInfoFileInfo is string if FileInfo.version === FileInfo.signature && !FileInfo.affectsGlobalScope otherwise encoded FileInfo
+ */
+/* @internal */
+export type ProgramBuildInfoFileInfo = string | ProgramBuildInfoBuilderStateFileInfo;
+/* @internal */
+export interface ProgramBuildInfo {
+ fileNames: readonly string[];
+ fileInfos: readonly ProgramBuildInfoFileInfo[];
+ options: CompilerOptions | undefined;
+ fileIdsList?: readonly (readonly ProgramBuildInfoFileId[])[];
+ referencedMap?: ProgramBuildInfoReferencedMap;
+ exportedModulesMap?: ProgramBuildInfoReferencedMap;
+ semanticDiagnosticsPerFile?: ProgramBuildInfoDiagnostic[];
+ affectedFilesPendingEmit?: ProgramBuilderInfoFilePendingEmit[];
+}
- let referencedMap: ProgramBuildInfoReferencedMap | undefined;
- if (state.referencedMap) {
- referencedMap = arrayFrom(state.referencedMap.keys()).sort(compareStringsCaseSensitive).map(key => [
- toFileId(key),
- toFileIdListId(state.referencedMap!.getValues(key)!)
- ]);
- }
+/**
+ * Gets the program information to be emitted in buildInfo so that we can use it to create new program
+ */
+/* @internal */
+function getProgramBuildInfo(state: Readonly, getCanonicalFileName: GetCanonicalFileName): ProgramBuildInfo | undefined {
+ if (outFile(state.compilerOptions))
+ return undefined;
+ const currentDirectory = Debug.checkDefined(state.program).getCurrentDirectory();
+ const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(state.compilerOptions)!, currentDirectory));
+ const fileNames: string[] = [];
+ const fileNameToFileId = new ts.Map();
+ let fileIdsList: (readonly ProgramBuildInfoFileId[])[] | undefined;
+ let fileNamesToFileIdListId: ESMap | undefined;
+ const fileInfos = arrayFrom(state.fileInfos.entries(), ([key, value]): ProgramBuildInfoFileInfo => {
+ // Ensure fileId
+ const fileId = toFileId(key);
+ Debug.assert(fileNames[fileId - 1] === relativeToBuildInfo(key));
+ const signature = state.currentAffectedFilesSignatures && state.currentAffectedFilesSignatures.get(key);
+ const actualSignature = signature ?? value.signature;
+ return value.version === actualSignature ?
+ value.affectsGlobalScope ?
+ { version: value.version, signature: undefined, affectsGlobalScope: true, impliedFormat: value.impliedFormat } :
+ value.version :
+ actualSignature !== undefined ?
+ signature === undefined ?
+ value :
+ { version: value.version, signature, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat } :
+ { version: value.version, signature: false, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat };
+ });
+
+ let referencedMap: ProgramBuildInfoReferencedMap | undefined;
+ if (state.referencedMap) {
+ referencedMap = arrayFrom(state.referencedMap.keys()).sort(compareStringsCaseSensitive).map(key => [
+ toFileId(key),
+ toFileIdListId(state.referencedMap!.getValues(key)!)
+ ]);
+ }
- let exportedModulesMap: ProgramBuildInfoReferencedMap | undefined;
- if (state.exportedModulesMap) {
- exportedModulesMap = mapDefined(arrayFrom(state.exportedModulesMap.keys()).sort(compareStringsCaseSensitive), key => {
- if (state.currentAffectedFilesExportedModulesMap) {
- if (state.currentAffectedFilesExportedModulesMap.deletedKeys()?.has(key)) {
- return undefined;
- }
+ let exportedModulesMap: ProgramBuildInfoReferencedMap | undefined;
+ if (state.exportedModulesMap) {
+ exportedModulesMap = mapDefined(arrayFrom(state.exportedModulesMap.keys()).sort(compareStringsCaseSensitive), key => {
+ if (state.currentAffectedFilesExportedModulesMap) {
+ if (state.currentAffectedFilesExportedModulesMap.deletedKeys()?.has(key)) {
+ return undefined;
+ }
- const newValue = state.currentAffectedFilesExportedModulesMap.getValues(key);
- if (newValue) {
- return [toFileId(key), toFileIdListId(newValue)];
- }
+ const newValue = state.currentAffectedFilesExportedModulesMap.getValues(key);
+ if (newValue) {
+ return [toFileId(key), toFileIdListId(newValue)];
}
+ }
- // Not in temporary cache, use existing value
- return [toFileId(key), toFileIdListId(state.exportedModulesMap!.getValues(key)!)];
- });
- }
+ // Not in temporary cache, use existing value
+ return [toFileId(key), toFileIdListId(state.exportedModulesMap!.getValues(key)!)];
+ });
+ }
- let semanticDiagnosticsPerFile: ProgramBuildInfoDiagnostic[] | undefined;
- if (state.semanticDiagnosticsPerFile) {
- for (const key of arrayFrom(state.semanticDiagnosticsPerFile.keys()).sort(compareStringsCaseSensitive)) {
- const value = state.semanticDiagnosticsPerFile.get(key)!;
- (semanticDiagnosticsPerFile ||= []).push(
- value.length ?
- [
- toFileId(key),
- state.hasReusableDiagnostic ?
- value as readonly ReusableDiagnostic[] :
- convertToReusableDiagnostics(value as readonly Diagnostic[], relativeToBuildInfo)
- ] :
- toFileId(key)
- );
- }
+ let semanticDiagnosticsPerFile: ProgramBuildInfoDiagnostic[] | undefined;
+ if (state.semanticDiagnosticsPerFile) {
+ for (const key of arrayFrom(state.semanticDiagnosticsPerFile.keys()).sort(compareStringsCaseSensitive)) {
+ const value = state.semanticDiagnosticsPerFile.get(key)!;
+ (semanticDiagnosticsPerFile ||= []).push(value.length ?
+ [
+ toFileId(key),
+ state.hasReusableDiagnostic ?
+ value as readonly ReusableDiagnostic[] :
+ convertToReusableDiagnostics(value as readonly Diagnostic[], relativeToBuildInfo)
+ ] :
+ toFileId(key));
}
+ }
- let affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] | undefined;
- if (state.affectedFilesPendingEmit) {
- const seenFiles = new Set();
- for (const path of state.affectedFilesPendingEmit.slice(state.affectedFilesPendingEmitIndex).sort(compareStringsCaseSensitive)) {
- if (tryAddToSet(seenFiles, path)) {
- (affectedFilesPendingEmit ||= []).push([toFileId(path), state.affectedFilesPendingEmitKind!.get(path)!]);
- }
+ let affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] | undefined;
+ if (state.affectedFilesPendingEmit) {
+ const seenFiles = new ts.Set();
+ for (const path of state.affectedFilesPendingEmit.slice(state.affectedFilesPendingEmitIndex).sort(compareStringsCaseSensitive)) {
+ if (tryAddToSet(seenFiles, path)) {
+ (affectedFilesPendingEmit ||= []).push([toFileId(path), state.affectedFilesPendingEmitKind!.get(path)!]);
}
}
+ }
- return {
- fileNames,
- fileInfos,
- options: convertToProgramBuildInfoCompilerOptions(state.compilerOptions, relativeToBuildInfoEnsuringAbsolutePath),
- fileIdsList,
- referencedMap,
- exportedModulesMap,
- semanticDiagnosticsPerFile,
- affectedFilesPendingEmit,
- };
-
- function relativeToBuildInfoEnsuringAbsolutePath(path: string) {
- return relativeToBuildInfo(getNormalizedAbsolutePath(path, currentDirectory));
- }
+ return {
+ fileNames,
+ fileInfos,
+ options: convertToProgramBuildInfoCompilerOptions(state.compilerOptions, relativeToBuildInfoEnsuringAbsolutePath),
+ fileIdsList,
+ referencedMap,
+ exportedModulesMap,
+ semanticDiagnosticsPerFile,
+ affectedFilesPendingEmit,
+ };
- function relativeToBuildInfo(path: string) {
- return ensurePathIsNonModuleName(getRelativePathFromDirectory(buildInfoDirectory, path, getCanonicalFileName));
- }
+ function relativeToBuildInfoEnsuringAbsolutePath(path: string) {
+ return relativeToBuildInfo(getNormalizedAbsolutePath(path, currentDirectory));
+ }
- function toFileId(path: Path): ProgramBuildInfoFileId {
- let fileId = fileNameToFileId.get(path);
- if (fileId === undefined) {
- fileNames.push(relativeToBuildInfo(path));
- fileNameToFileId.set(path, fileId = fileNames.length as ProgramBuildInfoFileId);
- }
- return fileId;
+ function relativeToBuildInfo(path: string) {
+ return ensurePathIsNonModuleName(getRelativePathFromDirectory(buildInfoDirectory, path, getCanonicalFileName));
+ }
+
+ function toFileId(path: Path): ProgramBuildInfoFileId {
+ let fileId = fileNameToFileId.get(path);
+ if (fileId === undefined) {
+ fileNames.push(relativeToBuildInfo(path));
+ fileNameToFileId.set(path, fileId = fileNames.length as ProgramBuildInfoFileId);
}
+ return fileId;
+ }
- function toFileIdListId(set: ReadonlySet): ProgramBuildInfoFileIdListId {
- const fileIds = arrayFrom(set.keys(), toFileId).sort(compareValues);
- const key = fileIds.join();
- let fileIdListId = fileNamesToFileIdListId?.get(key);
- if (fileIdListId === undefined) {
- (fileIdsList ||= []).push(fileIds);
- (fileNamesToFileIdListId ||= new Map()).set(key, fileIdListId = fileIdsList.length as ProgramBuildInfoFileIdListId);
- }
- return fileIdListId;
+ function toFileIdListId(set: ts.ReadonlySet): ProgramBuildInfoFileIdListId {
+ const fileIds = arrayFrom(set.keys(), toFileId).sort(compareValues);
+ const key = fileIds.join();
+ let fileIdListId = fileNamesToFileIdListId?.get(key);
+ if (fileIdListId === undefined) {
+ (fileIdsList ||= []).push(fileIds);
+ (fileNamesToFileIdListId ||= new ts.Map()).set(key, fileIdListId = fileIdsList.length as ProgramBuildInfoFileIdListId);
}
+ return fileIdListId;
}
+}
- function convertToProgramBuildInfoCompilerOptions(options: CompilerOptions, relativeToBuildInfo: (path: string) => string) {
- let result: CompilerOptions | undefined;
- const { optionsNameMap } = getOptionsNameMap();
-
- for (const name of getOwnKeys(options).sort(compareStringsCaseSensitive)) {
- const optionKey = name.toLowerCase();
- const optionInfo = optionsNameMap.get(optionKey);
- if (optionInfo?.affectsEmit || optionInfo?.affectsSemanticDiagnostics ||
- // We need to store `strict`, even though it won't be examined directly, so that the
- // flags it controls (e.g. `strictNullChecks`) will be retrieved correctly from the buildinfo
- optionKey === "strict" ||
- // We need to store these to determine whether `lib` files need to be rechecked.
- optionKey === "skiplibcheck" || optionKey === "skipdefaultlibcheck") {
- (result ||= {})[name] = convertToReusableCompilerOptionValue(
- optionInfo,
- options[name] as CompilerOptionsValue,
- relativeToBuildInfo
- );
- }
+/* @internal */
+function convertToProgramBuildInfoCompilerOptions(options: CompilerOptions, relativeToBuildInfo: (path: string) => string) {
+ let result: CompilerOptions | undefined;
+ const { optionsNameMap } = getOptionsNameMap();
+
+ for (const name of getOwnKeys(options).sort(compareStringsCaseSensitive)) {
+ const optionKey = name.toLowerCase();
+ const optionInfo = optionsNameMap.get(optionKey);
+ if (optionInfo?.affectsEmit || optionInfo?.affectsSemanticDiagnostics ||
+ // We need to store `strict`, even though it won't be examined directly, so that the
+ // flags it controls (e.g. `strictNullChecks`) will be retrieved correctly from the buildinfo
+ optionKey === "strict" ||
+ // We need to store these to determine whether `lib` files need to be rechecked.
+ optionKey === "skiplibcheck" || optionKey === "skipdefaultlibcheck") {
+ (result ||= {})[name] = convertToReusableCompilerOptionValue(optionInfo, options[name] as CompilerOptionsValue, relativeToBuildInfo);
}
- return result;
}
+ return result;
+}
- function convertToReusableCompilerOptionValue(option: CommandLineOption | undefined, value: CompilerOptionsValue, relativeToBuildInfo: (path: string) => string) {
- if (option) {
- if (option.type === "list") {
- const values = value as readonly (string | number)[];
- if (option.element.isFilePath && values.length) {
- return values.map(relativeToBuildInfo);
- }
- }
- else if (option.isFilePath) {
- return relativeToBuildInfo(value as string);
+/* @internal */
+function convertToReusableCompilerOptionValue(option: CommandLineOption | undefined, value: CompilerOptionsValue, relativeToBuildInfo: (path: string) => string) {
+ if (option) {
+ if (option.type === "list") {
+ const values = value as readonly (string | number)[];
+ if (option.element.isFilePath && values.length) {
+ return values.map(relativeToBuildInfo);
}
}
- return value;
+ else if (option.isFilePath) {
+ return relativeToBuildInfo(value as string);
+ }
}
+ return value;
+}
- function convertToReusableDiagnostics(diagnostics: readonly Diagnostic[], relativeToBuildInfo: (path: string) => string): readonly ReusableDiagnostic[] {
- Debug.assert(!!diagnostics.length);
- return diagnostics.map(diagnostic => {
- const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic, relativeToBuildInfo);
- result.reportsUnnecessary = diagnostic.reportsUnnecessary;
- result.reportDeprecated = diagnostic.reportsDeprecated;
- result.source = diagnostic.source;
- result.skippedOn = diagnostic.skippedOn;
- const { relatedInformation } = diagnostic;
- result.relatedInformation = relatedInformation ?
- relatedInformation.length ?
- relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r, relativeToBuildInfo)) :
- [] :
- undefined;
- return result;
- });
- }
+/* @internal */
+function convertToReusableDiagnostics(diagnostics: readonly Diagnostic[], relativeToBuildInfo: (path: string) => string): readonly ReusableDiagnostic[] {
+ Debug.assert(!!diagnostics.length);
+ return diagnostics.map(diagnostic => {
+ const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic, relativeToBuildInfo);
+ result.reportsUnnecessary = diagnostic.reportsUnnecessary;
+ result.reportDeprecated = diagnostic.reportsDeprecated;
+ result.source = diagnostic.source;
+ result.skippedOn = diagnostic.skippedOn;
+ const { relatedInformation } = diagnostic;
+ result.relatedInformation = relatedInformation ?
+ relatedInformation.length ?
+ relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r, relativeToBuildInfo)) :
+ [] :
+ undefined;
+ return result;
+ });
+}
- function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation, relativeToBuildInfo: (path: string) => string): ReusableDiagnosticRelatedInformation {
- const { file } = diagnostic;
- return {
- ...diagnostic,
- file: file ? relativeToBuildInfo(file.resolvedPath) : undefined
- };
- }
+/* @internal */
+function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation, relativeToBuildInfo: (path: string) => string): ReusableDiagnosticRelatedInformation {
+ const { file } = diagnostic;
+ return {
+ ...diagnostic,
+ file: file ? relativeToBuildInfo(file.resolvedPath) : undefined
+ };
+}
- export enum BuilderProgramKind {
- SemanticDiagnosticsBuilderProgram,
- EmitAndSemanticDiagnosticsBuilderProgram
- }
+/* @internal */
+export enum BuilderProgramKind {
+ SemanticDiagnosticsBuilderProgram,
+ EmitAndSemanticDiagnosticsBuilderProgram
+}
- export interface BuilderCreationParameters {
- newProgram: Program;
- host: BuilderProgramHost;
- oldProgram: BuilderProgram | undefined;
- configFileParsingDiagnostics: readonly Diagnostic[];
- }
+/* @internal */
+export interface BuilderCreationParameters {
+ newProgram: Program;
+ host: BuilderProgramHost;
+ oldProgram: BuilderProgram | undefined;
+ configFileParsingDiagnostics: readonly Diagnostic[];
+}
- export function getBuilderCreationParameters(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: BuilderProgram | CompilerHost, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderCreationParameters {
- let host: BuilderProgramHost;
- let newProgram: Program;
- let oldProgram: BuilderProgram;
- if (newProgramOrRootNames === undefined) {
- Debug.assert(hostOrOptions === undefined);
- host = oldProgramOrHost as CompilerHost;
- oldProgram = configFileParsingDiagnosticsOrOldProgram as BuilderProgram;
- Debug.assert(!!oldProgram);
- newProgram = oldProgram.getProgram();
- }
- else if (isArray(newProgramOrRootNames)) {
- oldProgram = configFileParsingDiagnosticsOrOldProgram as BuilderProgram;
- newProgram = createProgram({
- rootNames: newProgramOrRootNames,
- options: hostOrOptions as CompilerOptions,
- host: oldProgramOrHost as CompilerHost,
- oldProgram: oldProgram && oldProgram.getProgramOrUndefined(),
- configFileParsingDiagnostics,
- projectReferences
- });
- host = oldProgramOrHost as CompilerHost;
- }
- else {
- newProgram = newProgramOrRootNames;
- host = hostOrOptions as BuilderProgramHost;
- oldProgram = oldProgramOrHost as BuilderProgram;
- configFileParsingDiagnostics = configFileParsingDiagnosticsOrOldProgram as readonly Diagnostic[];
- }
- return { host, newProgram, oldProgram, configFileParsingDiagnostics: configFileParsingDiagnostics || emptyArray };
+/* @internal */
+export function getBuilderCreationParameters(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: BuilderProgram | CompilerHost, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderCreationParameters {
+ let host: BuilderProgramHost;
+ let newProgram: Program;
+ let oldProgram: BuilderProgram;
+ if (newProgramOrRootNames === undefined) {
+ Debug.assert(hostOrOptions === undefined);
+ host = oldProgramOrHost as CompilerHost;
+ oldProgram = configFileParsingDiagnosticsOrOldProgram as BuilderProgram;
+ Debug.assert(!!oldProgram);
+ newProgram = oldProgram.getProgram();
}
+ else if (isArray(newProgramOrRootNames)) {
+ oldProgram = configFileParsingDiagnosticsOrOldProgram as BuilderProgram;
+ newProgram = createProgram({
+ rootNames: newProgramOrRootNames,
+ options: hostOrOptions as CompilerOptions,
+ host: oldProgramOrHost as CompilerHost,
+ oldProgram: oldProgram && oldProgram.getProgramOrUndefined(),
+ configFileParsingDiagnostics,
+ projectReferences
+ });
+ host = oldProgramOrHost as CompilerHost;
+ }
+ else {
+ newProgram = newProgramOrRootNames;
+ host = hostOrOptions as BuilderProgramHost;
+ oldProgram = oldProgramOrHost as BuilderProgram;
+ configFileParsingDiagnostics = configFileParsingDiagnosticsOrOldProgram as readonly Diagnostic[];
+ }
+ return { host, newProgram, oldProgram, configFileParsingDiagnostics: configFileParsingDiagnostics || emptyArray };
+}
- export function createBuilderProgram(kind: BuilderProgramKind.SemanticDiagnosticsBuilderProgram, builderCreationParameters: BuilderCreationParameters): SemanticDiagnosticsBuilderProgram;
- export function createBuilderProgram(kind: BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, builderCreationParameters: BuilderCreationParameters): EmitAndSemanticDiagnosticsBuilderProgram;
- export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, host, oldProgram, configFileParsingDiagnostics }: BuilderCreationParameters) {
- // Return same program if underlying program doesnt change
- let oldState = oldProgram && oldProgram.getState();
- if (oldState && newProgram === oldState.program && configFileParsingDiagnostics === newProgram.getConfigFileParsingDiagnostics()) {
- newProgram = undefined!; // TODO: GH#18217
- oldState = undefined;
- return oldProgram;
- }
-
- /**
- * Create the canonical file name for identity
- */
- const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
- /**
- * Computing hash to for signature verification
- */
- const computeHash = maybeBind(host, host.createHash);
- let state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState, host.disableUseFileVersionAsSignature);
- let backupState: BuilderProgramState | undefined;
- newProgram.getProgramBuildInfo = () => getProgramBuildInfo(state, getCanonicalFileName);
-
- // To ensure that we arent storing any references to old program or new program without state
+/* @internal */
+export function createBuilderProgram(kind: BuilderProgramKind.SemanticDiagnosticsBuilderProgram, builderCreationParameters: BuilderCreationParameters): SemanticDiagnosticsBuilderProgram;
+/* @internal */
+export function createBuilderProgram(kind: BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, builderCreationParameters: BuilderCreationParameters): EmitAndSemanticDiagnosticsBuilderProgram;
+/* @internal */
+export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, host, oldProgram, configFileParsingDiagnostics }: BuilderCreationParameters) {
+ // Return same program if underlying program doesnt change
+ let oldState = oldProgram && oldProgram.getState();
+ if (oldState && newProgram === oldState.program && configFileParsingDiagnostics === newProgram.getConfigFileParsingDiagnostics()) {
newProgram = undefined!; // TODO: GH#18217
- oldProgram = undefined;
oldState = undefined;
+ return oldProgram;
+ }
- const getState = () => state;
- const builderProgram = createRedirectedBuilderProgram(getState, configFileParsingDiagnostics);
- builderProgram.getState = getState;
- builderProgram.backupState = () => {
- Debug.assert(backupState === undefined);
- backupState = cloneBuilderProgramState(state);
- };
- builderProgram.restoreState = () => {
- state = Debug.checkDefined(backupState);
- backupState = undefined;
- };
- builderProgram.getAllDependencies = sourceFile => BuilderState.getAllDependencies(state, Debug.checkDefined(state.program), sourceFile);
- builderProgram.getSemanticDiagnostics = getSemanticDiagnostics;
- builderProgram.emit = emit;
- builderProgram.releaseProgram = () => {
- releaseCache(state);
- backupState = undefined;
- };
-
- if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) {
- (builderProgram as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
- }
- else if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
- (builderProgram as EmitAndSemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
- (builderProgram as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile;
- builderProgram.emitBuildInfo = emitBuildInfo;
- }
- else {
- notImplemented();
- }
+ /**
+ * Create the canonical file name for identity
+ */
+ const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
+ /**
+ * Computing hash to for signature verification
+ */
+ const computeHash = maybeBind(host, host.createHash);
+ let state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState, host.disableUseFileVersionAsSignature);
+ let backupState: BuilderProgramState | undefined;
+ newProgram.getProgramBuildInfo = () => getProgramBuildInfo(state, getCanonicalFileName);
+
+ // To ensure that we arent storing any references to old program or new program without state
+ newProgram = undefined!; // TODO: GH#18217
+ oldProgram = undefined;
+ oldState = undefined;
+
+ const getState = () => state;
+ const builderProgram = createRedirectedBuilderProgram(getState, configFileParsingDiagnostics);
+ builderProgram.getState = getState;
+ builderProgram.backupState = () => {
+ Debug.assert(backupState === undefined);
+ backupState = cloneBuilderProgramState(state);
+ };
+ builderProgram.restoreState = () => {
+ state = Debug.checkDefined(backupState);
+ backupState = undefined;
+ };
+ builderProgram.getAllDependencies = sourceFile => BuilderState.getAllDependencies(state, Debug.checkDefined(state.program), sourceFile);
+ builderProgram.getSemanticDiagnostics = getSemanticDiagnostics;
+ builderProgram.emit = emit;
+ builderProgram.releaseProgram = () => {
+ releaseCache(state);
+ backupState = undefined;
+ };
+
+ if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) {
+ (builderProgram as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
+ }
+ else if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
+ (builderProgram as EmitAndSemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile;
+ (builderProgram as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile;
+ builderProgram.emitBuildInfo = emitBuildInfo;
+ }
+ else {
+ notImplemented();
+ }
- return builderProgram;
+ return builderProgram;
- function emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult {
- if (state.buildInfoEmitPending) {
- const result = Debug.checkDefined(state.program).emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken);
- state.buildInfoEmitPending = false;
- return result;
- }
- return emitSkippedWithNoDiagnostics;
+ function emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult {
+ if (state.buildInfoEmitPending) {
+ const result = Debug.checkDefined(state.program).emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken);
+ state.buildInfoEmitPending = false;
+ return result;
}
+ return emitSkippedWithNoDiagnostics;
+ }
- /**
- * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
- * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
- * in that order would be used to write the files
- */
- function emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult {
- let affected = getNextAffectedFile(state, cancellationToken, computeHash);
- let emitKind = BuilderFileEmit.Full;
- let isPendingEmitFile = false;
- if (!affected) {
- if (!outFile(state.compilerOptions)) {
- const pendingAffectedFile = getNextAffectedFilePendingEmit(state);
- if (!pendingAffectedFile) {
- if (!state.buildInfoEmitPending) {
- return undefined;
- }
-
- const affected = Debug.checkDefined(state.program);
- return toAffectedFileEmitResult(
- state,
- // When whole program is affected, do emit only once (eg when --out or --outFile is specified)
- // Otherwise just affected file
- affected.emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken),
- affected,
- /*emitKind*/ BuilderFileEmit.Full,
- /*isPendingEmitFile*/ false,
- /*isBuildInfoEmit*/ true
- );
+ /**
+ * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
+ * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
+ * in that order would be used to write the files
+ */
+ function emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult {
+ let affected = getNextAffectedFile(state, cancellationToken, computeHash);
+ let emitKind = BuilderFileEmit.Full;
+ let isPendingEmitFile = false;
+ if (!affected) {
+ if (!outFile(state.compilerOptions)) {
+ const pendingAffectedFile = getNextAffectedFilePendingEmit(state);
+ if (!pendingAffectedFile) {
+ if (!state.buildInfoEmitPending) {
+ return undefined;
}
- ({ affectedFile: affected, emitKind } = pendingAffectedFile);
- isPendingEmitFile = true;
- }
- else {
- const program = Debug.checkDefined(state.program);
- if (state.programEmitComplete) return undefined;
- affected = program;
- }
- }
- return toAffectedFileEmitResult(
- state,
- // When whole program is affected, do emit only once (eg when --out or --outFile is specified)
- // Otherwise just affected file
- Debug.checkDefined(state.program).emit(
- affected === state.program ? undefined : affected as SourceFile,
- writeFile || maybeBind(host, host.writeFile),
- cancellationToken,
- emitOnlyDtsFiles || emitKind === BuilderFileEmit.DtsOnly,
- customTransformers
- ),
- affected,
- emitKind,
- isPendingEmitFile,
- );
- }
-
- /**
- * Emits the JavaScript and declaration files.
- * When targetSource file is specified, emits the files corresponding to that source file,
- * otherwise for the whole program.
- * In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified,
- * it is assumed that that file is handled from affected file list. If targetSourceFile is not specified,
- * it will only emit all the affected files instead of whole program
- *
- * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
- * in that order would be used to write the files
- */
- function emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult {
- let restorePendingEmitOnHandlingNoEmitSuccess = false;
- let savedAffectedFilesPendingEmit;
- let savedAffectedFilesPendingEmitKind;
- let savedAffectedFilesPendingEmitIndex;
- // Backup and restore affected pendings emit state for non emit Builder if noEmitOnError is enabled and emitBuildInfo could be written in case there are errors
- // This ensures pending files to emit is updated in tsbuildinfo
- // Note that when there are no errors, emit proceeds as if everything is emitted as it is callers reponsibility to write the files to disk if at all (because its builder that doesnt track files to emit)
- if (kind !== BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram &&
- !targetSourceFile &&
- !outFile(state.compilerOptions) &&
- !state.compilerOptions.noEmit &&
- state.compilerOptions.noEmitOnError) {
- restorePendingEmitOnHandlingNoEmitSuccess = true;
- savedAffectedFilesPendingEmit = state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice();
- savedAffectedFilesPendingEmitKind = state.affectedFilesPendingEmitKind && new Map(state.affectedFilesPendingEmitKind);
- savedAffectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex;
+ const affected = Debug.checkDefined(state.program);
+ return toAffectedFileEmitResult(state,
+ // When whole program is affected, do emit only once (eg when --out or --outFile is specified)
+ // Otherwise just affected file
+ affected.emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken), affected,
+ /*emitKind*/ BuilderFileEmit.Full,
+ /*isPendingEmitFile*/ false,
+ /*isBuildInfoEmit*/ true);
+ }
+ ({ affectedFile: affected, emitKind } = pendingAffectedFile);
+ isPendingEmitFile = true;
}
-
- if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
- assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile);
+ else {
+ const program = Debug.checkDefined(state.program);
+ if (state.programEmitComplete)
+ return undefined;
+ affected = program;
}
- const result = handleNoEmitOptions(builderProgram, targetSourceFile, writeFile, cancellationToken);
- if (result) return result;
+ }
- if (restorePendingEmitOnHandlingNoEmitSuccess) {
- state.affectedFilesPendingEmit = savedAffectedFilesPendingEmit;
- state.affectedFilesPendingEmitKind = savedAffectedFilesPendingEmitKind;
- state.affectedFilesPendingEmitIndex = savedAffectedFilesPendingEmitIndex;
- }
+ return toAffectedFileEmitResult(state,
+ // When whole program is affected, do emit only once (eg when --out or --outFile is specified)
+ // Otherwise just affected file
+ Debug.checkDefined(state.program).emit(affected === state.program ? undefined : affected as SourceFile, writeFile || maybeBind(host, host.writeFile), cancellationToken, emitOnlyDtsFiles || emitKind === BuilderFileEmit.DtsOnly, customTransformers), affected, emitKind, isPendingEmitFile);
+ }
- // Emit only affected files if using builder for emit
- if (!targetSourceFile && kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
- // Emit and report any errors we ran into.
- let sourceMaps: SourceMapEmitResult[] = [];
- let emitSkipped = false;
- let diagnostics: Diagnostic[] | undefined;
- let emittedFiles: string[] = [];
-
- let affectedEmitResult: AffectedFileResult;
- while (affectedEmitResult = emitNextAffectedFile(writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers)) {
- emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped;
- diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics);
- emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles);
- sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps);
- }
- return {
- emitSkipped,
- diagnostics: diagnostics || emptyArray,
- emittedFiles,
- sourceMaps
- };
- }
- return Debug.checkDefined(state.program).emit(targetSourceFile, writeFile || maybeBind(host, host.writeFile), cancellationToken, emitOnlyDtsFiles, customTransformers);
+ /**
+ * Emits the JavaScript and declaration files.
+ * When targetSource file is specified, emits the files corresponding to that source file,
+ * otherwise for the whole program.
+ * In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified,
+ * it is assumed that that file is handled from affected file list. If targetSourceFile is not specified,
+ * it will only emit all the affected files instead of whole program
+ *
+ * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
+ * in that order would be used to write the files
+ */
+ function emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult {
+ let restorePendingEmitOnHandlingNoEmitSuccess = false;
+ let savedAffectedFilesPendingEmit;
+ let savedAffectedFilesPendingEmitKind;
+ let savedAffectedFilesPendingEmitIndex;
+ // Backup and restore affected pendings emit state for non emit Builder if noEmitOnError is enabled and emitBuildInfo could be written in case there are errors
+ // This ensures pending files to emit is updated in tsbuildinfo
+ // Note that when there are no errors, emit proceeds as if everything is emitted as it is callers reponsibility to write the files to disk if at all (because its builder that doesnt track files to emit)
+ if (kind !== BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram &&
+ !targetSourceFile &&
+ !outFile(state.compilerOptions) &&
+ !state.compilerOptions.noEmit &&
+ state.compilerOptions.noEmitOnError) {
+ restorePendingEmitOnHandlingNoEmitSuccess = true;
+ savedAffectedFilesPendingEmit = state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice();
+ savedAffectedFilesPendingEmitKind = state.affectedFilesPendingEmitKind && new ts.Map(state.affectedFilesPendingEmitKind);
+ savedAffectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex;
}
- /**
- * Return the semantic diagnostics for the next affected file or undefined if iteration is complete
- * If provided ignoreSourceFile would be called before getting the diagnostics and would ignore the sourceFile if the returned value was true
- */
- function getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult {
- while (true) {
- const affected = getNextAffectedFile(state, cancellationToken, computeHash);
- if (!affected) {
- // Done
- return undefined;
- }
- else if (affected === state.program) {
- // When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified)
- return toAffectedFileResult(
- state,
- state.program.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken),
- affected
- );
- }
-
- // Add file to affected file pending emit to handle for later emit time
- // Apart for emit builder do this for tsbuildinfo, do this for non emit builder when noEmit is set as tsbuildinfo is written and reused between emitters
- if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram || state.compilerOptions.noEmit || state.compilerOptions.noEmitOnError) {
- addToAffectedFilesPendingEmit(state, (affected as SourceFile).resolvedPath, BuilderFileEmit.Full);
- }
+ if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
+ assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile);
+ }
+ const result = handleNoEmitOptions(builderProgram, targetSourceFile, writeFile, cancellationToken);
+ if (result)
+ return result;
- // Get diagnostics for the affected file if its not ignored
- if (ignoreSourceFile && ignoreSourceFile(affected as SourceFile)) {
- // Get next affected file
- doneWithAffectedFile(state, affected);
- continue;
- }
+ if (restorePendingEmitOnHandlingNoEmitSuccess) {
+ state.affectedFilesPendingEmit = savedAffectedFilesPendingEmit;
+ state.affectedFilesPendingEmitKind = savedAffectedFilesPendingEmitKind;
+ state.affectedFilesPendingEmitIndex = savedAffectedFilesPendingEmitIndex;
+ }
- return toAffectedFileResult(
- state,
- getSemanticDiagnosticsOfFile(state, affected as SourceFile, cancellationToken),
- affected
- );
+ // Emit only affected files if using builder for emit
+ if (!targetSourceFile && kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
+ // Emit and report any errors we ran into.
+ let sourceMaps: SourceMapEmitResult[] = [];
+ let emitSkipped = false;
+ let diagnostics: Diagnostic[] | undefined;
+ let emittedFiles: string[] = [];
+
+ let affectedEmitResult: AffectedFileResult;
+ while (affectedEmitResult = emitNextAffectedFile(writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers)) {
+ emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped;
+ diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics);
+ emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles);
+ sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps);
}
+ return {
+ emitSkipped,
+ diagnostics: diagnostics || emptyArray,
+ emittedFiles,
+ sourceMaps
+ };
}
+ return Debug.checkDefined(state.program).emit(targetSourceFile, writeFile || maybeBind(host, host.writeFile), cancellationToken, emitOnlyDtsFiles, customTransformers);
+ }
- /**
- * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
- * The semantic diagnostics are cached and managed here
- * Note that it is assumed that when asked about semantic diagnostics through this API,
- * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
- * In case of SemanticDiagnosticsBuilderProgram if the source file is not provided,
- * it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics
- */
- function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
- assertSourceFileOkWithoutNextAffectedCall(state, sourceFile);
- const compilerOptions = Debug.checkDefined(state.program).getCompilerOptions();
- if (outFile(compilerOptions)) {
- Debug.assert(!state.semanticDiagnosticsPerFile);
- // We dont need to cache the diagnostics just return them from program
- return Debug.checkDefined(state.program).getSemanticDiagnostics(sourceFile, cancellationToken);
+ /**
+ * Return the semantic diagnostics for the next affected file or undefined if iteration is complete
+ * If provided ignoreSourceFile would be called before getting the diagnostics and would ignore the sourceFile if the returned value was true
+ */
+ function getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult {
+ while (true) {
+ const affected = getNextAffectedFile(state, cancellationToken, computeHash);
+ if (!affected) {
+ // Done
+ return undefined;
}
-
- if (sourceFile) {
- return getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken);
+ else if (affected === state.program) {
+ // When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified)
+ return toAffectedFileResult(state, state.program.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken), affected);
}
- // When semantic builder asks for diagnostics of the whole program,
- // ensure that all the affected files are handled
- // eslint-disable-next-line no-empty
- while (getSemanticDiagnosticsOfNextAffectedFile(cancellationToken)) {
+ // Add file to affected file pending emit to handle for later emit time
+ // Apart for emit builder do this for tsbuildinfo, do this for non emit builder when noEmit is set as tsbuildinfo is written and reused between emitters
+ if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram || state.compilerOptions.noEmit || state.compilerOptions.noEmitOnError) {
+ addToAffectedFilesPendingEmit(state, (affected as SourceFile).resolvedPath, BuilderFileEmit.Full);
}
- let diagnostics: Diagnostic[] | undefined;
- for (const sourceFile of Debug.checkDefined(state.program).getSourceFiles()) {
- diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken));
+ // Get diagnostics for the affected file if its not ignored
+ if (ignoreSourceFile && ignoreSourceFile(affected as SourceFile)) {
+ // Get next affected file
+ doneWithAffectedFile(state, affected);
+ continue;
}
- return diagnostics || emptyArray;
+
+ return toAffectedFileResult(state, getSemanticDiagnosticsOfFile(state, affected as SourceFile, cancellationToken), affected);
}
}
- function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilePendingEmit: Path, kind: BuilderFileEmit) {
- if (!state.affectedFilesPendingEmit) state.affectedFilesPendingEmit = [];
- if (!state.affectedFilesPendingEmitKind) state.affectedFilesPendingEmitKind = new Map();
+ /**
+ * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
+ * The semantic diagnostics are cached and managed here
+ * Note that it is assumed that when asked about semantic diagnostics through this API,
+ * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
+ * In case of SemanticDiagnosticsBuilderProgram if the source file is not provided,
+ * it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics
+ */
+ function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
+ assertSourceFileOkWithoutNextAffectedCall(state, sourceFile);
+ const compilerOptions = Debug.checkDefined(state.program).getCompilerOptions();
+ if (outFile(compilerOptions)) {
+ Debug.assert(!state.semanticDiagnosticsPerFile);
+ // We dont need to cache the diagnostics just return them from program
+ return Debug.checkDefined(state.program).getSemanticDiagnostics(sourceFile, cancellationToken);
+ }
+
+ if (sourceFile) {
+ return getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken);
+ }
- const existingKind = state.affectedFilesPendingEmitKind.get(affectedFilePendingEmit);
- state.affectedFilesPendingEmit.push(affectedFilePendingEmit);
- state.affectedFilesPendingEmitKind.set(affectedFilePendingEmit, existingKind || kind);
+ // When semantic builder asks for diagnostics of the whole program,
+ // ensure that all the affected files are handled
+ // eslint-disable-next-line no-empty
+ while (getSemanticDiagnosticsOfNextAffectedFile(cancellationToken)) {
+ }
- // affectedFilesPendingEmitIndex === undefined
- // - means the emit state.affectedFilesPendingEmit was undefined before adding current affected files
- // so start from 0 as array would be affectedFilesPendingEmit
- // else, continue to iterate from existing index, the current set is appended to existing files
- if (state.affectedFilesPendingEmitIndex === undefined) {
- state.affectedFilesPendingEmitIndex = 0;
+ let diagnostics: Diagnostic[] | undefined;
+ for (const sourceFile of Debug.checkDefined(state.program).getSourceFiles()) {
+ diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken));
}
+ return diagnostics || emptyArray;
}
+}
- export function toBuilderStateFileInfo(fileInfo: ProgramBuildInfoFileInfo): BuilderState.FileInfo {
- return isString(fileInfo) ?
- { version: fileInfo, signature: fileInfo, affectsGlobalScope: undefined, impliedFormat: undefined } :
- isString(fileInfo.signature) ?
- fileInfo as BuilderState.FileInfo :
- { version: fileInfo.version, signature: fileInfo.signature === false ? undefined : fileInfo.version, affectsGlobalScope: fileInfo.affectsGlobalScope, impliedFormat: fileInfo.impliedFormat };
+/* @internal */
+function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilePendingEmit: Path, kind: BuilderFileEmit) {
+ if (!state.affectedFilesPendingEmit)
+ state.affectedFilesPendingEmit = [];
+ if (!state.affectedFilesPendingEmitKind)
+ state.affectedFilesPendingEmitKind = new ts.Map();
+
+ const existingKind = state.affectedFilesPendingEmitKind.get(affectedFilePendingEmit);
+ state.affectedFilesPendingEmit.push(affectedFilePendingEmit);
+ state.affectedFilesPendingEmitKind.set(affectedFilePendingEmit, existingKind || kind);
+
+ // affectedFilesPendingEmitIndex === undefined
+ // - means the emit state.affectedFilesPendingEmit was undefined before adding current affected files
+ // so start from 0 as array would be affectedFilesPendingEmit
+ // else, continue to iterate from existing index, the current set is appended to existing files
+ if (state.affectedFilesPendingEmitIndex === undefined) {
+ state.affectedFilesPendingEmitIndex = 0;
}
+}
- export function createBuildProgramUsingProgramBuildInfo(program: ProgramBuildInfo, buildInfoPath: string, host: ReadBuildProgramHost): EmitAndSemanticDiagnosticsBuilderProgram {
- const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
- const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
-
- const filePaths = program.fileNames.map(toPath);
- const filePathsSetList = program.fileIdsList?.map(fileIds => new Set(fileIds.map(toFilePath)));
- const fileInfos = new Map();
- program.fileInfos.forEach((fileInfo, index) => fileInfos.set(toFilePath(index + 1 as ProgramBuildInfoFileId), toBuilderStateFileInfo(fileInfo)));
- const state: ReusableBuilderProgramState = {
- fileInfos,
- compilerOptions: program.options ? convertToOptionsWithAbsolutePaths(program.options, toAbsolutePath) : {},
- referencedMap: toManyToManyPathMap(program.referencedMap),
- exportedModulesMap: toManyToManyPathMap(program.exportedModulesMap),
- semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => toFilePath(isNumber(value) ? value : value[0]), value => isNumber(value) ? emptyArray : value[1]),
- hasReusableDiagnostic: true,
- affectedFilesPendingEmit: map(program.affectedFilesPendingEmit, value => toFilePath(value[0])),
- affectedFilesPendingEmitKind: program.affectedFilesPendingEmit && arrayToMap(program.affectedFilesPendingEmit, value => toFilePath(value[0]), value => value[1]),
- affectedFilesPendingEmitIndex: program.affectedFilesPendingEmit && 0,
- };
- return {
- getState: () => state,
- backupState: noop,
- restoreState: noop,
- getProgram: notImplemented,
- getProgramOrUndefined: returnUndefined,
- releaseProgram: noop,
- getCompilerOptions: () => state.compilerOptions,
- getSourceFile: notImplemented,
- getSourceFiles: notImplemented,
- getOptionsDiagnostics: notImplemented,
- getGlobalDiagnostics: notImplemented,
- getConfigFileParsingDiagnostics: notImplemented,
- getSyntacticDiagnostics: notImplemented,
- getDeclarationDiagnostics: notImplemented,
- getSemanticDiagnostics: notImplemented,
- emit: notImplemented,
- getAllDependencies: notImplemented,
- getCurrentDirectory: notImplemented,
- emitNextAffectedFile: notImplemented,
- getSemanticDiagnosticsOfNextAffectedFile: notImplemented,
- emitBuildInfo: notImplemented,
- close: noop,
- };
-
- function toPath(path: string) {
- return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
- }
+/* @internal */
+export function toBuilderStateFileInfo(fileInfo: ProgramBuildInfoFileInfo): BuilderState.FileInfo {
+ return isString(fileInfo) ?
+ { version: fileInfo, signature: fileInfo, affectsGlobalScope: undefined, impliedFormat: undefined } :
+ isString(fileInfo.signature) ?
+ fileInfo as BuilderState.FileInfo :
+ { version: fileInfo.version, signature: fileInfo.signature === false ? undefined : fileInfo.version, affectsGlobalScope: fileInfo.affectsGlobalScope, impliedFormat: fileInfo.impliedFormat };
+}
- function toAbsolutePath(path: string) {
- return getNormalizedAbsolutePath(path, buildInfoDirectory);
- }
+/* @internal */
+export function createBuildProgramUsingProgramBuildInfo(program: ProgramBuildInfo, buildInfoPath: string, host: ReadBuildProgramHost): EmitAndSemanticDiagnosticsBuilderProgram {
+ const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
+ const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
+
+ const filePaths = program.fileNames.map(toPath);
+ const filePathsSetList = program.fileIdsList?.map(fileIds => new ts.Set(fileIds.map(toFilePath)));
+ const fileInfos = new ts.Map();
+ program.fileInfos.forEach((fileInfo, index) => fileInfos.set(toFilePath(index + 1 as ProgramBuildInfoFileId), toBuilderStateFileInfo(fileInfo)));
+ const state: ReusableBuilderProgramState = {
+ fileInfos,
+ compilerOptions: program.options ? convertToOptionsWithAbsolutePaths(program.options, toAbsolutePath) : {},
+ referencedMap: toManyToManyPathMap(program.referencedMap),
+ exportedModulesMap: toManyToManyPathMap(program.exportedModulesMap),
+ semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => toFilePath(isNumber(value) ? value : value[0]), value => isNumber(value) ? emptyArray : value[1]),
+ hasReusableDiagnostic: true,
+ affectedFilesPendingEmit: map(program.affectedFilesPendingEmit, value => toFilePath(value[0])),
+ affectedFilesPendingEmitKind: program.affectedFilesPendingEmit && arrayToMap(program.affectedFilesPendingEmit, value => toFilePath(value[0]), value => value[1]),
+ affectedFilesPendingEmitIndex: program.affectedFilesPendingEmit && 0,
+ };
+ return {
+ getState: () => state,
+ backupState: noop,
+ restoreState: noop,
+ getProgram: notImplemented,
+ getProgramOrUndefined: returnUndefined,
+ releaseProgram: noop,
+ getCompilerOptions: () => state.compilerOptions,
+ getSourceFile: notImplemented,
+ getSourceFiles: notImplemented,
+ getOptionsDiagnostics: notImplemented,
+ getGlobalDiagnostics: notImplemented,
+ getConfigFileParsingDiagnostics: notImplemented,
+ getSyntacticDiagnostics: notImplemented,
+ getDeclarationDiagnostics: notImplemented,
+ getSemanticDiagnostics: notImplemented,
+ emit: notImplemented,
+ getAllDependencies: notImplemented,
+ getCurrentDirectory: notImplemented,
+ emitNextAffectedFile: notImplemented,
+ getSemanticDiagnosticsOfNextAffectedFile: notImplemented,
+ emitBuildInfo: notImplemented,
+ close: noop,
+ };
- function toFilePath(fileId: ProgramBuildInfoFileId) {
- return filePaths[fileId - 1];
- }
+ function toPath(path: string) {
+ return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
+ }
- function toFilePathsSet(fileIdsListId: ProgramBuildInfoFileIdListId) {
- return filePathsSetList![fileIdsListId - 1];
- }
+ function toAbsolutePath(path: string) {
+ return getNormalizedAbsolutePath(path, buildInfoDirectory);
+ }
- function toManyToManyPathMap(referenceMap: ProgramBuildInfoReferencedMap | undefined): BuilderState.ManyToManyPathMap | undefined {
- if (!referenceMap) {
- return undefined;
- }
+ function toFilePath(fileId: ProgramBuildInfoFileId) {
+ return filePaths[fileId - 1];
+ }
- const map = BuilderState.createManyToManyPathMap();
- referenceMap.forEach(([fileId, fileIdListId]) =>
- map.set(toFilePath(fileId), toFilePathsSet(fileIdListId))
- );
- return map;
- }
+ function toFilePathsSet(fileIdsListId: ProgramBuildInfoFileIdListId) {
+ return filePathsSetList![fileIdsListId - 1];
}
- export function createRedirectedBuilderProgram(getState: () => { program: Program | undefined; compilerOptions: CompilerOptions; }, configFileParsingDiagnostics: readonly Diagnostic[]): BuilderProgram {
- return {
- getState: notImplemented,
- backupState: noop,
- restoreState: noop,
- getProgram,
- getProgramOrUndefined: () => getState().program,
- releaseProgram: () => getState().program = undefined,
- getCompilerOptions: () => getState().compilerOptions,
- getSourceFile: fileName => getProgram().getSourceFile(fileName),
- getSourceFiles: () => getProgram().getSourceFiles(),
- getOptionsDiagnostics: cancellationToken => getProgram().getOptionsDiagnostics(cancellationToken),
- getGlobalDiagnostics: cancellationToken => getProgram().getGlobalDiagnostics(cancellationToken),
- getConfigFileParsingDiagnostics: () => configFileParsingDiagnostics,
- getSyntacticDiagnostics: (sourceFile, cancellationToken) => getProgram().getSyntacticDiagnostics(sourceFile, cancellationToken),
- getDeclarationDiagnostics: (sourceFile, cancellationToken) => getProgram().getDeclarationDiagnostics(sourceFile, cancellationToken),
- getSemanticDiagnostics: (sourceFile, cancellationToken) => getProgram().getSemanticDiagnostics(sourceFile, cancellationToken),
- emit: (sourceFile, writeFile, cancellationToken, emitOnlyDts, customTransformers) => getProgram().emit(sourceFile, writeFile, cancellationToken, emitOnlyDts, customTransformers),
- emitBuildInfo: (writeFile, cancellationToken) => getProgram().emitBuildInfo(writeFile, cancellationToken),
- getAllDependencies: notImplemented,
- getCurrentDirectory: () => getProgram().getCurrentDirectory(),
- close: noop,
- };
-
- function getProgram() {
- return Debug.checkDefined(getState().program);
+ function toManyToManyPathMap(referenceMap: ProgramBuildInfoReferencedMap | undefined): BuilderState.ManyToManyPathMap | undefined {
+ if (!referenceMap) {
+ return undefined;
}
+
+ const map = BuilderState.createManyToManyPathMap();
+ referenceMap.forEach(([fileId, fileIdListId]) => map.set(toFilePath(fileId), toFilePathsSet(fileIdListId)));
+ return map;
+ }
+}
+
+/* @internal */
+export function createRedirectedBuilderProgram(getState: () => {
+ program: Program | undefined;
+ compilerOptions: CompilerOptions;
+}, configFileParsingDiagnostics: readonly Diagnostic[]): BuilderProgram {
+ return {
+ getState: notImplemented,
+ backupState: noop,
+ restoreState: noop,
+ getProgram,
+ getProgramOrUndefined: () => getState().program,
+ releaseProgram: () => getState().program = undefined,
+ getCompilerOptions: () => getState().compilerOptions,
+ getSourceFile: fileName => getProgram().getSourceFile(fileName),
+ getSourceFiles: () => getProgram().getSourceFiles(),
+ getOptionsDiagnostics: cancellationToken => getProgram().getOptionsDiagnostics(cancellationToken),
+ getGlobalDiagnostics: cancellationToken => getProgram().getGlobalDiagnostics(cancellationToken),
+ getConfigFileParsingDiagnostics: () => configFileParsingDiagnostics,
+ getSyntacticDiagnostics: (sourceFile, cancellationToken) => getProgram().getSyntacticDiagnostics(sourceFile, cancellationToken),
+ getDeclarationDiagnostics: (sourceFile, cancellationToken) => getProgram().getDeclarationDiagnostics(sourceFile, cancellationToken),
+ getSemanticDiagnostics: (sourceFile, cancellationToken) => getProgram().getSemanticDiagnostics(sourceFile, cancellationToken),
+ emit: (sourceFile, writeFile, cancellationToken, emitOnlyDts, customTransformers) => getProgram().emit(sourceFile, writeFile, cancellationToken, emitOnlyDts, customTransformers),
+ emitBuildInfo: (writeFile, cancellationToken) => getProgram().emitBuildInfo(writeFile, cancellationToken),
+ getAllDependencies: notImplemented,
+ getCurrentDirectory: () => getProgram().getCurrentDirectory(),
+ close: noop,
+ };
+
+ function getProgram() {
+ return Debug.checkDefined(getState().program);
}
}
diff --git a/src/compiler/builderPublic.ts b/src/compiler/builderPublic.ts
index 5d5d2ed25dd52..6413466f2a157 100644
--- a/src/compiler/builderPublic.ts
+++ b/src/compiler/builderPublic.ts
@@ -1,169 +1,171 @@
-namespace ts {
- export type AffectedFileResult = { result: T; affected: SourceFile | Program; } | undefined;
-
- export interface BuilderProgramHost {
- /**
- * return true if file names are treated with case sensitivity
- */
- useCaseSensitiveFileNames(): boolean;
- /**
- * If provided this would be used this hash instead of actual file shape text for detecting changes
- */
- createHash?: (data: string) => string;
- /**
- * When emit or emitNextAffectedFile are called without writeFile,
- * this callback if present would be used to write files
- */
- writeFile?: WriteFileCallback;
- /**
- * disable using source file version as signature for testing
- */
- /*@internal*/
- disableUseFileVersionAsSignature?: boolean;
- }
+import { SourceFile, Program, WriteFileCallback, ReusableBuilderProgramState, CompilerOptions, CancellationToken, Diagnostic, DiagnosticWithLocation, CustomTransformers, EmitResult, CompilerHost, ProjectReference, createBuilderProgram, BuilderProgramKind, getBuilderCreationParameters, createRedirectedBuilderProgram } from "./ts";
+export type AffectedFileResult = {
+ result: T;
+ affected: SourceFile | Program;
+} | undefined;
+export interface BuilderProgramHost {
/**
- * Builder to manage the program state changes
- */
- export interface BuilderProgram {
- /*@internal*/
- getState(): ReusableBuilderProgramState;
- /*@internal*/
- backupState(): void;
- /*@internal*/
- restoreState(): void;
- /**
- * Returns current program
- */
- getProgram(): Program;
- /**
- * Returns current program that could be undefined if the program was released
- */
- /*@internal*/
- getProgramOrUndefined(): Program | undefined;
- /**
- * Releases reference to the program, making all the other operations that need program to fail.
- */
- /*@internal*/
- releaseProgram(): void;
- /**
- * Get compiler options of the program
- */
- getCompilerOptions(): CompilerOptions;
- /**
- * Get the source file in the program with file name
- */
- getSourceFile(fileName: string): SourceFile | undefined;
- /**
- * Get a list of files in the program
- */
- getSourceFiles(): readonly SourceFile[];
- /**
- * Get the diagnostics for compiler options
- */
- getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
- /**
- * Get the diagnostics that dont belong to any file
- */
- getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
- /**
- * Get the diagnostics from config file parsing
- */
- getConfigFileParsingDiagnostics(): readonly Diagnostic[];
- /**
- * Get the syntax diagnostics, for all source files if source file is not supplied
- */
- getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
- /**
- * Get the declaration diagnostics, for all source files if source file is not supplied
- */
- getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[];
- /**
- * Get all the dependencies of the file
- */
- getAllDependencies(sourceFile: SourceFile): readonly string[];
-
- /**
- * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
- * The semantic diagnostics are cached and managed here
- * Note that it is assumed that when asked about semantic diagnostics through this API,
- * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
- * In case of SemanticDiagnosticsBuilderProgram if the source file is not provided,
- * it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics
- */
- getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
- /**
- * Emits the JavaScript and declaration files.
- * When targetSource file is specified, emits the files corresponding to that source file,
- * otherwise for the whole program.
- * In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified,
- * it is assumed that that file is handled from affected file list. If targetSourceFile is not specified,
- * it will only emit all the affected files instead of whole program
- *
- * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
- * in that order would be used to write the files
- */
- emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
- /*@internal*/
- emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
- /**
- * Get the current directory of the program
- */
- getCurrentDirectory(): string;
- /*@internal*/
- close(): void;
- }
-
+ * return true if file names are treated with case sensitivity
+ */
+ useCaseSensitiveFileNames(): boolean;
+ /**
+ * If provided this would be used this hash instead of actual file shape text for detecting changes
+ */
+ createHash?: (data: string) => string;
/**
- * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files
+ * When emit or emitNextAffectedFile are called without writeFile,
+ * this callback if present would be used to write files
*/
- export interface SemanticDiagnosticsBuilderProgram extends BuilderProgram {
- /**
- * Gets the semantic diagnostics from the program for the next affected file and caches it
- * Returns undefined if the iteration is complete
- */
- getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult;
- }
+ writeFile?: WriteFileCallback;
+ /**
+ * disable using source file version as signature for testing
+ */
+ /*@internal*/
+ disableUseFileVersionAsSignature?: boolean;
+}
+/**
+ * Builder to manage the program state changes
+ */
+export interface BuilderProgram {
+ /*@internal*/
+ getState(): ReusableBuilderProgramState;
+ /*@internal*/
+ backupState(): void;
+ /*@internal*/
+ restoreState(): void;
+ /**
+ * Returns current program
+ */
+ getProgram(): Program;
+ /**
+ * Returns current program that could be undefined if the program was released
+ */
+ /*@internal*/
+ getProgramOrUndefined(): Program | undefined;
+ /**
+ * Releases reference to the program, making all the other operations that need program to fail.
+ */
+ /*@internal*/
+ releaseProgram(): void;
+ /**
+ * Get compiler options of the program
+ */
+ getCompilerOptions(): CompilerOptions;
+ /**
+ * Get the source file in the program with file name
+ */
+ getSourceFile(fileName: string): SourceFile | undefined;
+ /**
+ * Get a list of files in the program
+ */
+ getSourceFiles(): readonly SourceFile[];
+ /**
+ * Get the diagnostics for compiler options
+ */
+ getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
+ /**
+ * Get the diagnostics that dont belong to any file
+ */
+ getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
- * The builder that can handle the changes in program and iterate through changed file to emit the files
- * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files
- */
- export interface EmitAndSemanticDiagnosticsBuilderProgram extends SemanticDiagnosticsBuilderProgram {
- /**
- * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
- * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
- * in that order would be used to write the files
- */
- emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult;
- }
+ * Get the diagnostics from config file parsing
+ */
+ getConfigFileParsingDiagnostics(): readonly Diagnostic[];
+ /**
+ * Get the syntax diagnostics, for all source files if source file is not supplied
+ */
+ getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
+ /**
+ * Get the declaration diagnostics, for all source files if source file is not supplied
+ */
+ getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[];
+ /**
+ * Get all the dependencies of the file
+ */
+ getAllDependencies(sourceFile: SourceFile): readonly string[];
/**
- * Create the builder to manage semantic diagnostics and cache them
+ * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
+ * The semantic diagnostics are cached and managed here
+ * Note that it is assumed that when asked about semantic diagnostics through this API,
+ * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
+ * In case of SemanticDiagnosticsBuilderProgram if the source file is not provided,
+ * it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics
+ */
+ getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
+ /**
+ * Emits the JavaScript and declaration files.
+ * When targetSource file is specified, emits the files corresponding to that source file,
+ * otherwise for the whole program.
+ * In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified,
+ * it is assumed that that file is handled from affected file list. If targetSourceFile is not specified,
+ * it will only emit all the affected files instead of whole program
+ *
+ * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
+ * in that order would be used to write the files
+ */
+ emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
+ /*@internal*/
+ emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
+ /**
+ * Get the current directory of the program
*/
- export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): SemanticDiagnosticsBuilderProgram;
- export function createSemanticDiagnosticsBuilderProgram(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): SemanticDiagnosticsBuilderProgram;
- export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]) {
- return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
- }
+ getCurrentDirectory(): string;
+ /*@internal*/
+ close(): void;
+}
+/**
+ * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files
+ */
+export interface SemanticDiagnosticsBuilderProgram extends BuilderProgram {
/**
- * Create the builder that can handle the changes in program and iterate through changed files
- * to emit the those files and manage semantic diagnostics cache as well
+ * Gets the semantic diagnostics from the program for the next affected file and caches it
+ * Returns undefined if the iteration is complete
*/
- export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): EmitAndSemanticDiagnosticsBuilderProgram;
- export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): EmitAndSemanticDiagnosticsBuilderProgram;
- export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]) {
- return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
- }
+ getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult;
+}
+/**
+ * The builder that can handle the changes in program and iterate through changed file to emit the files
+ * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files
+ */
+export interface EmitAndSemanticDiagnosticsBuilderProgram extends SemanticDiagnosticsBuilderProgram {
/**
- * Creates a builder thats just abstraction over program and can be used with watch
+ * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
+ * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
+ * in that order would be used to write the files
*/
- export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): BuilderProgram;
- export function createAbstractBuilder(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderProgram;
- export function createAbstractBuilder(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | BuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderProgram {
- const { newProgram, configFileParsingDiagnostics: newConfigFileParsingDiagnostics } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences);
- return createRedirectedBuilderProgram(() => ({ program: newProgram, compilerOptions: newProgram.getCompilerOptions() }), newConfigFileParsingDiagnostics);
- }
+ emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult;
+}
+
+/**
+ * Create the builder to manage semantic diagnostics and cache them
+ */
+export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): SemanticDiagnosticsBuilderProgram;
+export function createSemanticDiagnosticsBuilderProgram(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): SemanticDiagnosticsBuilderProgram;
+export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]) {
+ return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
+}
+
+/**
+ * Create the builder that can handle the changes in program and iterate through changed files
+ * to emit the those files and manage semantic diagnostics cache as well
+ */
+export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): EmitAndSemanticDiagnosticsBuilderProgram;
+export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): EmitAndSemanticDiagnosticsBuilderProgram;
+export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]) {
+ return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
+}
+
+/**
+ * Creates a builder thats just abstraction over program and can be used with watch
+ */
+export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): BuilderProgram;
+export function createAbstractBuilder(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderProgram;
+export function createAbstractBuilder(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | BuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderProgram {
+ const { newProgram, configFileParsingDiagnostics: newConfigFileParsingDiagnostics } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences);
+ return createRedirectedBuilderProgram(() => ({ program: newProgram, compilerOptions: newProgram.getCompilerOptions() }), newConfigFileParsingDiagnostics);
}
diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts
index a64a6c13ed1e8..0cbafbd75646d 100644
--- a/src/compiler/builderState.ts
+++ b/src/compiler/builderState.ts
@@ -1,678 +1,680 @@
+import { Program, SourceFile, CancellationToken, CustomTransformers, EmitOutput, OutputFile, ReadonlyESMap, Path, ESMap, Symbol, mapDefined, getSourceFileOfNode, TypeChecker, StringLiteralLike, GetCanonicalFileName, toPath, getDirectoryPath, isStringLiteral, ModuleKind, Debug, emptyArray, firstOrUndefined, fileExtensionIsOneOf, Extension, getAnyExtensionFromPath, generateDjb2Hash, ExportedModulesFromDeclarationEmit, outFile, arrayFrom, mapDefinedIterator, isModuleWithStringLiteralName, some, isGlobalScopeAugmentation, ModuleDeclaration, isExternalOrCommonJsModule, isJsonSourceFile } from "./ts";
+import * as ts from "./ts";
/*@internal*/
-namespace ts {
- export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean,
- cancellationToken?: CancellationToken, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitOutput {
- const outputFiles: OutputFile[] = [];
- const { emitSkipped, diagnostics, exportedModulesFromDeclarationEmit } = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, forceDtsEmit);
- return { outputFiles, emitSkipped, diagnostics, exportedModulesFromDeclarationEmit };
+export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitOutput {
+ const outputFiles: OutputFile[] = [];
+ const { emitSkipped, diagnostics, exportedModulesFromDeclarationEmit } = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, forceDtsEmit);
+ return { outputFiles, emitSkipped, diagnostics, exportedModulesFromDeclarationEmit };
- function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) {
- outputFiles.push({ name: fileName, writeByteOrderMark, text });
- }
+ function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) {
+ outputFiles.push({ name: fileName, writeByteOrderMark, text });
}
+}
- export interface ReusableBuilderState {
- /**
- * Information of the file eg. its version, signature etc
- */
- fileInfos: ReadonlyESMap;
- /**
- * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled
- * Otherwise undefined
- * Thus non undefined value indicates, module emit
- */
- readonly referencedMap?: BuilderState.ReadonlyManyToManyPathMap | undefined;
- /**
- * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled
- * Otherwise undefined
- */
- readonly exportedModulesMap?: BuilderState.ReadonlyManyToManyPathMap | undefined;
- }
+/* @internal */
+export interface ReusableBuilderState {
+ /**
+ * Information of the file eg. its version, signature etc
+ */
+ fileInfos: ReadonlyESMap;
+ /**
+ * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled
+ * Otherwise undefined
+ * Thus non undefined value indicates, module emit
+ */
+ readonly referencedMap?: BuilderState.ReadonlyManyToManyPathMap | undefined;
+ /**
+ * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled
+ * Otherwise undefined
+ */
+ readonly exportedModulesMap?: BuilderState.ReadonlyManyToManyPathMap | undefined;
+}
- export interface BuilderState {
- /**
- * Information of the file eg. its version, signature etc
- */
- fileInfos: ESMap;
- /**
- * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled
- * Otherwise undefined
- * Thus non undefined value indicates, module emit
- */
- readonly referencedMap: BuilderState.ReadonlyManyToManyPathMap | undefined;
- /**
- * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled
- * Otherwise undefined
- *
- * This is equivalent to referencedMap, but for the emitted .d.ts file.
- */
- readonly exportedModulesMap: BuilderState.ManyToManyPathMap | undefined;
+/* @internal */
+export interface BuilderState {
+ /**
+ * Information of the file eg. its version, signature etc
+ */
+ fileInfos: ESMap;
+ /**
+ * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled
+ * Otherwise undefined
+ * Thus non undefined value indicates, module emit
+ */
+ readonly referencedMap: BuilderState.ReadonlyManyToManyPathMap | undefined;
+ /**
+ * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled
+ * Otherwise undefined
+ *
+ * This is equivalent to referencedMap, but for the emitted .d.ts file.
+ */
+ readonly exportedModulesMap: BuilderState.ManyToManyPathMap | undefined;
+
+ previousCache?: {
+ id: number;
+ version: number;
+ };
+
+ /**
+ * true if file version is used as signature
+ * This helps in delaying the calculation of the d.ts hash as version for the file till reasonable time
+ */
+ useFileVersionAsSignature: boolean;
+ /**
+ * Map of files that have already called update signature.
+ * That means hence forth these files are assumed to have
+ * no change in their signature for this version of the program
+ */
+ hasCalledUpdateShapeSignature: ts.Set;
+ /**
+ * Cache of all files excluding default library file for the current program
+ */
+ allFilesExcludingDefaultLibraryFile?: readonly SourceFile[];
+ /**
+ * Cache of all the file names
+ */
+ allFileNames?: readonly string[];
+}
- previousCache?: {
- id: number,
- version: number,
- };
+/* @internal */
+export namespace BuilderState {
+ /**
+ * Information about the source file: Its version and optional signature from last emit
+ */
+ export interface FileInfo {
+ readonly version: string;
+ signature: string | undefined;
+ affectsGlobalScope: boolean | undefined;
+ impliedFormat: number | undefined;
+ }
+
+ export interface ReadonlyManyToManyPathMap {
+ readonly id: number;
+ clone(): ManyToManyPathMap;
+ forEach(action: (v: ts.ReadonlySet, k: Path) => void): void;
+ getKeys(v: Path): ts.ReadonlySet | undefined;
+ getValues(k: Path): ts.ReadonlySet | undefined;
+ hasKey(k: Path): boolean;
+ keys(): ts.Iterator;
/**
- * true if file version is used as signature
- * This helps in delaying the calculation of the d.ts hash as version for the file till reasonable time
- */
- useFileVersionAsSignature: boolean;
- /**
- * Map of files that have already called update signature.
- * That means hence forth these files are assumed to have
- * no change in their signature for this version of the program
- */
- hasCalledUpdateShapeSignature: Set;
- /**
- * Cache of all files excluding default library file for the current program
- */
- allFilesExcludingDefaultLibraryFile?: readonly SourceFile[];
- /**
- * Cache of all the file names
+ * The set of arguments to {@link deleteKeys} which have not subsequently
+ * been arguments to {@link set}. Note that a key does not have to have
+ * ever been in the map to appear in this set.
*/
- allFileNames?: readonly string[];
+ deletedKeys(): ts.ReadonlySet | undefined;
}
- export namespace BuilderState {
- /**
- * Information about the source file: Its version and optional signature from last emit
- */
- export interface FileInfo {
- readonly version: string;
- signature: string | undefined;
- affectsGlobalScope: boolean | undefined;
- impliedFormat: number | undefined;
- }
-
- export interface ReadonlyManyToManyPathMap {
- readonly id: number;
- clone(): ManyToManyPathMap;
- forEach(action: (v: ReadonlySet, k: Path) => void): void;
- getKeys(v: Path): ReadonlySet | undefined;
- getValues(k: Path): ReadonlySet | undefined;
- hasKey(k: Path): boolean;
- keys(): Iterator;
-
- /**
- * The set of arguments to {@link deleteKeys} which have not subsequently
- * been arguments to {@link set}. Note that a key does not have to have
- * ever been in the map to appear in this set.
- */
- deletedKeys(): ReadonlySet | undefined;
- }
-
- export interface ManyToManyPathMap extends ReadonlyManyToManyPathMap {
- version(): number; // Incremented each time the contents are changed
- deleteKey(k: Path): boolean;
- set(k: Path, v: ReadonlySet): void;
- }
-
- let manyToManyPathMapCount = 0;
- export function createManyToManyPathMap(): ManyToManyPathMap {
- function create(forward: ESMap>, reverse: ESMap>, deleted: Set | undefined): ManyToManyPathMap {
- let version = 0;
- const map: ManyToManyPathMap = {
- id: manyToManyPathMapCount++,
- version: () => version,
- clone: () => create(new Map(forward), new Map(reverse), deleted && new Set(deleted)),
- forEach: fn => forward.forEach(fn),
- getKeys: v => reverse.get(v),
- getValues: k => forward.get(k),
- hasKey: k => forward.has(k),
- keys: () => forward.keys(),
-
- deletedKeys: () => deleted,
- deleteKey: k => {
- (deleted ||= new Set()).add(k);
-
- const set = forward.get(k);
- if (!set) {
- return false;
+ export interface ManyToManyPathMap extends ReadonlyManyToManyPathMap {
+ version(): number; // Incremented each time the contents are changed
+ deleteKey(k: Path): boolean;
+ set(k: Path, v: ts.ReadonlySet): void;
+ }
+
+ let manyToManyPathMapCount = 0;
+ export function createManyToManyPathMap(): ManyToManyPathMap {
+ function create(forward: ESMap>, reverse: ESMap>, deleted: ts.Set | undefined): ManyToManyPathMap {
+ let version = 0;
+ const map: ManyToManyPathMap = {
+ id: manyToManyPathMapCount++,
+ version: () => version,
+ clone: () => create(new ts.Map(forward), new ts.Map(reverse), deleted && new ts.Set(deleted)),
+ forEach: fn => forward.forEach(fn),
+ getKeys: v => reverse.get(v),
+ getValues: k => forward.get(k),
+ hasKey: k => forward.has(k),
+ keys: () => forward.keys(),
+
+ deletedKeys: () => deleted,
+ deleteKey: k => {
+ (deleted ||= new ts.Set()).add(k);
+
+ const set = forward.get(k);
+ if (!set) {
+ return false;
+ }
+
+ set.forEach(v => deleteFromMultimap(reverse, v, k));
+ forward.delete(k);
+ version++;
+ return true;
+ },
+ set: (k, vSet) => {
+ let changed = !!deleted?.delete(k);
+
+ const existingVSet = forward.get(k);
+ forward.set(k, vSet);
+
+ existingVSet?.forEach(v => {
+ if (!vSet.has(v)) {
+ changed = true;
+ deleteFromMultimap(reverse, v, k);
}
+ });
- set.forEach(v => deleteFromMultimap(reverse, v, k));
- forward.delete(k);
- version++;
- return true;
- },
- set: (k, vSet) => {
- let changed = !!deleted?.delete(k);
-
- const existingVSet = forward.get(k);
- forward.set(k, vSet);
-
- existingVSet?.forEach(v => {
- if (!vSet.has(v)) {
- changed = true;
- deleteFromMultimap(reverse, v, k);
- }
- });
-
- vSet.forEach(v => {
- if (!existingVSet?.has(v)) {
- changed = true;
- addToMultimap(reverse, v, k);
- }
- });
-
- if (changed) {
- version++;
+ vSet.forEach(v => {
+ if (!existingVSet?.has(v)) {
+ changed = true;
+ addToMultimap(reverse, v, k);
}
+ });
- return map;
- },
- };
+ if (changed) {
+ version++;
+ }
- return map;
- }
+ return map;
+ },
+ };
- return create(new Map>(), new Map>(), /*deleted*/ undefined);
+ return map;
}
- function addToMultimap(map: ESMap>, k: K, v: V): void {
- let set = map.get(k);
- if (!set) {
- set = new Set();
- map.set(k, set);
- }
- set.add(v);
+ return create(new ts.Map>(), new ts.Map>(), /*deleted*/ undefined);
+ }
+
+ function addToMultimap(map: ESMap>, k: K, v: V): void {
+ let set = map.get(k);
+ if (!set) {
+ set = new ts.Set();
+ map.set(k, set);
}
+ set.add(v);
+ }
- function deleteFromMultimap(map: ESMap>, k: K, v: V, removeEmpty = true): boolean {
- const set = map.get(k);
+ function deleteFromMultimap(map: ESMap>, k: K, v: V, removeEmpty = true): boolean {
+ const set = map.get(k);
- if (set?.delete(v)) {
- if (removeEmpty && !set.size) {
- map.delete(k);
- }
- return true;
+ if (set?.delete(v)) {
+ if (removeEmpty && !set.size) {
+ map.delete(k);
}
-
- return false;
+ return true;
}
- /**
- * Compute the hash to store the shape of the file
- */
- export type ComputeHash = ((data: string) => string) | undefined;
+ return false;
+ }
- function getReferencedFilesFromImportedModuleSymbol(symbol: Symbol): Path[] {
- return mapDefined(symbol.declarations, declaration => getSourceFileOfNode(declaration)?.resolvedPath);
- }
+ /**
+ * Compute the hash to store the shape of the file
+ */
+ export type ComputeHash = ((data: string) => string) | undefined;
- /**
- * Get the module source file and all augmenting files from the import name node from file
- */
- function getReferencedFilesFromImportLiteral(checker: TypeChecker, importName: StringLiteralLike): Path[] | undefined {
- const symbol = checker.getSymbolAtLocation(importName);
- return symbol && getReferencedFilesFromImportedModuleSymbol(symbol);
- }
+ function getReferencedFilesFromImportedModuleSymbol(symbol: Symbol): Path[] {
+ return mapDefined(symbol.declarations, declaration => getSourceFileOfNode(declaration)?.resolvedPath);
+ }
- /**
- * Gets the path to reference file from file name, it could be resolvedPath if present otherwise path
- */
- function getReferencedFileFromFileName(program: Program, fileName: string, sourceFileDirectory: Path, getCanonicalFileName: GetCanonicalFileName): Path {
- return toPath(program.getProjectReferenceRedirect(fileName) || fileName, sourceFileDirectory, getCanonicalFileName);
- }
+ /**
+ * Get the module source file and all augmenting files from the import name node from file
+ */
+ function getReferencedFilesFromImportLiteral(checker: TypeChecker, importName: StringLiteralLike): Path[] | undefined {
+ const symbol = checker.getSymbolAtLocation(importName);
+ return symbol && getReferencedFilesFromImportedModuleSymbol(symbol);
+ }
- /**
- * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true
- */
- function getReferencedFiles(program: Program, sourceFile: SourceFile, getCanonicalFileName: GetCanonicalFileName): Set | undefined {
- let referencedFiles: Set | undefined;
-
- // We need to use a set here since the code can contain the same import twice,
- // but that will only be one dependency.
- // To avoid invernal conversion, the key of the referencedFiles map must be of type Path
- if (sourceFile.imports && sourceFile.imports.length > 0) {
- const checker: TypeChecker = program.getTypeChecker();
- for (const importName of sourceFile.imports) {
- const declarationSourceFilePaths = getReferencedFilesFromImportLiteral(checker, importName);
- declarationSourceFilePaths?.forEach(addReferencedFile);
- }
- }
+ /**
+ * Gets the path to reference file from file name, it could be resolvedPath if present otherwise path
+ */
+ function getReferencedFileFromFileName(program: Program, fileName: string, sourceFileDirectory: Path, getCanonicalFileName: GetCanonicalFileName): Path {
+ return toPath(program.getProjectReferenceRedirect(fileName) || fileName, sourceFileDirectory, getCanonicalFileName);
+ }
- const sourceFileDirectory = getDirectoryPath(sourceFile.resolvedPath);
- // Handle triple slash references
- if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) {
- for (const referencedFile of sourceFile.referencedFiles) {
- const referencedPath = getReferencedFileFromFileName(program, referencedFile.fileName, sourceFileDirectory, getCanonicalFileName);
- addReferencedFile(referencedPath);
- }
+ /**
+ * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true
+ */
+ function getReferencedFiles(program: Program, sourceFile: SourceFile, getCanonicalFileName: GetCanonicalFileName): ts.Set | undefined {
+ let referencedFiles: ts.Set | undefined;
+
+ // We need to use a set here since the code can contain the same import twice,
+ // but that will only be one dependency.
+ // To avoid invernal conversion, the key of the referencedFiles map must be of type Path
+ if (sourceFile.imports && sourceFile.imports.length > 0) {
+ const checker: TypeChecker = program.getTypeChecker();
+ for (const importName of sourceFile.imports) {
+ const declarationSourceFilePaths = getReferencedFilesFromImportLiteral(checker, importName);
+ declarationSourceFilePaths?.forEach(addReferencedFile);
}
+ }
- // Handle type reference directives
- if (sourceFile.resolvedTypeReferenceDirectiveNames) {
- sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => {
- if (!resolvedTypeReferenceDirective) {
- return;
- }
-
- const fileName = resolvedTypeReferenceDirective.resolvedFileName!; // TODO: GH#18217
- const typeFilePath = getReferencedFileFromFileName(program, fileName, sourceFileDirectory, getCanonicalFileName);
- addReferencedFile(typeFilePath);
- });
+ const sourceFileDirectory = getDirectoryPath(sourceFile.resolvedPath);
+ // Handle triple slash references
+ if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) {
+ for (const referencedFile of sourceFile.referencedFiles) {
+ const referencedPath = getReferencedFileFromFileName(program, referencedFile.fileName, sourceFileDirectory, getCanonicalFileName);
+ addReferencedFile(referencedPath);
}
+ }
- // Add module augmentation as references
- if (sourceFile.moduleAugmentations.length) {
- const checker = program.getTypeChecker();
- for (const moduleName of sourceFile.moduleAugmentations) {
- if (!isStringLiteral(moduleName)) continue;
- const symbol = checker.getSymbolAtLocation(moduleName);
- if (!symbol) continue;
-
- // Add any file other than our own as reference
- addReferenceFromAmbientModule(symbol);
+ // Handle type reference directives
+ if (sourceFile.resolvedTypeReferenceDirectiveNames) {
+ sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => {
+ if (!resolvedTypeReferenceDirective) {
+ return;
}
- }
- // From ambient modules
- for (const ambientModule of program.getTypeChecker().getAmbientModules()) {
- if (ambientModule.declarations && ambientModule.declarations.length > 1) {
- addReferenceFromAmbientModule(ambientModule);
- }
- }
+ const fileName = resolvedTypeReferenceDirective.resolvedFileName!; // TODO: GH#18217
+ const typeFilePath = getReferencedFileFromFileName(program, fileName, sourceFileDirectory, getCanonicalFileName);
+ addReferencedFile(typeFilePath);
+ });
+ }
- return referencedFiles;
+ // Add module augmentation as references
+ if (sourceFile.moduleAugmentations.length) {
+ const checker = program.getTypeChecker();
+ for (const moduleName of sourceFile.moduleAugmentations) {
+ if (!isStringLiteral(moduleName))
+ continue;
+ const symbol = checker.getSymbolAtLocation(moduleName);
+ if (!symbol)
+ continue;
- function addReferenceFromAmbientModule(symbol: Symbol) {
- if (!symbol.declarations) {
- return;
- }
// Add any file other than our own as reference
- for (const declaration of symbol.declarations) {
- const declarationSourceFile = getSourceFileOfNode(declaration);
- if (declarationSourceFile &&
- declarationSourceFile !== sourceFile) {
- addReferencedFile(declarationSourceFile.resolvedPath);
- }
- }
+ addReferenceFromAmbientModule(symbol);
}
+ }
- function addReferencedFile(referencedPath: Path) {
- (referencedFiles || (referencedFiles = new Set())).add(referencedPath);
+ // From ambient modules
+ for (const ambientModule of program.getTypeChecker().getAmbientModules()) {
+ if (ambientModule.declarations && ambientModule.declarations.length > 1) {
+ addReferenceFromAmbientModule(ambientModule);
}
}
- /**
- * Returns true if oldState is reusable, that is the emitKind = module/non module has not changed
- */
- export function canReuseOldState(newReferencedMap: ReadonlyManyToManyPathMap | undefined, oldState: Readonly | undefined) {
- return oldState && !oldState.referencedMap === !newReferencedMap;
- }
+ return referencedFiles;
- /**
- * Creates the state of file references and signature for the new program from oldState if it is safe
- */
- export function create(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly, disableUseFileVersionAsSignature?: boolean): BuilderState {
- const fileInfos = new Map();
- const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? createManyToManyPathMap() : undefined;
- const exportedModulesMap = referencedMap ? createManyToManyPathMap() : undefined;
- const hasCalledUpdateShapeSignature = new Set();
- const useOldState = canReuseOldState(referencedMap, oldState);
-
- // Ensure source files have parent pointers set
- newProgram.getTypeChecker();
-
- // Create the reference map, and set the file infos
- for (const sourceFile of newProgram.getSourceFiles()) {
- const version = Debug.checkDefined(sourceFile.version, "Program intended to be used with Builder should have source files with versions set");
- const oldInfo = useOldState ? oldState!.fileInfos.get(sourceFile.resolvedPath) : undefined;
- if (referencedMap) {
- const newReferences = getReferencedFiles(newProgram, sourceFile, getCanonicalFileName);
- if (newReferences) {
- referencedMap.set(sourceFile.resolvedPath, newReferences);
- }
- // Copy old visible to outside files map
- if (useOldState) {
- const exportedModules = oldState!.exportedModulesMap!.getValues(sourceFile.resolvedPath);
- if (exportedModules) {
- exportedModulesMap!.set(sourceFile.resolvedPath, exportedModules);
- }
- }
+ function addReferenceFromAmbientModule(symbol: Symbol) {
+ if (!symbol.declarations) {
+ return;
+ }
+ // Add any file other than our own as reference
+ for (const declaration of symbol.declarations) {
+ const declarationSourceFile = getSourceFileOfNode(declaration);
+ if (declarationSourceFile &&
+ declarationSourceFile !== sourceFile) {
+ addReferencedFile(declarationSourceFile.resolvedPath);
}
- fileInfos.set(sourceFile.resolvedPath, { version, signature: oldInfo && oldInfo.signature, affectsGlobalScope: isFileAffectingGlobalScope(sourceFile) || undefined, impliedFormat: sourceFile.impliedNodeFormat });
}
-
- return {
- fileInfos,
- referencedMap,
- exportedModulesMap,
- hasCalledUpdateShapeSignature,
- useFileVersionAsSignature: !disableUseFileVersionAsSignature && !useOldState
- };
}
- /**
- * Releases needed properties
- */
- export function releaseCache(state: BuilderState) {
- state.allFilesExcludingDefaultLibraryFile = undefined;
- state.allFileNames = undefined;
+ function addReferencedFile(referencedPath: Path) {
+ (referencedFiles || (referencedFiles = new ts.Set())).add(referencedPath);
}
+ }
- /**
- * Creates a clone of the state
- */
- export function clone(state: Readonly): BuilderState {
- // Dont need to backup allFiles info since its cache anyway
- return {
- fileInfos: new Map(state.fileInfos),
- referencedMap: state.referencedMap?.clone(),
- exportedModulesMap: state.exportedModulesMap?.clone(),
- hasCalledUpdateShapeSignature: new Set(state.hasCalledUpdateShapeSignature),
- useFileVersionAsSignature: state.useFileVersionAsSignature,
- };
- }
+ /**
+ * Returns true if oldState is reusable, that is the emitKind = module/non module has not changed
+ */
+ export function canReuseOldState(newReferencedMap: ReadonlyManyToManyPathMap | undefined, oldState: Readonly | undefined) {
+ return oldState && !oldState.referencedMap === !newReferencedMap;
+ }
- /**
- * Gets the files affected by the path from the program
- */
- export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: ESMap, exportedModulesMapCache?: ManyToManyPathMap): readonly SourceFile[] {
- // Since the operation could be cancelled, the signatures are always stored in the cache
- // They will be committed once it is safe to use them
- // eg when calling this api from tsserver, if there is no cancellation of the operation
- // In the other cases the affected files signatures are committed only after the iteration through the result is complete
- const signatureCache = cacheToUpdateSignature || new Map();
- const sourceFile = programOfThisState.getSourceFileByPath(path);
- if (!sourceFile) {
- return emptyArray;
+ /**
+ * Creates the state of file references and signature for the new program from oldState if it is safe
+ */
+ export function create(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly, disableUseFileVersionAsSignature?: boolean): BuilderState {
+ const fileInfos = new ts.Map();
+ const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? createManyToManyPathMap() : undefined;
+ const exportedModulesMap = referencedMap ? createManyToManyPathMap() : undefined;
+ const hasCalledUpdateShapeSignature = new ts.Set();
+ const useOldState = canReuseOldState(referencedMap, oldState);
+
+ // Ensure source files have parent pointers set
+ newProgram.getTypeChecker();
+
+ // Create the reference map, and set the file infos
+ for (const sourceFile of newProgram.getSourceFiles()) {
+ const version = Debug.checkDefined(sourceFile.version, "Program intended to be used with Builder should have source files with versions set");
+ const oldInfo = useOldState ? oldState!.fileInfos.get(sourceFile.resolvedPath) : undefined;
+ if (referencedMap) {
+ const newReferences = getReferencedFiles(newProgram, sourceFile, getCanonicalFileName);
+ if (newReferences) {
+ referencedMap.set(sourceFile.resolvedPath, newReferences);
+ }
+ // Copy old visible to outside files map
+ if (useOldState) {
+ const exportedModules = oldState!.exportedModulesMap!.getValues(sourceFile.resolvedPath);
+ if (exportedModules) {
+ exportedModulesMap!.set(sourceFile.resolvedPath, exportedModules);
+ }
+ }
}
+ fileInfos.set(sourceFile.resolvedPath, { version, signature: oldInfo && oldInfo.signature, affectsGlobalScope: isFileAffectingGlobalScope(sourceFile) || undefined, impliedFormat: sourceFile.impliedNodeFormat });
+ }
- if (!updateShapeSignature(state, programOfThisState, sourceFile, signatureCache, cancellationToken, computeHash, exportedModulesMapCache)) {
- return [sourceFile];
- }
+ return {
+ fileInfos,
+ referencedMap,
+ exportedModulesMap,
+ hasCalledUpdateShapeSignature,
+ useFileVersionAsSignature: !disableUseFileVersionAsSignature && !useOldState
+ };
+ }
- const result = (state.referencedMap ? getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit)(state, programOfThisState, sourceFile, signatureCache, cancellationToken, computeHash, exportedModulesMapCache);
- if (!cacheToUpdateSignature) {
- // Commit all the signatures in the signature cache
- updateSignaturesFromCache(state, signatureCache);
- }
- return result;
+ /**
+ * Releases needed properties
+ */
+ export function releaseCache(state: BuilderState) {
+ state.allFilesExcludingDefaultLibraryFile = undefined;
+ state.allFileNames = undefined;
+ }
+
+ /**
+ * Creates a clone of the state
+ */
+ export function clone(state: Readonly): BuilderState {
+ // Dont need to backup allFiles info since its cache anyway
+ return {
+ fileInfos: new ts.Map(state.fileInfos),
+ referencedMap: state.referencedMap?.clone(),
+ exportedModulesMap: state.exportedModulesMap?.clone(),
+ hasCalledUpdateShapeSignature: new ts.Set(state.hasCalledUpdateShapeSignature),
+ useFileVersionAsSignature: state.useFileVersionAsSignature,
+ };
+ }
+
+ /**
+ * Gets the files affected by the path from the program
+ */
+ export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: ESMap, exportedModulesMapCache?: ManyToManyPathMap): readonly SourceFile[] {
+ // Since the operation could be cancelled, the signatures are always stored in the cache
+ // They will be committed once it is safe to use them
+ // eg when calling this api from tsserver, if there is no cancellation of the operation
+ // In the other cases the affected files signatures are committed only after the iteration through the result is complete
+ const signatureCache = cacheToUpdateSignature || new ts.Map();
+ const sourceFile = programOfThisState.getSourceFileByPath(path);
+ if (!sourceFile) {
+ return emptyArray;
}
- /**
- * Updates the signatures from the cache into state's fileinfo signatures
- * This should be called whenever it is safe to commit the state of the builder
- */
- export function updateSignaturesFromCache(state: BuilderState, signatureCache: ESMap) {
- signatureCache.forEach((signature, path) => updateSignatureOfFile(state, signature, path));
+ if (!updateShapeSignature(state, programOfThisState, sourceFile, signatureCache, cancellationToken, computeHash, exportedModulesMapCache)) {
+ return [sourceFile];
}
- export function updateSignatureOfFile(state: BuilderState, signature: string | undefined, path: Path) {
- state.fileInfos.get(path)!.signature = signature;
- state.hasCalledUpdateShapeSignature.add(path);
+ const result = (state.referencedMap ? getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit)(state, programOfThisState, sourceFile, signatureCache, cancellationToken, computeHash, exportedModulesMapCache);
+ if (!cacheToUpdateSignature) {
+ // Commit all the signatures in the signature cache
+ updateSignaturesFromCache(state, signatureCache);
}
+ return result;
+ }
- /**
- * Returns if the shape of the signature has changed since last emit
- */
- export function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: ESMap, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ManyToManyPathMap, useFileVersionAsSignature: boolean = state.useFileVersionAsSignature) {
- Debug.assert(!!sourceFile);
- Debug.assert(!exportedModulesMapCache || !!state.exportedModulesMap, "Compute visible to outside map only if visibleToOutsideReferencedMap present in the state");
+ /**
+ * Updates the signatures from the cache into state's fileinfo signatures
+ * This should be called whenever it is safe to commit the state of the builder
+ */
+ export function updateSignaturesFromCache(state: BuilderState, signatureCache: ESMap) {
+ signatureCache.forEach((signature, path) => updateSignatureOfFile(state, signature, path));
+ }
- // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate
- if (state.hasCalledUpdateShapeSignature.has(sourceFile.resolvedPath) || cacheToUpdateSignature.has(sourceFile.resolvedPath)) {
- return false;
- }
+ export function updateSignatureOfFile(state: BuilderState, signature: string | undefined, path: Path) {
+ state.fileInfos.get(path)!.signature = signature;
+ state.hasCalledUpdateShapeSignature.add(path);
+ }
- const info = state.fileInfos.get(sourceFile.resolvedPath);
- if (!info) return Debug.fail();
-
- const prevSignature = info.signature;
- let latestSignature: string | undefined;
- if (!sourceFile.isDeclarationFile && !useFileVersionAsSignature) {
- const emitOutput = getFileEmitOutput(
- programOfThisState,
- sourceFile,
- /*emitOnlyDtsFiles*/ true,
- cancellationToken,
- /*customTransformers*/ undefined,
- /*forceDtsEmit*/ true
- );
- const firstDts = firstOrUndefined(emitOutput.outputFiles);
- if (firstDts) {
- Debug.assert(fileExtensionIsOneOf(firstDts.name, [Extension.Dts, Extension.Dmts, Extension.Dcts]), "File extension for signature expected to be dts", () => `Found: ${getAnyExtensionFromPath(firstDts.name)} for ${firstDts.name}:: All output files: ${JSON.stringify(emitOutput.outputFiles.map(f => f.name))}`);
- latestSignature = (computeHash || generateDjb2Hash)(firstDts.text);
- if (exportedModulesMapCache && latestSignature !== prevSignature) {
- updateExportedModules(sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache);
- }
- }
- }
- // Default is to use file version as signature
- if (latestSignature === undefined) {
- latestSignature = sourceFile.version;
- if (exportedModulesMapCache && latestSignature !== prevSignature) {
- // All the references in this file are exported
- const references = state.referencedMap ? state.referencedMap.getValues(sourceFile.resolvedPath) : undefined;
- if (references) {
- exportedModulesMapCache.set(sourceFile.resolvedPath, references);
- }
- else {
- exportedModulesMapCache.deleteKey(sourceFile.resolvedPath);
- }
- }
- }
- cacheToUpdateSignature.set(sourceFile.resolvedPath, latestSignature);
- return latestSignature !== prevSignature;
- }
+ /**
+ * Returns if the shape of the signature has changed since last emit
+ */
+ export function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: ESMap, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ManyToManyPathMap, useFileVersionAsSignature: boolean = state.useFileVersionAsSignature) {
+ Debug.assert(!!sourceFile);
+ Debug.assert(!exportedModulesMapCache || !!state.exportedModulesMap, "Compute visible to outside map only if visibleToOutsideReferencedMap present in the state");
- /**
- * Coverts the declaration emit result into exported modules map
- */
- function updateExportedModules(sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ManyToManyPathMap) {
- if (!exportedModulesFromDeclarationEmit) {
- exportedModulesMapCache.deleteKey(sourceFile.resolvedPath);
- return;
- }
-
- let exportedModules: Set | undefined;
- exportedModulesFromDeclarationEmit.forEach(symbol => addExportedModule(getReferencedFilesFromImportedModuleSymbol(symbol)));
- if (exportedModules) {
- exportedModulesMapCache.set(sourceFile.resolvedPath, exportedModules);
- }
- else {
- exportedModulesMapCache.deleteKey(sourceFile.resolvedPath);
- }
+ // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate
+ if (state.hasCalledUpdateShapeSignature.has(sourceFile.resolvedPath) || cacheToUpdateSignature.has(sourceFile.resolvedPath)) {
+ return false;
+ }
- function addExportedModule(exportedModulePaths: Path[] | undefined) {
- if (exportedModulePaths?.length) {
- if (!exportedModules) {
- exportedModules = new Set();
- }
- exportedModulePaths.forEach(path => exportedModules!.add(path));
+ const info = state.fileInfos.get(sourceFile.resolvedPath);
+ if (!info)
+ return Debug.fail();
+
+ const prevSignature = info.signature;
+ let latestSignature: string | undefined;
+ if (!sourceFile.isDeclarationFile && !useFileVersionAsSignature) {
+ const emitOutput = getFileEmitOutput(programOfThisState, sourceFile,
+ /*emitOnlyDtsFiles*/ true, cancellationToken,
+ /*customTransformers*/ undefined,
+ /*forceDtsEmit*/ true);
+ const firstDts = firstOrUndefined(emitOutput.outputFiles);
+ if (firstDts) {
+ Debug.assert(fileExtensionIsOneOf(firstDts.name, [Extension.Dts, Extension.Dmts, Extension.Dcts]), "File extension for signature expected to be dts", () => `Found: ${getAnyExtensionFromPath(firstDts.name)} for ${firstDts.name}:: All output files: ${JSON.stringify(emitOutput.outputFiles.map(f => f.name))}`);
+ latestSignature = (computeHash || generateDjb2Hash)(firstDts.text);
+ if (exportedModulesMapCache && latestSignature !== prevSignature) {
+ updateExportedModules(sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache);
}
}
}
-
- /**
- * Updates the exported modules from cache into state's exported modules map
- * This should be called whenever it is safe to commit the state of the builder
- */
- export function updateExportedFilesMapFromCache(state: BuilderState, exportedModulesMapCache: ManyToManyPathMap | undefined) {
- if (exportedModulesMapCache) {
- Debug.assert(!!state.exportedModulesMap);
-
- const cacheId = exportedModulesMapCache.id;
- const cacheVersion = exportedModulesMapCache.version();
- if (state.previousCache) {
- if (state.previousCache.id === cacheId && state.previousCache.version === cacheVersion) {
- // If this is the same cache at the same version as last time this BuilderState
- // was updated, there's no need to update again
- return;
- }
- state.previousCache.id = cacheId;
- state.previousCache.version = cacheVersion;
+ // Default is to use file version as signature
+ if (latestSignature === undefined) {
+ latestSignature = sourceFile.version;
+ if (exportedModulesMapCache && latestSignature !== prevSignature) {
+ // All the references in this file are exported
+ const references = state.referencedMap ? state.referencedMap.getValues(sourceFile.resolvedPath) : undefined;
+ if (references) {
+ exportedModulesMapCache.set(sourceFile.resolvedPath, references);
}
else {
- state.previousCache = { id: cacheId, version: cacheVersion };
+ exportedModulesMapCache.deleteKey(sourceFile.resolvedPath);
}
-
- exportedModulesMapCache.deletedKeys()?.forEach(path => state.exportedModulesMap!.deleteKey(path));
- exportedModulesMapCache.forEach((exportedModules, path) => state.exportedModulesMap!.set(path, exportedModules));
}
}
+ cacheToUpdateSignature.set(sourceFile.resolvedPath, latestSignature);
+ return latestSignature !== prevSignature;
+ }
- /**
- * Get all the dependencies of the sourceFile
- */
- export function getAllDependencies(state: BuilderState, programOfThisState: Program, sourceFile: SourceFile): readonly string[] {
- const compilerOptions = programOfThisState.getCompilerOptions();
- // With --out or --outFile all outputs go into single file, all files depend on each other
- if (outFile(compilerOptions)) {
- return getAllFileNames(state, programOfThisState);
- }
+ /**
+ * Coverts the declaration emit result into exported modules map
+ */
+ function updateExportedModules(sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ManyToManyPathMap) {
+ if (!exportedModulesFromDeclarationEmit) {
+ exportedModulesMapCache.deleteKey(sourceFile.resolvedPath);
+ return;
+ }
- // If this is non module emit, or its a global file, it depends on all the source files
- if (!state.referencedMap || isFileAffectingGlobalScope(sourceFile)) {
- return getAllFileNames(state, programOfThisState);
+ let exportedModules: ts.Set | undefined;
+ exportedModulesFromDeclarationEmit.forEach(symbol => addExportedModule(getReferencedFilesFromImportedModuleSymbol(symbol)));
+ if (exportedModules) {
+ exportedModulesMapCache.set(sourceFile.resolvedPath, exportedModules);
+ }
+ else {
+ exportedModulesMapCache.deleteKey(sourceFile.resolvedPath);
+ }
+
+ function addExportedModule(exportedModulePaths: Path[] | undefined) {
+ if (exportedModulePaths?.length) {
+ if (!exportedModules) {
+ exportedModules = new ts.Set();
+ }
+ exportedModulePaths.forEach(path => exportedModules!.add(path));
}
+ }
+ }
- // Get the references, traversing deep from the referenceMap
- const seenMap = new Set();
- const queue = [sourceFile.resolvedPath];
- while (queue.length) {
- const path = queue.pop()!;
- if (!seenMap.has(path)) {
- seenMap.add(path);
- const references = state.referencedMap.getValues(path);
- if (references) {
- const iterator = references.keys();
- for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) {
- queue.push(iterResult.value);
- }
- }
+ /**
+ * Updates the exported modules from cache into state's exported modules map
+ * This should be called whenever it is safe to commit the state of the builder
+ */
+ export function updateExportedFilesMapFromCache(state: BuilderState, exportedModulesMapCache: ManyToManyPathMap | undefined) {
+ if (exportedModulesMapCache) {
+ Debug.assert(!!state.exportedModulesMap);
+
+ const cacheId = exportedModulesMapCache.id;
+ const cacheVersion = exportedModulesMapCache.version();
+ if (state.previousCache) {
+ if (state.previousCache.id === cacheId && state.previousCache.version === cacheVersion) {
+ // If this is the same cache at the same version as last time this BuilderState
+ // was updated, there's no need to update again
+ return;
}
+ state.previousCache.id = cacheId;
+ state.previousCache.version = cacheVersion;
+ }
+ else {
+ state.previousCache = { id: cacheId, version: cacheVersion };
}
- return arrayFrom(mapDefinedIterator(seenMap.keys(), path => programOfThisState.getSourceFileByPath(path)?.fileName ?? path));
+ exportedModulesMapCache.deletedKeys()?.forEach(path => state.exportedModulesMap!.deleteKey(path));
+ exportedModulesMapCache.forEach((exportedModules, path) => state.exportedModulesMap!.set(path, exportedModules));
}
+ }
- /**
- * Gets the names of all files from the program
- */
- function getAllFileNames(state: BuilderState, programOfThisState: Program): readonly string[] {
- if (!state.allFileNames) {
- const sourceFiles = programOfThisState.getSourceFiles();
- state.allFileNames = sourceFiles === emptyArray ? emptyArray : sourceFiles.map(file => file.fileName);
- }
- return state.allFileNames;
+ /**
+ * Get all the dependencies of the sourceFile
+ */
+ export function getAllDependencies(state: BuilderState, programOfThisState: Program, sourceFile: SourceFile): readonly string[] {
+ const compilerOptions = programOfThisState.getCompilerOptions();
+ // With --out or --outFile all outputs go into single file, all files depend on each other
+ if (outFile(compilerOptions)) {
+ return getAllFileNames(state, programOfThisState);
}
- /**
- * Gets the files referenced by the the file path
- */
- export function getReferencedByPaths(state: Readonly, referencedFilePath: Path) {
- const keys = state.referencedMap!.getKeys(referencedFilePath);
- return keys ? arrayFrom(keys.keys()) : [];
+ // If this is non module emit, or its a global file, it depends on all the source files
+ if (!state.referencedMap || isFileAffectingGlobalScope(sourceFile)) {
+ return getAllFileNames(state, programOfThisState);
}
- /**
- * For script files that contains only ambient external modules, although they are not actually external module files,
- * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore,
- * there are no point to rebuild all script files if these special files have changed. However, if any statement
- * in the file is not ambient external module, we treat it as a regular script file.
- */
- function containsOnlyAmbientModules(sourceFile: SourceFile) {
- for (const statement of sourceFile.statements) {
- if (!isModuleWithStringLiteralName(statement)) {
- return false;
+ // Get the references, traversing deep from the referenceMap
+ const seenMap = new ts.Set();
+ const queue = [sourceFile.resolvedPath];
+ while (queue.length) {
+ const path = queue.pop()!;
+ if (!seenMap.has(path)) {
+ seenMap.add(path);
+ const references = state.referencedMap.getValues(path);
+ if (references) {
+ const iterator = references.keys();
+ for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) {
+ queue.push(iterResult.value);
+ }
}
}
- return true;
}
- /**
- * Return true if file contains anything that augments to global scope we need to build them as if
- * they are global files as well as module
- */
- function containsGlobalScopeAugmentation(sourceFile: SourceFile) {
- return some(sourceFile.moduleAugmentations, augmentation => isGlobalScopeAugmentation(augmentation.parent as ModuleDeclaration));
- }
+ return arrayFrom(mapDefinedIterator(seenMap.keys(), path => programOfThisState.getSourceFileByPath(path)?.fileName ?? path));
+ }
- /**
- * Return true if the file will invalidate all files because it affectes global scope
- */
- function isFileAffectingGlobalScope(sourceFile: SourceFile) {
- return containsGlobalScopeAugmentation(sourceFile) ||
- !isExternalOrCommonJsModule(sourceFile) && !isJsonSourceFile(sourceFile) && !containsOnlyAmbientModules(sourceFile);
+ /**
+ * Gets the names of all files from the program
+ */
+ function getAllFileNames(state: BuilderState, programOfThisState: Program): readonly string[] {
+ if (!state.allFileNames) {
+ const sourceFiles = programOfThisState.getSourceFiles();
+ state.allFileNames = sourceFiles === emptyArray ? emptyArray : sourceFiles.map(file => file.fileName);
}
+ return state.allFileNames;
+ }
- /**
- * Gets all files of the program excluding the default library file
- */
- export function getAllFilesExcludingDefaultLibraryFile(state: BuilderState, programOfThisState: Program, firstSourceFile: SourceFile | undefined): readonly SourceFile[] {
- // Use cached result
- if (state.allFilesExcludingDefaultLibraryFile) {
- return state.allFilesExcludingDefaultLibraryFile;
- }
+ /**
+ * Gets the files referenced by the the file path
+ */
+ export function getReferencedByPaths(state: Readonly, referencedFilePath: Path) {
+ const keys = state.referencedMap!.getKeys(referencedFilePath);
+ return keys ? arrayFrom(keys.keys()) : [];
+ }
- let result: SourceFile[] | undefined;
- if (firstSourceFile) addSourceFile(firstSourceFile);
- for (const sourceFile of programOfThisState.getSourceFiles()) {
- if (sourceFile !== firstSourceFile) {
- addSourceFile(sourceFile);
- }
+ /**
+ * For script files that contains only ambient external modules, although they are not actually external module files,
+ * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore,
+ * there are no point to rebuild all script files if these special files have changed. However, if any statement
+ * in the file is not ambient external module, we treat it as a regular script file.
+ */
+ function containsOnlyAmbientModules(sourceFile: SourceFile) {
+ for (const statement of sourceFile.statements) {
+ if (!isModuleWithStringLiteralName(statement)) {
+ return false;
}
- state.allFilesExcludingDefaultLibraryFile = result || emptyArray;
+ }
+ return true;
+ }
+
+ /**
+ * Return true if file contains anything that augments to global scope we need to build them as if
+ * they are global files as well as module
+ */
+ function containsGlobalScopeAugmentation(sourceFile: SourceFile) {
+ return some(sourceFile.moduleAugmentations, augmentation => isGlobalScopeAugmentation(augmentation.parent as ModuleDeclaration));
+ }
+
+ /**
+ * Return true if the file will invalidate all files because it affectes global scope
+ */
+ function isFileAffectingGlobalScope(sourceFile: SourceFile) {
+ return containsGlobalScopeAugmentation(sourceFile) ||
+ !isExternalOrCommonJsModule(sourceFile) && !isJsonSourceFile(sourceFile) && !containsOnlyAmbientModules(sourceFile);
+ }
+
+ /**
+ * Gets all files of the program excluding the default library file
+ */
+ export function getAllFilesExcludingDefaultLibraryFile(state: BuilderState, programOfThisState: Program, firstSourceFile: SourceFile | undefined): readonly SourceFile[] {
+ // Use cached result
+ if (state.allFilesExcludingDefaultLibraryFile) {
return state.allFilesExcludingDefaultLibraryFile;
+ }
- function addSourceFile(sourceFile: SourceFile) {
- if (!programOfThisState.isSourceFileDefaultLibrary(sourceFile)) {
- (result || (result = [])).push(sourceFile);
- }
+ let result: SourceFile[] | undefined;
+ if (firstSourceFile)
+ addSourceFile(firstSourceFile);
+ for (const sourceFile of programOfThisState.getSourceFiles()) {
+ if (sourceFile !== firstSourceFile) {
+ addSourceFile(sourceFile);
}
}
+ state.allFilesExcludingDefaultLibraryFile = result || emptyArray;
+ return state.allFilesExcludingDefaultLibraryFile;
- /**
- * When program emits non modular code, gets the files affected by the sourceFile whose shape has changed
- */
- function getFilesAffectedByUpdatedShapeWhenNonModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) {
- const compilerOptions = programOfThisState.getCompilerOptions();
- // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project,
- // so returning the file itself is good enough.
- if (compilerOptions && outFile(compilerOptions)) {
- return [sourceFileWithUpdatedShape];
+ function addSourceFile(sourceFile: SourceFile) {
+ if (!programOfThisState.isSourceFileDefaultLibrary(sourceFile)) {
+ (result || (result = [])).push(sourceFile);
}
- return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape);
}
+ }
+
+ /**
+ * When program emits non modular code, gets the files affected by the sourceFile whose shape has changed
+ */
+ function getFilesAffectedByUpdatedShapeWhenNonModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) {
+ const compilerOptions = programOfThisState.getCompilerOptions();
+ // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project,
+ // so returning the file itself is good enough.
+ if (compilerOptions && outFile(compilerOptions)) {
+ return [sourceFileWithUpdatedShape];
+ }
+ return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape);
+ }
- /**
- * When program emits modular code, gets the files affected by the sourceFile whose shape has changed
- */
- function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: ESMap, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache: ManyToManyPathMap | undefined) {
- if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) {
- return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape);
- }
+ /**
+ * When program emits modular code, gets the files affected by the sourceFile whose shape has changed
+ */
+ function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: ESMap, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache: ManyToManyPathMap | undefined) {
+ if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) {
+ return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape);
+ }
- const compilerOptions = programOfThisState.getCompilerOptions();
- if (compilerOptions && (compilerOptions.isolatedModules || outFile(compilerOptions))) {
- return [sourceFileWithUpdatedShape];
- }
+ const compilerOptions = programOfThisState.getCompilerOptions();
+ if (compilerOptions && (compilerOptions.isolatedModules || outFile(compilerOptions))) {
+ return [sourceFileWithUpdatedShape];
+ }
- // Now we need to if each file in the referencedBy list has a shape change as well.
- // Because if so, its own referencedBy files need to be saved as well to make the
- // emitting result consistent with files on disk.
- const seenFileNamesMap = new Map();
-
- // Start with the paths this file was referenced by
- seenFileNamesMap.set(sourceFileWithUpdatedShape.resolvedPath, sourceFileWithUpdatedShape);
- const queue = getReferencedByPaths(state, sourceFileWithUpdatedShape.resolvedPath);
- while (queue.length > 0) {
- const currentPath = queue.pop()!;
- if (!seenFileNamesMap.has(currentPath)) {
- const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath)!;
- seenFileNamesMap.set(currentPath, currentSourceFile);
- if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken, computeHash, exportedModulesMapCache)) {
- queue.push(...getReferencedByPaths(state, currentSourceFile.resolvedPath));
- }
+ // Now we need to if each file in the referencedBy list has a shape change as well.
+ // Because if so, its own referencedBy files need to be saved as well to make the
+ // emitting result consistent with files on disk.
+ const seenFileNamesMap = new ts.Map();
+
+ // Start with the paths this file was referenced by
+ seenFileNamesMap.set(sourceFileWithUpdatedShape.resolvedPath, sourceFileWithUpdatedShape);
+ const queue = getReferencedByPaths(state, sourceFileWithUpdatedShape.resolvedPath);
+ while (queue.length > 0) {
+ const currentPath = queue.pop()!;
+ if (!seenFileNamesMap.has(currentPath)) {
+ const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath)!;
+ seenFileNamesMap.set(currentPath, currentSourceFile);
+ if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken, computeHash, exportedModulesMapCache)) {
+ queue.push(...getReferencedByPaths(state, currentSourceFile.resolvedPath));
}
}
-
- // Return array of values that needs emit
- return arrayFrom(mapDefinedIterator(seenFileNamesMap.values(), value => value));
}
+
+ // Return array of values that needs emit
+ return arrayFrom(mapDefinedIterator(seenFileNamesMap.values(), value => value));
}
}
diff --git a/src/compiler/builderStatePublic.ts b/src/compiler/builderStatePublic.ts
index ce542b0825b80..104fd708734f7 100644
--- a/src/compiler/builderStatePublic.ts
+++ b/src/compiler/builderStatePublic.ts
@@ -1,14 +1,13 @@
-namespace ts {
- export interface EmitOutput {
- outputFiles: OutputFile[];
- emitSkipped: boolean;
- /* @internal */ diagnostics: readonly Diagnostic[];
- /* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit;
- }
+import { Diagnostic, ExportedModulesFromDeclarationEmit } from "./ts";
+export interface EmitOutput {
+ outputFiles: OutputFile[];
+ emitSkipped: boolean;
+ /* @internal */ diagnostics: readonly Diagnostic[];
+ /* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit;
+}
- export interface OutputFile {
- name: string;
- writeByteOrderMark: boolean;
- text: string;
- }
+export interface OutputFile {
+ name: string;
+ writeByteOrderMark: boolean;
+ text: string;
}
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index ad9c11d7178c7..df9b83acf3e0a 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -1,23999 +1,23584 @@
+import { __String, GenericType, Type, Node, DiagnosticMessage, ReadonlyESMap, getEntries, Symbol, Signature, DiagnosticWithLocation, and, SymbolId, ModuleDeclaration, getModuleInstanceState, ModuleInstanceState, TypeCheckerHost, TypeChecker, memoize, Extension, CancellationToken, ExternalEmitHelpers, objectAllocator, createSymbolTable, VarianceFlags, getEmitScriptTarget, getEmitModuleKind, getUseDefineForClassFields, getAllowSyntheticDefaultImports, getStrictOptionValue, ObjectFlags, SymbolFlags, CheckFlags, sum, getParseTreeNode, isParameter, Debug, escapeLeadingUnderscores, IndexKind, isTypeNode, isExportSpecifier, isAssignmentPattern, isIdentifier, Expression, ContextFlags, isExpression, findAncestor, isCallLikeExpression, isObjectLiteralElementLike, isJsxAttributeLike, isPropertyAccessOrQualifiedNameOrImportTypeNode, isPropertyAccessExpression, isFunctionLike, createGetSymbolWalker, getFirstIdentifier, TypeFlags, TypeParameter, unescapeLeadingUnderscores, isSourceFile, skipTypeChecking, emptyArray, NodeCheckFlags, addRange, containsParseError, NodeFlags, DiagnosticCategory, CallLikeExpression, UnionType, StringLiteralType, NumberLiteralType, BigIntLiteralType, LiteralType, IndexedAccessType, TemplateLiteralType, StringMappingType, SubstitutionType, EvolvingArrayType, SymbolTable, InternalSymbolName, TransientSymbol, FreshableIntrinsicType, TypeMapper, ObjectType, TypeReference, TypePredicateKind, SignatureFlags, IterationTypes, Diagnostics, Declaration, SourceFile, ESMap, PatternAmbientModule, Path, FlowNode, FlowType, createDiagnosticCollection, EntityName, RelationComparisonResult, JsxEmit, getSourceFileOfNode, isJsxOpeningFragment, isArray, parseIsolatedEntityName, visitNode, factory, VisitResult, setTextRangePosEnd, visitEachChild, nullTransformationContext, Diagnostic, createDiagnosticForNode, createCompilerDiagnostic, CompilerOptions, DiagnosticMessageChain, createFileDiagnostic, createDiagnosticForFileFromMessageChain, createDiagnosticForNodeFromMessageChain, addRelatedInfo, forEach, getJSDocDeprecatedTag, setValueDeclaration, getNameOfDeclaration, comparePaths, Comparison, getOrUpdate, pushIfUnique, getExpandoInitializer, getNameOfExpando, length, some, compareDiagnostics, StringLiteral, Identifier, isGlobalScopeAugmentation, arrayFrom, SyntaxKind, isExternalOrCommonJsModule, getCheckFlags, ParameterDeclaration, getEnclosingBlockScopeContainer, outFile, isPropertyDeclaration, isThisProperty, getAncestor, BindingElement, isBindingElement, VariableDeclaration, isClassDeclaration, isComputedPropertyName, isParameterPropertyDeclaration, ScriptTarget, getContainingClass, ExportAssignment, isInterfaceDeclaration, isTypeAliasDeclaration, isForInOrOfStatement, isClassStaticBlockDeclaration, tryCast, isStatic, isPrivateIdentifier, filter, PropertyDeclaration, ParameterPropertyDeclaration, FunctionLikeDeclaration, MethodDeclaration, AccessorDeclaration, PropertyAssignment, hasStaticModifier, isNullishCoalesce, isOptionalChain, isObjectBindingPattern, forEachChild, ConditionalTypeNode, isModuleDeclaration, getLocalSymbolForExportDefault, getDeclarationOfKind, isJSDocTypeAlias, ClassLikeDeclaration, InterfaceDeclaration, ClassExpression, ExpressionWithTypeArguments, HeritageClause, isClassLike, FunctionExpression, isClassElement, getJSDocRoot, isBindingPattern, isParameterDeclaration, InferTypeNode, isJSDocTemplateTag, getEffectiveContainerForJSDocTemplateTag, isInJSFile, isRequireCall, isAmbientModule, nodeIsSynthesized, declarationNameToString, every, isNamespaceExportDeclaration, getRootDeclaration, isValidTypeOnlyAliasUseSite, TypeOnlyCompatibleAliasDeclaration, isTypeQueryNode, isFunctionLikeDeclaration, SignatureDeclaration, ArrowFunction, hasSyntacticModifier, ModifierFlags, getImmediatelyInvokedFunctionExpression, PrivateIdentifier, isString, getJSDocHost, find, JSDoc, getThisContainer, InterfaceType, getTextOfNode, EntityNameExpression, isEntityNameExpression, isQualifiedName, isPropertySignature, isTypeLiteralNode, TypeLiteralNode, isBlockOrCatchScoped, shouldPreserveConstEnums, AnyImportSyntax, ImportEqualsDeclaration, ImportClause, NamespaceImport, ImportSpecifier, findLast, exportAssignmentIsAlias, isBinaryExpression, getAssignmentDeclarationKind, AssignmentDeclarationKind, isAccessExpression, isRequireVariableDeclaration, isAliasableExpression, isFunctionExpression, getLeftmostAccessExpression, CallExpression, isVariableDeclaration, getExternalModuleRequireArgument, getExternalModuleImportEqualsDeclarationExpression, isExportAssignment, isStringLiteralLike, getModeForUsageLocation, ModuleKind, endsWith, StringLiteralLike, isSourceFileJS, isShorthandAmbientModuleSymbol, isExportDeclaration, NamespaceExport, deduplicate, concatenate, equateValues, ImportDeclaration, ExportDeclaration, ImportOrExportSpecifier, PropertyAccessExpression, getESModuleInterop, map, NamespaceExportDeclaration, ExportSpecifier, BinaryExpression, isClassExpression, isEntityName, AccessExpression, ShorthandPropertyAssignment, isTypeOnlyImportOrExportDeclaration, TypeOnlyAliasDeclaration, isInternalModuleImportEqualsDeclaration, isRightSideOfQualifiedNameOrPropertyAccess, QualifiedName, SymbolFormatFlags, EntityNameOrEntityNameExpression, nodeIsMissing, isTypeOfExpression, entityNameToString, getAliasDeclarationFromName, TypeReferenceNode, isJSDocNode, isExpressionStatement, isObjectLiteralMethod, isPropertyAssignment, getEffectiveJSDocHost, isAssignmentDeclaration, getAssignedExpandoInitializer, hasOnlyExpressionInitializer, getDeclaredExpandoInitializer, getEmitModuleResolutionKind, ModuleResolutionKind, startsWith, removePrefix, isImportCall, isImportDeclaration, isExternalModuleImportEqualsDeclaration, isLiteralImportTypeNode, getResolvedModule, getResolutionDiagnostic, resolutionExtensionIsTSOrJson, isImportEqualsDeclaration, findBestPatternMatch, tryExtractTSExtension, pathIsRelative, hasExtension, removeExtension, fileExtensionIs, hasJsonModuleEmitEnabled, getNormalizedAbsolutePath, getDirectoryPath, ResolvedModuleFull, isExternalModuleNameRelative, chainDiagnosticMessages, mangleScopedPackageName, getTypesPackageName, getNamespaceDeclarationNode, SignatureKind, ImportCall, StructuredType, getObjectFlags, UnderscoreEscapedMap, append, isExternalModule, mapDefined, forEachEntry, isModuleExportsAccessExpression, isExportsIdentifier, first, isObjectLiteralExpression, ConstructorDeclaration, nodeIsPresent, tracing, IntrinsicType, CharacterCodes, IndexInfo, ResolvedType, isUMDExportSymbol, isNamespaceReexportDeclaration, SymbolAccessibility, SymbolAccessibilityResult, isModuleWithStringLiteralName, SymbolVisibilityResult, LateVisibilityPaintedStatement, isVariableStatement, isLateVisibilityPaintedStatement, appendIfUnique, isExpressionWithTypeArgumentsInClassExtendsClause, EmitTextWriter, NodeBuilderFlags, usingSingleLineStringWriter, createPrinter, EmitHint, TypeFormatFlags, getTrailingSemicolonDeferringWriter, createTextWriter, noTruncationMaximumTruncationLength, defaultMaximumTruncationLength, SymbolTracker, Program, maybeBind, TypeNode, addSyntheticLeadingComment, symbolName, isIdentifierText, ImportTypeNode, isImportTypeNode, isTypeReferenceNode, setEmitFlags, EmitFlags, pseudoBigIntToString, isThisTypeParameter, contains, idText, IntersectionType, IndexType, ConditionalType, MappedType, ReadonlyKeyword, PlusToken, MinusToken, QuestionToken, setTextRange, FunctionTypeNode, ConstructorTypeNode, countWhere, sameMap, TupleType, ElementFlags, rangeEquals, TypeElement, CallSignatureDeclaration, ConstructSignatureDeclaration, getDeclarationModifierFlagsFromSymbol, ReverseMappedSymbol, last, isElementAccessExpression, isPropertyAccessEntityNameExpression, MethodSignature, JSDocPropertyTag, getTextOfJSDocComment, setSyntheticLeadingComments, setCommentRange, createUnderscoreEscapedMultiMap, isIdentifierTypeReference, arrayIsHomogeneous, IndexSignatureDeclaration, getNameFromIndexInfo, Modifier, PropertyName, TypeParameterDeclaration, modifiersToFlags, cast, getJSDocThisTag, JSDocParameterTag, isTransientSymbol, isJSDocParameterTag, isRestParameter, BindingName, NodeArray, IndexedAccessTypeNode, isIndexedAccessTypeNode, firstDefined, getNonAugmentationDeclaration, getOriginalNode, isSingleOrDoubleQuote, isIdentifierStart, stripQuotes, isStringLiteral, createPropertyNameNodeForIdentifierOrLiteral, isNumericLiteralName, UniqueESSymbolType, getEffectiveTypeAnnotationNode, isGetAccessorDeclaration, getEffectiveReturnTypeNode, isModuleIdentifier, setOriginalNode, isJSDocAllType, isJSDocUnknownType, isJSDocNullableType, isJSDocOptionalType, isJSDocNonNullableType, isJSDocVariadicType, JSDocVariadicType, isJSDocTypeLiteral, isExpressionWithTypeArguments, isJSDocIndexSignature, isJSDocFunctionType, isJSDocConstructSignature, visitNodes, isInJSDoc, isTupleTypeNode, getLineAndCharacterOfPosition, createGetCanonicalFileName, getResolvedExternalModuleName, Statement, ClassElement, DeclarationStatement, findIndex, isModuleBlock, getEffectiveModifierFlags, flatMap, nodeHasName, isNamedExports, group, NamedExports, indicesOf, HasModifiers, orderedRemoveItemAt, isExternalModuleIndicator, hasScopeMarker, needsScopeMarker, createEmptyExports, isEnumDeclaration, isFunctionDeclaration, isExternalModuleAugmentation, isStringANonContextualKeyword, isVariableDeclarationList, isNamespaceExport, isExternalModuleReference, isJsonSourceFile, canHaveModifiers, isJSDocTypeExpression, arrayToMultiMap, isEnumMember, FunctionDeclaration, parseNodeFactory, setParent, NamespaceDeclaration, getEffectiveImplementsTypeNodes, isNamedDeclaration, isImportSpecifier, getExportAssignmentExpression, getPropertyAssignmentAliasLikeExpression, Decorator, or, isAccessor, isSetAccessor, isGetAccessor, isPrototypePropertyAssignment, getSelectedEffectiveModifierFlags, TypePredicate, walkUpParenthesizedTypes, TypeId, escapeString, isCallExpression, isBindableObjectDefinePropertyCall, BindingPattern, getCombinedModifierFlags, hasEffectiveModifier, tryAddToSet, JSDocEnumTag, BindingElementGrandparent, ElementAccessExpression, isLeftHandSideExpression, ArrayLiteralExpression, AccessFlags, TupleTypeReference, walkUpBindingElementsAndPatterns, UnionReduction, getJSDocType, skipParentheses, PropertySignature, JSDocPropertyLikeTag, getCombinedNodeFlags, isFunctionTypeNode, isJsxAttribute, isStringOrNumericLiteralLike, ClassStaticBlockDeclaration, getJSDocTypeTag, getAssignmentDeclarationPropertyAccessKind, BindableObjectDefinePropertyCall, copyEntries, forEachChildRecursively, ObjectBindingPattern, lastOrUndefined, isOmittedExpression, findLastIndex, VariableLikeDeclaration, isCatchClauseVariableDeclarationOrBindingElement, isBindableStaticElementAccessExpression, isNumericLiteral, isMethodDeclaration, isMethodSignature, isShorthandPropertyAssignment, isJSDocPropertyLikeTag, getEffectiveSetAccessorTypeAnnotationNode, HasInitializer, MappedSymbol, MappedTypeNode, getEffectiveTypeParameterDeclarations, DeclarationWithTypeParameters, getParameterSymbolFromJSDoc, isTypeAlias, TypeAliasDeclaration, JSDocTypedefTag, JSDocCallbackTag, getClassLikeDeclarationOfSymbol, getEffectiveBaseTypeNode, BaseType, resolvingEmptyArray, getInterfaceBaseTypeNodes, EnumMember, PrefixUnaryExpression, EnumKind, EnumDeclaration, ArrayTypeNode, getEffectiveConstraintOfTypeParameter, hasInitializer, isPrivateIdentifierClassElementDeclaration, InterfaceTypeWithDeclaredMembers, DeclarationName, LateBoundName, LateBoundDeclaration, LateBoundBinaryExpressionDeclaration, hasDynamicName, isDynamicName, getMembersOfDeclaration, JSDocSignature, reduceLeft, AnonymousType, ReverseMappedType, TypeOperatorNode, UnionOrIntersectionType, ObjectLiteralExpression, JsxAttributes, ObjectLiteralElementLike, JsxAttributeLike, InstantiableType, isNodeDescendantOf, isTypeParameterDeclaration, Ternary, getJSDocParameterTags, hasQuestionToken, isValueSignatureDeclaration, hasJSDocParameterTags, ClassDeclaration, hasRestParameter, isConstructorTypeNode, isConstructorDeclaration, isJSDocSignature, getJSDocTags, NamedDeclaration, nodeStartsNewLexicalEnvironment, isPartOfTypeNode, isTypePredicateNode, TypePredicateNode, walkUpParenthesizedTypesAndGetParentAndChild, NamedTupleMember, TupleTypeNode, DeferredTypeReference, NodeWithTypeArguments, isJSDocAugmentsTag, getContainingFunction, TypeReferenceType, isStatement, JSDocNullableType, isConstTypeReference, isAssertionExpression, TypeQueryNode, isThisIdentifier, RestTypeNode, ParenthesizedTypeNode, OptionalTypeNode, JSDocTypeReferencingNode, UnionOrIntersectionTypeNode, isTypeOperatorNode, replaceElement, arrayOf, binarySearch, compareValues, UnionTypeNode, IntersectionTypeNode, isKnownSymbol, TemplateLiteralTypeNode, ArrayBindingPattern, ComputedPropertyName, NumericLiteral, SyntheticExpression, isPropertyName, getPropertyNameForPropertyNameNode, isCallOrNewExpression, getAssignmentTargetKind, AssignmentKind, isAssignmentTarget, isDeleteTarget, ConditionalRoot, isOptionalTypeNode, isRestTypeNode, InferenceFlags, InferencePriority, isParenthesizedTypeNode, PseudoBigInt, LiteralTypeNode, isValidESSymbolDeclaration, getHostSignatureFromJSDoc, ThisExpression, ThisTypeNode, TypeNodeSyntaxKind, JSDocOptionalType, JSDocTypeExpression, TypeMapKind, InferenceContext, TypeVariable, JsxChild, ConditionalExpression, ParenthesizedExpression, isJsxOpeningElement, JsxAttribute, JsxExpression, hasContextSensitiveParameters, isFunctionExpressionOrArrowFunction, isBlock, getFunctionFlags, FunctionFlags, isJsxSpreadAttribute, JsxElement, isJsxElement, getSemanticJsxChildren, formatMessage, isSpreadAssignment, isComputedNonLiteralName, TypeComparer, isIdentifierTypePredicate, IdentifierTypePredicate, FreshableType, DiagnosticRelatedInformation, concatenateDiagnosticMessageChains, FreshObjectLiteralType, isJsxAttributes, isJsxOpeningLikeElement, firstOrUndefined, cartesianProduct, getSymbolNameForPrivateIdentifier, OptionalChain, isOutermostOptionalChain, isExpressionOfOptionalChainRoot, WideningContext, isCheckJsEnabledForFile, isCallSignatureDeclaration, isTypeNodeKind, InferenceInfo, ObjectFlagsType, createScanner, TokenFlags, arraysEqual, isWriteOnlyAccess, isThisInTypeQuery, NonNullExpression, isAssignmentExpression, MetaProperty, NewExpression, ForOfStatement, SpreadElement, CaseClause, DefaultClause, SwitchStatement, IncompleteType, isPushOrUnshiftIdentifier, isFunctionOrModuleBlock, Block, ModuleBlock, getSpanOfTokenAtPosition, FlowFlags, FlowAssignment, FlowCondition, FlowArrayMutation, FlowCall, FlowLabel, FlowSwitchClause, FlowReduceLabel, isParameterOrCatchClauseVariable, FlowStart, isVarConst, TypeOfExpression, LiteralExpression, isCallChain, isExpressionNode, isWriteAccess, isDeclarationName, isCatchClause, isJsxSelfClosingElement, getThisParameter, nodeIsDecorated, isObjectLiteralOrClassExpressionMethodOrAccessor, ForStatement, isIterationStatement, isForStatement, PostfixUnaryExpression, SuperCall, isSuperCall, getClassExtendsHeritageElement, textRangeContainsPositionInclusive, JSDocFunctionType, getSuperContainer, forEachEnclosingBlockScopeContainer, isSuperProperty, walkUpParenthesizedExpressions, indexOfNode, AwaitExpression, YieldExpression, TemplateExpression, TaggedTemplateExpression, isDefaultedExpandoInitializer, getElementOrPropertyAccessName, isThisInitializedDeclaration, JsxSpreadAttribute, AssertionExpression, isJSDocTypeTag, JsxOpeningLikeElement, JsxReferenceKind, parameterIsThisKeyword, isInJsonFile, getJSDocEnumTag, JsxSelfClosingElement, JsxFragment, getJSXTransformEnabled, stringContains, JsxTagNameExpression, isIntrinsicJsxName, SpreadAssignment, JsxClosingElement, JsxFlags, getJSXRuntimeImport, getJSXImplicitImportBase, JsxOpeningFragment, isThisInitializedObjectBindingExpression, getTextOfIdentifierOrLiteral, PropertyAccessChain, isPartOfTypeQuery, isForInStatement, ScriptKind, getScriptTargetFeatures, getOwnKeys, tryGetPropertyAccessOrIdentifierToString, getSpellingSuggestion, ForInStatement, VariableDeclarationList, ElementAccessChain, JsxOpeningElement, isTaggedTemplateExpression, LeftHandSideExpression, isOptionalChainRoot, skipOuterExpressions, getErrorSpanForNode, isNewExpression, createDiagnosticForNodeArray, flatten, minAndMax, isLineBreak, skipTrivia, getJSDocClassTag, isArrayLiteralExpression, isBindableStaticNameExpression, isSameEntityName, isPrototypeAccess, getInitializerOfBinaryExpression, isDottedName, getInvokedExpression, SyntheticDefaultModuleType, fileExtensionIsOneOf, UnaryExpression, NonNullChain, getNewTargetContainer, forEachYieldExpression, forEachReturnStatement, OuterExpressionKinds, DeleteExpression, VoidExpression, getContainingFunctionOrClassStaticBlock, isInTopLevelContext, TextSpan, isEffectiveExternalModule, parsePseudoBigInt, BigIntLiteral, tokenToString, isPrivateIdentifierPropertyAccessExpression, isAssignmentOperator, createBinaryExpressionTrampoline, BinaryOperatorToken, isIfStatement, textSpanContainsPosition, isJSDocTypedefTag, expressionResultIsUnused, isParenthesizedExpression, isJSDocTypeAssertion, HasExpressionInitializer, getEffectiveInitializer, isDeclarationReadonly, getJSDocTypeAssertionType, isSpreadElement, isTemplateSpan, CallChain, setNodeFlags, TypeAssertion, hasJSDocNodes, ExpressionStatement, isPrologueDirective, isTypeReferenceType, isNamedTupleMember, isPropertyNameLiteral, getEscapedTextOfIdentifierOrLiteral, PromiseOrAwaitableType, getEntityNameFromTypeNode, getRestParameterElementType, nodeCanBeDecorated, getFirstConstructorWithBody, JSDocTemplateTag, JSDocTypeTag, JSDocImplementsTag, JSDocAugmentsTag, JSDocPublicTag, JSDocProtectedTag, JSDocPrivateTag, CaseBlock, DeclarationWithTypeParameterChildren, rangeOfNode, rangeOfTypeParameters, isArrayBindingPattern, isImportClause, isVariableLike, VariableStatement, IfStatement, DoStatement, WhileStatement, ForInOrOfStatement, MatchingKeys, IterableOrIteratorType, BreakOrContinueStatement, ReturnStatement, WithStatement, CaseOrDefaultClause, LabeledStatement, ThrowStatement, TryStatement, forEachKey, DynamicNamedDeclaration, hasAmbientModifier, MemberOverrideStatus, hasOverrideModifier, hasAbstractModifier, getTextOfPropertyName, isInfinityOrNaNString, isEnumConst, isLiteralExpression, getExternalModuleName, hasEffectiveModifiers, forEachImportClauseDeclaration, getEmitDeclarations, JSDocContainer, isJSDocCallbackTag, clear, ImportsNotUsedAsValues, relativeComplement, introducesArgumentsExoticObject, getCombinedLocalAndExportSymbolFlags, JSDocMemberName, isJSDocMemberName, isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName, PropertyAccessEntityNameExpression, getTypeParameterFromJsDoc, isJSDocLinkLike, isJSDocNameReference, isJSXTagName, isImportOrExportSpecifier, isLiteralComputedPropertyDeclarationName, isMetaProperty, isInExpressionContext, isLiteralTypeNode, tryGetClassImplementingOrExtendingExpressionWithTypeArguments, isDeclaration, AssignmentPattern, singleElementArray, isGeneratedIdentifier, isModuleOrEnumDeclaration, isStatementWithLocals, isBlockScopedContainerTopLevel, TypeReferenceSerializationKind, isVariableLikeOrAccessor, KeywordTypeNode, EmitResolver, hasPossibleExternalModuleReference, AllAccessorDeclarations, isGetOrSetAccessorDeclaration, SetAccessorDeclaration, GetAccessorDeclaration, resolveTripleslashReference, AnyImportOrReExport, bindSourceFile, externalHelpersModuleNameText, getAllAccessorDeclarations, modifierToFlag, findUseStrictPrologue, isArrowFunction, ExclamationToken, MemberName, isCommaSequence, isForOfStatement, getSetAccessorValueParameter, isVariableDeclarationInVariableStatement, hasEffectiveReadonlyModifier, isFunctionLikeOrClassStaticBlockDeclaration, isLet, getJSDocTypeParameterDeclarations, isChildOfNodeWithKind, isPrefixUnaryExpression, textSpanEnd, NamedImportsOrExports } from "./ts";
+import { countPathComponents, getModuleSpecifiers } from "./ts.moduleSpecifiers";
+import { mark, measure } from "./ts.performance";
+import * as ts from "./ts";
/* @internal */
-namespace ts {
- const ambientModuleSymbolRegex = /^".+"$/;
- const anon = "(anonymous)" as __String & string;
-
- let nextSymbolId = 1;
- let nextNodeId = 1;
- let nextMergeId = 1;
- let nextFlowId = 1;
-
- const enum IterationUse {
- AllowsSyncIterablesFlag = 1 << 0,
- AllowsAsyncIterablesFlag = 1 << 1,
- AllowsStringInputFlag = 1 << 2,
- ForOfFlag = 1 << 3,
- YieldStarFlag = 1 << 4,
- SpreadFlag = 1 << 5,
- DestructuringFlag = 1 << 6,
- PossiblyOutOfBounds = 1 << 7,
-
- // Spread, Destructuring, Array element assignment
- Element = AllowsSyncIterablesFlag,
- Spread = AllowsSyncIterablesFlag | SpreadFlag,
- Destructuring = AllowsSyncIterablesFlag | DestructuringFlag,
-
- ForOf = AllowsSyncIterablesFlag | AllowsStringInputFlag | ForOfFlag,
- ForAwaitOf = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag | AllowsStringInputFlag | ForOfFlag,
-
- YieldStar = AllowsSyncIterablesFlag | YieldStarFlag,
- AsyncYieldStar = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag | YieldStarFlag,
-
- GeneratorReturnType = AllowsSyncIterablesFlag,
- AsyncGeneratorReturnType = AllowsAsyncIterablesFlag,
-
- }
-
- const enum IterationTypeKind {
- Yield,
- Return,
- Next,
- }
-
- interface IterationTypesResolver {
- iterableCacheKey: "iterationTypesOfAsyncIterable" | "iterationTypesOfIterable";
- iteratorCacheKey: "iterationTypesOfAsyncIterator" | "iterationTypesOfIterator";
- iteratorSymbolName: "asyncIterator" | "iterator";
- getGlobalIteratorType: (reportErrors: boolean) => GenericType;
- getGlobalIterableType: (reportErrors: boolean) => GenericType;
- getGlobalIterableIteratorType: (reportErrors: boolean) => GenericType;
- getGlobalGeneratorType: (reportErrors: boolean) => GenericType;
- resolveIterationType: (type: Type, errorNode: Node | undefined) => Type | undefined;
- mustHaveANextMethodDiagnostic: DiagnosticMessage;
- mustBeAMethodDiagnostic: DiagnosticMessage;
- mustHaveAValueDiagnostic: DiagnosticMessage;
- }
-
- const enum WideningKind {
- Normal,
- FunctionReturn,
- GeneratorNext,
- GeneratorYield,
- }
-
- const enum TypeFacts {
- None = 0,
- TypeofEQString = 1 << 0, // typeof x === "string"
- TypeofEQNumber = 1 << 1, // typeof x === "number"
- TypeofEQBigInt = 1 << 2, // typeof x === "bigint"
- TypeofEQBoolean = 1 << 3, // typeof x === "boolean"
- TypeofEQSymbol = 1 << 4, // typeof x === "symbol"
- TypeofEQObject = 1 << 5, // typeof x === "object"
- TypeofEQFunction = 1 << 6, // typeof x === "function"
- TypeofEQHostObject = 1 << 7, // typeof x === "xxx"
- TypeofNEString = 1 << 8, // typeof x !== "string"
- TypeofNENumber = 1 << 9, // typeof x !== "number"
- TypeofNEBigInt = 1 << 10, // typeof x !== "bigint"
- TypeofNEBoolean = 1 << 11, // typeof x !== "boolean"
- TypeofNESymbol = 1 << 12, // typeof x !== "symbol"
- TypeofNEObject = 1 << 13, // typeof x !== "object"
- TypeofNEFunction = 1 << 14, // typeof x !== "function"
- TypeofNEHostObject = 1 << 15, // typeof x !== "xxx"
- EQUndefined = 1 << 16, // x === undefined
- EQNull = 1 << 17, // x === null
- EQUndefinedOrNull = 1 << 18, // x === undefined / x === null
- NEUndefined = 1 << 19, // x !== undefined
- NENull = 1 << 20, // x !== null
- NEUndefinedOrNull = 1 << 21, // x != undefined / x != null
- Truthy = 1 << 22, // x
- Falsy = 1 << 23, // !x
- All = (1 << 24) - 1,
- // The following members encode facts about particular kinds of types for use in the getTypeFacts function.
- // The presence of a particular fact means that the given test is true for some (and possibly all) values
- // of that kind of type.
- BaseStringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
- BaseStringFacts = BaseStringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
- StringStrictFacts = BaseStringStrictFacts | Truthy | Falsy,
- StringFacts = BaseStringFacts | Truthy,
- EmptyStringStrictFacts = BaseStringStrictFacts | Falsy,
- EmptyStringFacts = BaseStringFacts,
- NonEmptyStringStrictFacts = BaseStringStrictFacts | Truthy,
- NonEmptyStringFacts = BaseStringFacts | Truthy,
- BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
- BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
- NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy,
- NumberFacts = BaseNumberFacts | Truthy,
- ZeroNumberStrictFacts = BaseNumberStrictFacts | Falsy,
- ZeroNumberFacts = BaseNumberFacts,
- NonZeroNumberStrictFacts = BaseNumberStrictFacts | Truthy,
- NonZeroNumberFacts = BaseNumberFacts | Truthy,
- BaseBigIntStrictFacts = TypeofEQBigInt | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
- BaseBigIntFacts = BaseBigIntStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
- BigIntStrictFacts = BaseBigIntStrictFacts | Truthy | Falsy,
- BigIntFacts = BaseBigIntFacts | Truthy,
- ZeroBigIntStrictFacts = BaseBigIntStrictFacts | Falsy,
- ZeroBigIntFacts = BaseBigIntFacts,
- NonZeroBigIntStrictFacts = BaseBigIntStrictFacts | Truthy,
- NonZeroBigIntFacts = BaseBigIntFacts | Truthy,
- BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
- BaseBooleanFacts = BaseBooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
- BooleanStrictFacts = BaseBooleanStrictFacts | Truthy | Falsy,
- BooleanFacts = BaseBooleanFacts | Truthy,
- FalseStrictFacts = BaseBooleanStrictFacts | Falsy,
- FalseFacts = BaseBooleanFacts,
- TrueStrictFacts = BaseBooleanStrictFacts | Truthy,
- TrueFacts = BaseBooleanFacts | Truthy,
- SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
- SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
- ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
- ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
- FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
- FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
- UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy,
- NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy,
- EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull),
- AllTypeofNE = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined,
- EmptyObjectFacts = All,
- }
-
- const typeofEQFacts: ReadonlyESMap = new Map(getEntries({
- string: TypeFacts.TypeofEQString,
- number: TypeFacts.TypeofEQNumber,
- bigint: TypeFacts.TypeofEQBigInt,
- boolean: TypeFacts.TypeofEQBoolean,
- symbol: TypeFacts.TypeofEQSymbol,
- undefined: TypeFacts.EQUndefined,
- object: TypeFacts.TypeofEQObject,
- function: TypeFacts.TypeofEQFunction
- }));
+const ambientModuleSymbolRegex = /^".+"$/;
+/* @internal */
+const anon = "(anonymous)" as __String & string;
- const typeofNEFacts: ReadonlyESMap = new Map(getEntries({
- string: TypeFacts.TypeofNEString,
- number: TypeFacts.TypeofNENumber,
- bigint: TypeFacts.TypeofNEBigInt,
- boolean: TypeFacts.TypeofNEBoolean,
- symbol: TypeFacts.TypeofNESymbol,
- undefined: TypeFacts.NEUndefined,
- object: TypeFacts.TypeofNEObject,
- function: TypeFacts.TypeofNEFunction
- }));
+/* @internal */
+let nextSymbolId = 1;
+/* @internal */
+let nextNodeId = 1;
+/* @internal */
+let nextMergeId = 1;
+/* @internal */
+let nextFlowId = 1;
- type TypeSystemEntity = Node | Symbol | Type | Signature;
+/* @internal */
+const enum IterationUse {
+ AllowsSyncIterablesFlag = 1 << 0,
+ AllowsAsyncIterablesFlag = 1 << 1,
+ AllowsStringInputFlag = 1 << 2,
+ ForOfFlag = 1 << 3,
+ YieldStarFlag = 1 << 4,
+ SpreadFlag = 1 << 5,
+ DestructuringFlag = 1 << 6,
+ PossiblyOutOfBounds = 1 << 7,
+
+ // Spread, Destructuring, Array element assignment
+ Element = AllowsSyncIterablesFlag,
+ Spread = AllowsSyncIterablesFlag | SpreadFlag,
+ Destructuring = AllowsSyncIterablesFlag | DestructuringFlag,
+
+ ForOf = AllowsSyncIterablesFlag | AllowsStringInputFlag | ForOfFlag,
+ ForAwaitOf = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag | AllowsStringInputFlag | ForOfFlag,
+
+ YieldStar = AllowsSyncIterablesFlag | YieldStarFlag,
+ AsyncYieldStar = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag | YieldStarFlag,
+
+ GeneratorReturnType = AllowsSyncIterablesFlag,
+ AsyncGeneratorReturnType = AllowsAsyncIterablesFlag
- const enum TypeSystemPropertyName {
- Type,
- ResolvedBaseConstructorType,
- DeclaredType,
- ResolvedReturnType,
- ImmediateBaseConstraint,
- EnumTagType,
- ResolvedTypeArguments,
- ResolvedBaseTypes,
- }
+}
- const enum CheckMode {
- Normal = 0, // Normal type checking
- Contextual = 1 << 0, // Explicitly assigned contextual type, therefore not cacheable
- Inferential = 1 << 1, // Inferential typing
- SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions
- SkipGenericFunctions = 1 << 3, // Skip single signature generic functions
- IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help
- }
+/* @internal */
+const enum IterationTypeKind {
+ Yield,
+ Return,
+ Next
+}
- const enum SignatureCheckMode {
- BivariantCallback = 1 << 0,
- StrictCallback = 1 << 1,
- IgnoreReturnTypes = 1 << 2,
- StrictArity = 1 << 3,
- Callback = BivariantCallback | StrictCallback,
- }
+/* @internal */
+interface IterationTypesResolver {
+ iterableCacheKey: "iterationTypesOfAsyncIterable" | "iterationTypesOfIterable";
+ iteratorCacheKey: "iterationTypesOfAsyncIterator" | "iterationTypesOfIterator";
+ iteratorSymbolName: "asyncIterator" | "iterator";
+ getGlobalIteratorType: (reportErrors: boolean) => GenericType;
+ getGlobalIterableType: (reportErrors: boolean) => GenericType;
+ getGlobalIterableIteratorType: (reportErrors: boolean) => GenericType;
+ getGlobalGeneratorType: (reportErrors: boolean) => GenericType;
+ resolveIterationType: (type: Type, errorNode: Node | undefined) => Type | undefined;
+ mustHaveANextMethodDiagnostic: DiagnosticMessage;
+ mustBeAMethodDiagnostic: DiagnosticMessage;
+ mustHaveAValueDiagnostic: DiagnosticMessage;
+}
- const enum IntersectionState {
- None = 0,
- Source = 1 << 0,
- Target = 1 << 1,
- PropertyCheck = 1 << 2,
- UnionIntersectionCheck = 1 << 3,
- InPropertyCheck = 1 << 4,
- }
+/* @internal */
+const enum WideningKind {
+ Normal,
+ FunctionReturn,
+ GeneratorNext,
+ GeneratorYield
+}
- const enum RecursionFlags {
- None = 0,
- Source = 1 << 0,
- Target = 1 << 1,
- Both = Source | Target,
- }
+/* @internal */
+const enum TypeFacts {
+ None = 0,
+ TypeofEQString = 1 << 0,
+ TypeofEQNumber = 1 << 1,
+ TypeofEQBigInt = 1 << 2,
+ TypeofEQBoolean = 1 << 3,
+ TypeofEQSymbol = 1 << 4,
+ TypeofEQObject = 1 << 5,
+ TypeofEQFunction = 1 << 6,
+ TypeofEQHostObject = 1 << 7,
+ TypeofNEString = 1 << 8,
+ TypeofNENumber = 1 << 9,
+ TypeofNEBigInt = 1 << 10,
+ TypeofNEBoolean = 1 << 11,
+ TypeofNESymbol = 1 << 12,
+ TypeofNEObject = 1 << 13,
+ TypeofNEFunction = 1 << 14,
+ TypeofNEHostObject = 1 << 15,
+ EQUndefined = 1 << 16,
+ EQNull = 1 << 17,
+ EQUndefinedOrNull = 1 << 18,
+ NEUndefined = 1 << 19,
+ NENull = 1 << 20,
+ NEUndefinedOrNull = 1 << 21,
+ Truthy = 1 << 22,
+ Falsy = 1 << 23,
+ All = (1 << 24) - 1,
+ // The following members encode facts about particular kinds of types for use in the getTypeFacts function.
+ // The presence of a particular fact means that the given test is true for some (and possibly all) values
+ // of that kind of type.
+ BaseStringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
+ BaseStringFacts = BaseStringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
+ StringStrictFacts = BaseStringStrictFacts | Truthy | Falsy,
+ StringFacts = BaseStringFacts | Truthy,
+ EmptyStringStrictFacts = BaseStringStrictFacts | Falsy,
+ EmptyStringFacts = BaseStringFacts,
+ NonEmptyStringStrictFacts = BaseStringStrictFacts | Truthy,
+ NonEmptyStringFacts = BaseStringFacts | Truthy,
+ BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
+ BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
+ NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy,
+ NumberFacts = BaseNumberFacts | Truthy,
+ ZeroNumberStrictFacts = BaseNumberStrictFacts | Falsy,
+ ZeroNumberFacts = BaseNumberFacts,
+ NonZeroNumberStrictFacts = BaseNumberStrictFacts | Truthy,
+ NonZeroNumberFacts = BaseNumberFacts | Truthy,
+ BaseBigIntStrictFacts = TypeofEQBigInt | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
+ BaseBigIntFacts = BaseBigIntStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
+ BigIntStrictFacts = BaseBigIntStrictFacts | Truthy | Falsy,
+ BigIntFacts = BaseBigIntFacts | Truthy,
+ ZeroBigIntStrictFacts = BaseBigIntStrictFacts | Falsy,
+ ZeroBigIntFacts = BaseBigIntFacts,
+ NonZeroBigIntStrictFacts = BaseBigIntStrictFacts | Truthy,
+ NonZeroBigIntFacts = BaseBigIntFacts | Truthy,
+ BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
+ BaseBooleanFacts = BaseBooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
+ BooleanStrictFacts = BaseBooleanStrictFacts | Truthy | Falsy,
+ BooleanFacts = BaseBooleanFacts | Truthy,
+ FalseStrictFacts = BaseBooleanStrictFacts | Falsy,
+ FalseFacts = BaseBooleanFacts,
+ TrueStrictFacts = BaseBooleanStrictFacts | Truthy,
+ TrueFacts = BaseBooleanFacts | Truthy,
+ SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
+ SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
+ ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
+ ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
+ FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
+ FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
+ UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy,
+ NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy,
+ EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull),
+ AllTypeofNE = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined,
+ EmptyObjectFacts = All
+}
- const enum MappedTypeModifiers {
- IncludeReadonly = 1 << 0,
- ExcludeReadonly = 1 << 1,
- IncludeOptional = 1 << 2,
- ExcludeOptional = 1 << 3,
- }
+/* @internal */
+const typeofEQFacts: ReadonlyESMap = new ts.Map(getEntries({
+ string: TypeFacts.TypeofEQString,
+ number: TypeFacts.TypeofEQNumber,
+ bigint: TypeFacts.TypeofEQBigInt,
+ boolean: TypeFacts.TypeofEQBoolean,
+ symbol: TypeFacts.TypeofEQSymbol,
+ undefined: TypeFacts.EQUndefined,
+ object: TypeFacts.TypeofEQObject,
+ function: TypeFacts.TypeofEQFunction
+}));
- const enum ExpandingFlags {
- None = 0,
- Source = 1,
- Target = 1 << 1,
- Both = Source | Target,
- }
+/* @internal */
+const typeofNEFacts: ReadonlyESMap = new ts.Map(getEntries({
+ string: TypeFacts.TypeofNEString,
+ number: TypeFacts.TypeofNENumber,
+ bigint: TypeFacts.TypeofNEBigInt,
+ boolean: TypeFacts.TypeofNEBoolean,
+ symbol: TypeFacts.TypeofNESymbol,
+ undefined: TypeFacts.NEUndefined,
+ object: TypeFacts.TypeofNEObject,
+ function: TypeFacts.TypeofNEFunction
+}));
- const enum MembersOrExportsResolutionKind {
- resolvedExports = "resolvedExports",
- resolvedMembers = "resolvedMembers"
- }
+/* @internal */
+type TypeSystemEntity = Node | Symbol | Type | Signature;
- const enum UnusedKind {
- Local,
- Parameter,
- }
+/* @internal */
+const enum TypeSystemPropertyName {
+ Type,
+ ResolvedBaseConstructorType,
+ DeclaredType,
+ ResolvedReturnType,
+ ImmediateBaseConstraint,
+ EnumTagType,
+ ResolvedTypeArguments,
+ ResolvedBaseTypes
+}
- /** @param containingNode Node to check for parse error */
- type AddUnusedDiagnostic = (containingNode: Node, type: UnusedKind, diagnostic: DiagnosticWithLocation) => void;
+/* @internal */
+const enum CheckMode {
+ Normal = 0,
+ Contextual = 1 << 0,
+ Inferential = 1 << 1,
+ SkipContextSensitive = 1 << 2,
+ SkipGenericFunctions = 1 << 3,
+ IsForSignatureHelp = 1 << 4
+}
- const isNotOverloadAndNotAccessor = and(isNotOverload, isNotAccessor);
+/* @internal */
+const enum SignatureCheckMode {
+ BivariantCallback = 1 << 0,
+ StrictCallback = 1 << 1,
+ IgnoreReturnTypes = 1 << 2,
+ StrictArity = 1 << 3,
+ Callback = BivariantCallback | StrictCallback
+}
- const enum DeclarationMeaning {
- GetAccessor = 1,
- SetAccessor = 2,
- PropertyAssignment = 4,
- Method = 8,
- PrivateStatic = 16,
- GetOrSetAccessor = GetAccessor | SetAccessor,
- PropertyAssignmentOrMethod = PropertyAssignment | Method,
- }
+/* @internal */
+const enum IntersectionState {
+ None = 0,
+ Source = 1 << 0,
+ Target = 1 << 1,
+ PropertyCheck = 1 << 2,
+ UnionIntersectionCheck = 1 << 3,
+ InPropertyCheck = 1 << 4
+}
- const enum DeclarationSpaces {
- None = 0,
- ExportValue = 1 << 0,
- ExportType = 1 << 1,
- ExportNamespace = 1 << 2,
- }
+/* @internal */
+const enum RecursionFlags {
+ None = 0,
+ Source = 1 << 0,
+ Target = 1 << 1,
+ Both = Source | Target
+}
- const enum MinArgumentCountFlags {
- None = 0,
- StrongArityForUntypedJS = 1 << 0,
- VoidIsNonOptional = 1 << 1,
- }
+/* @internal */
+const enum MappedTypeModifiers {
+ IncludeReadonly = 1 << 0,
+ ExcludeReadonly = 1 << 1,
+ IncludeOptional = 1 << 2,
+ ExcludeOptional = 1 << 3
+}
- const enum IntrinsicTypeKind {
- Uppercase,
- Lowercase,
- Capitalize,
- Uncapitalize
- }
+/* @internal */
+const enum ExpandingFlags {
+ None = 0,
+ Source = 1,
+ Target = 1 << 1,
+ Both = Source | Target
+}
- const intrinsicTypeKinds: ReadonlyESMap = new Map(getEntries({
- Uppercase: IntrinsicTypeKind.Uppercase,
- Lowercase: IntrinsicTypeKind.Lowercase,
- Capitalize: IntrinsicTypeKind.Capitalize,
- Uncapitalize: IntrinsicTypeKind.Uncapitalize
- }));
+/* @internal */
+const enum MembersOrExportsResolutionKind {
+ resolvedExports = "resolvedExports",
+ resolvedMembers = "resolvedMembers"
+}
- function SymbolLinks(this: SymbolLinks) {
- }
+/* @internal */
+const enum UnusedKind {
+ Local,
+ Parameter
+}
- function NodeLinks(this: NodeLinks) {
- this.flags = 0;
- }
+/** @param containingNode Node to check for parse error */
+/* @internal */
+type AddUnusedDiagnostic = (containingNode: Node, type: UnusedKind, diagnostic: DiagnosticWithLocation) => void;
- export function getNodeId(node: Node): number {
- if (!node.id) {
- node.id = nextNodeId;
- nextNodeId++;
- }
- return node.id;
- }
+/* @internal */
+const isNotOverloadAndNotAccessor = and(isNotOverload, isNotAccessor);
- export function getSymbolId(symbol: Symbol): SymbolId {
- if (!symbol.id) {
- symbol.id = nextSymbolId;
- nextSymbolId++;
- }
+/* @internal */
+const enum DeclarationMeaning {
+ GetAccessor = 1,
+ SetAccessor = 2,
+ PropertyAssignment = 4,
+ Method = 8,
+ PrivateStatic = 16,
+ GetOrSetAccessor = GetAccessor | SetAccessor,
+ PropertyAssignmentOrMethod = PropertyAssignment | Method
+}
- return symbol.id;
- }
+/* @internal */
+const enum DeclarationSpaces {
+ None = 0,
+ ExportValue = 1 << 0,
+ ExportType = 1 << 1,
+ ExportNamespace = 1 << 2
+}
- export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) {
- const moduleState = getModuleInstanceState(node);
- return moduleState === ModuleInstanceState.Instantiated ||
- (preserveConstEnums && moduleState === ModuleInstanceState.ConstEnumOnly);
- }
+/* @internal */
+const enum MinArgumentCountFlags {
+ None = 0,
+ StrongArityForUntypedJS = 1 << 0,
+ VoidIsNonOptional = 1 << 1
+}
- export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker {
- const getPackagesMap = memoize(() => {
- // A package name maps to true when we detect it has .d.ts files.
- // This is useful as an approximation of whether a package bundles its own types.
- // Note: we only look at files already found by module resolution,
- // so there may be files we did not consider.
- const map = new Map();
- host.getSourceFiles().forEach(sf => {
- if (!sf.resolvedModules) return;
+/* @internal */
+const enum IntrinsicTypeKind {
+ Uppercase,
+ Lowercase,
+ Capitalize,
+ Uncapitalize
+}
- sf.resolvedModules.forEach(r => {
- if (r && r.packageId) map.set(r.packageId.name, r.extension === Extension.Dts || !!map.get(r.packageId.name));
- });
- });
- return map;
- });
+/* @internal */
+const intrinsicTypeKinds: ReadonlyESMap = new ts.Map(getEntries({
+ Uppercase: IntrinsicTypeKind.Uppercase,
+ Lowercase: IntrinsicTypeKind.Lowercase,
+ Capitalize: IntrinsicTypeKind.Capitalize,
+ Uncapitalize: IntrinsicTypeKind.Uncapitalize
+}));
- // Cancellation that controls whether or not we can cancel in the middle of type checking.
- // In general cancelling is *not* safe for the type checker. We might be in the middle of
- // computing something, and we will leave our internals in an inconsistent state. Callers
- // who set the cancellation token should catch if a cancellation exception occurs, and
- // should throw away and create a new TypeChecker.
- //
- // Currently we only support setting the cancellation token when getting diagnostics. This
- // is because diagnostics can be quite expensive, and we want to allow hosts to bail out if
- // they no longer need the information (for example, if the user started editing again).
- let cancellationToken: CancellationToken | undefined;
- let requestedExternalEmitHelpers: ExternalEmitHelpers;
- let externalHelpersModule: Symbol;
-
- const Symbol = objectAllocator.getSymbolConstructor();
- const Type = objectAllocator.getTypeConstructor();
- const Signature = objectAllocator.getSignatureConstructor();
-
- let typeCount = 0;
- let symbolCount = 0;
- let enumCount = 0;
- let totalInstantiationCount = 0;
- let instantiationCount = 0;
- let instantiationDepth = 0;
- let inlineLevel = 0;
- let currentNode: Node | undefined;
-
- const emptySymbols = createSymbolTable();
- const arrayVariances = [VarianceFlags.Covariant];
-
- const compilerOptions = host.getCompilerOptions();
- const languageVersion = getEmitScriptTarget(compilerOptions);
- const moduleKind = getEmitModuleKind(compilerOptions);
- const useDefineForClassFields = getUseDefineForClassFields(compilerOptions);
- const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
- const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
- const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes");
- const strictBindCallApply = getStrictOptionValue(compilerOptions, "strictBindCallApply");
- const strictPropertyInitialization = getStrictOptionValue(compilerOptions, "strictPropertyInitialization");
- const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny");
- const noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis");
- const useUnknownInCatchVariables = getStrictOptionValue(compilerOptions, "useUnknownInCatchVariables");
- const keyofStringsOnly = !!compilerOptions.keyofStringsOnly;
- const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : ObjectFlags.FreshLiteral;
- const exactOptionalPropertyTypes = compilerOptions.exactOptionalPropertyTypes;
-
- const checkBinaryExpression = createCheckBinaryExpression();
- const emitResolver = createResolver();
- const nodeBuilder = createNodeBuilder();
-
- const globals = createSymbolTable();
- const undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined" as __String);
- undefinedSymbol.declarations = [];
-
- const globalThisSymbol = createSymbol(SymbolFlags.Module, "globalThis" as __String, CheckFlags.Readonly);
- globalThisSymbol.exports = globals;
- globalThisSymbol.declarations = [];
- globals.set(globalThisSymbol.escapedName, globalThisSymbol);
-
- const argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments" as __String);
- const requireSymbol = createSymbol(SymbolFlags.Property, "require" as __String);
-
- /** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */
- let apparentArgumentCount: number | undefined;
-
- // for public members that accept a Node or one of its subtypes, we must guard against
- // synthetic nodes created during transformations by calling `getParseTreeNode`.
- // for most of these, we perform the guard only on `checker` to avoid any possible
- // extra cost of calling `getParseTreeNode` when calling these functions from inside the
- // checker.
- const checker: TypeChecker = {
- getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"),
- getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"),
- getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount,
- getTypeCount: () => typeCount,
- getInstantiationCount: () => totalInstantiationCount,
- getRelationCacheSizes: () => ({
- assignable: assignableRelation.size,
- identity: identityRelation.size,
- subtype: subtypeRelation.size,
- strictSubtype: strictSubtypeRelation.size,
- }),
- isUndefinedSymbol: symbol => symbol === undefinedSymbol,
- isArgumentsSymbol: symbol => symbol === argumentsSymbol,
- isUnknownSymbol: symbol => symbol === unknownSymbol,
- getMergedSymbol,
- getDiagnostics,
- getGlobalDiagnostics,
- getRecursionIdentity,
- getUnmatchedProperties,
- getTypeOfSymbolAtLocation: (symbol, locationIn) => {
- const location = getParseTreeNode(locationIn);
- return location ? getTypeOfSymbolAtLocation(symbol, location) : errorType;
- },
- getTypeOfSymbol,
- getSymbolsOfParameterPropertyDeclaration: (parameterIn, parameterName) => {
- const parameter = getParseTreeNode(parameterIn, isParameter);
- if (parameter === undefined) return Debug.fail("Cannot get symbols of a synthetic parameter that cannot be resolved to a parse-tree node.");
- return getSymbolsOfParameterPropertyDeclaration(parameter, escapeLeadingUnderscores(parameterName));
- },
- getDeclaredTypeOfSymbol,
- getPropertiesOfType,
- getPropertyOfType: (type, name) => getPropertyOfType(type, escapeLeadingUnderscores(name)),
- getPrivateIdentifierPropertyOfType: (leftType: Type, name: string, location: Node) => {
- const node = getParseTreeNode(location);
- if (!node) {
- return undefined;
- }
- const propName = escapeLeadingUnderscores(name);
- const lexicallyScopedIdentifier = lookupSymbolForPrivateIdentifierDeclaration(propName, node);
- return lexicallyScopedIdentifier ? getPrivateIdentifierPropertyOfType(leftType, lexicallyScopedIdentifier) : undefined;
- },
- getTypeOfPropertyOfType: (type, name) => getTypeOfPropertyOfType(type, escapeLeadingUnderscores(name)),
- getIndexInfoOfType: (type, kind) => getIndexInfoOfType(type, kind === IndexKind.String ? stringType : numberType),
- getIndexInfosOfType,
- getSignaturesOfType,
- getIndexTypeOfType: (type, kind) => getIndexTypeOfType(type, kind === IndexKind.String ? stringType : numberType),
- getIndexType: type => getIndexType(type),
- getBaseTypes,
- getBaseTypeOfLiteralType,
- getWidenedType,
- getTypeFromTypeNode: nodeIn => {
- const node = getParseTreeNode(nodeIn, isTypeNode);
- return node ? getTypeFromTypeNode(node) : errorType;
- },
- getParameterType: getTypeAtPosition,
- getParameterIdentifierNameAtPosition,
- getPromisedTypeOfPromise,
- getAwaitedType: type => getAwaitedType(type),
- getReturnTypeOfSignature,
- isNullableType,
- getNullableType,
- getNonNullableType,
- getNonOptionalType: removeOptionalTypeMarker,
- getTypeArguments,
- typeToTypeNode: nodeBuilder.typeToTypeNode,
- indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration,
- signatureToSignatureDeclaration: nodeBuilder.signatureToSignatureDeclaration,
- symbolToEntityName: nodeBuilder.symbolToEntityName,
- symbolToExpression: nodeBuilder.symbolToExpression,
- symbolToTypeParameterDeclarations: nodeBuilder.symbolToTypeParameterDeclarations,
- symbolToParameterDeclaration: nodeBuilder.symbolToParameterDeclaration,
- typeParameterToDeclaration: nodeBuilder.typeParameterToDeclaration,
- getSymbolsInScope: (locationIn, meaning) => {
- const location = getParseTreeNode(locationIn);
- return location ? getSymbolsInScope(location, meaning) : [];
- },
- getSymbolAtLocation: nodeIn => {
- const node = getParseTreeNode(nodeIn);
- // set ignoreErrors: true because any lookups invoked by the API shouldn't cause any new errors
- return node ? getSymbolAtLocation(node, /*ignoreErrors*/ true) : undefined;
- },
- getIndexInfosAtLocation: nodeIn => {
- const node = getParseTreeNode(nodeIn);
- return node ? getIndexInfosAtLocation(node) : undefined;
- },
- getShorthandAssignmentValueSymbol: nodeIn => {
- const node = getParseTreeNode(nodeIn);
- return node ? getShorthandAssignmentValueSymbol(node) : undefined;
- },
- getExportSpecifierLocalTargetSymbol: nodeIn => {
- const node = getParseTreeNode(nodeIn, isExportSpecifier);
- return node ? getExportSpecifierLocalTargetSymbol(node) : undefined;
- },
- getExportSymbolOfSymbol(symbol) {
- return getMergedSymbol(symbol.exportSymbol || symbol);
- },
- getTypeAtLocation: nodeIn => {
- const node = getParseTreeNode(nodeIn);
- return node ? getTypeOfNode(node) : errorType;
- },
- getTypeOfAssignmentPattern: nodeIn => {
- const node = getParseTreeNode(nodeIn, isAssignmentPattern);
- return node && getTypeOfAssignmentPattern(node) || errorType;
- },
- getPropertySymbolOfDestructuringAssignment: locationIn => {
- const location = getParseTreeNode(locationIn, isIdentifier);
- return location ? getPropertySymbolOfDestructuringAssignment(location) : undefined;
- },
- signatureToString: (signature, enclosingDeclaration, flags, kind) => {
- return signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind);
- },
- typeToString: (type, enclosingDeclaration, flags) => {
- return typeToString(type, getParseTreeNode(enclosingDeclaration), flags);
- },
- symbolToString: (symbol, enclosingDeclaration, meaning, flags) => {
- return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning, flags);
- },
- typePredicateToString: (predicate, enclosingDeclaration, flags) => {
- return typePredicateToString(predicate, getParseTreeNode(enclosingDeclaration), flags);
- },
- writeSignature: (signature, enclosingDeclaration, flags, kind, writer) => {
- return signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind, writer);
- },
- writeType: (type, enclosingDeclaration, flags, writer) => {
- return typeToString(type, getParseTreeNode(enclosingDeclaration), flags, writer);
- },
- writeSymbol: (symbol, enclosingDeclaration, meaning, flags, writer) => {
- return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning, flags, writer);
- },
- writeTypePredicate: (predicate, enclosingDeclaration, flags, writer) => {
- return typePredicateToString(predicate, getParseTreeNode(enclosingDeclaration), flags, writer);
- },
- getAugmentedPropertiesOfType,
- getRootSymbols,
- getSymbolOfExpando,
- getContextualType: (nodeIn: Expression, contextFlags?: ContextFlags) => {
- const node = getParseTreeNode(nodeIn, isExpression);
- if (!node) {
- return undefined;
- }
- const containingCall = findAncestor(node, isCallLikeExpression);
- const containingCallResolvedSignature = containingCall && getNodeLinks(containingCall).resolvedSignature;
- if (contextFlags! & ContextFlags.Completions && containingCall) {
- let toMarkSkip = node as Node;
- do {
- getNodeLinks(toMarkSkip).skipDirectInference = true;
- toMarkSkip = toMarkSkip.parent;
- } while (toMarkSkip && toMarkSkip !== containingCall);
- getNodeLinks(containingCall).resolvedSignature = undefined;
- }
- const result = getContextualType(node, contextFlags);
- if (contextFlags! & ContextFlags.Completions && containingCall) {
- let toMarkSkip = node as Node;
- do {
- getNodeLinks(toMarkSkip).skipDirectInference = undefined;
- toMarkSkip = toMarkSkip.parent;
- } while (toMarkSkip && toMarkSkip !== containingCall);
- getNodeLinks(containingCall).resolvedSignature = containingCallResolvedSignature;
- }
- return result;
- },
- getContextualTypeForObjectLiteralElement: nodeIn => {
- const node = getParseTreeNode(nodeIn, isObjectLiteralElementLike);
- return node ? getContextualTypeForObjectLiteralElement(node) : undefined;
- },
- getContextualTypeForArgumentAtIndex: (nodeIn, argIndex) => {
- const node = getParseTreeNode(nodeIn, isCallLikeExpression);
- return node && getContextualTypeForArgumentAtIndex(node, argIndex);
- },
- getContextualTypeForJsxAttribute: (nodeIn) => {
- const node = getParseTreeNode(nodeIn, isJsxAttributeLike);
- return node && getContextualTypeForJsxAttribute(node);
- },
- isContextSensitive,
- getTypeOfPropertyOfContextualType,
- getFullyQualifiedName,
- getResolvedSignature: (node, candidatesOutArray, argumentCount) =>
- getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal),
- getResolvedSignatureForSignatureHelp: (node, candidatesOutArray, argumentCount) =>
- getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.IsForSignatureHelp),
- getExpandedParameters,
- hasEffectiveRestParameter,
- containsArgumentsReference,
- getConstantValue: nodeIn => {
- const node = getParseTreeNode(nodeIn, canHaveConstantValue);
- return node ? getConstantValue(node) : undefined;
- },
- isValidPropertyAccess: (nodeIn, propertyName) => {
- const node = getParseTreeNode(nodeIn, isPropertyAccessOrQualifiedNameOrImportTypeNode);
- return !!node && isValidPropertyAccess(node, escapeLeadingUnderscores(propertyName));
- },
- isValidPropertyAccessForCompletions: (nodeIn, type, property) => {
- const node = getParseTreeNode(nodeIn, isPropertyAccessExpression);
- return !!node && isValidPropertyAccessForCompletions(node, type, property);
- },
- getSignatureFromDeclaration: declarationIn => {
- const declaration = getParseTreeNode(declarationIn, isFunctionLike);
- return declaration ? getSignatureFromDeclaration(declaration) : undefined;
- },
- isImplementationOfOverload: nodeIn => {
- const node = getParseTreeNode(nodeIn, isFunctionLike);
- return node ? isImplementationOfOverload(node) : undefined;
- },
- getImmediateAliasedSymbol,
- getAliasedSymbol: resolveAlias,
- getEmitResolver,
- getExportsOfModule: getExportsOfModuleAsArray,
- getExportsAndPropertiesOfModule,
- forEachExportAndPropertyOfModule,
- getSymbolWalker: createGetSymbolWalker(
- getRestTypeOfSignature,
- getTypePredicateOfSignature,
- getReturnTypeOfSignature,
- getBaseTypes,
- resolveStructuredTypeMembers,
- getTypeOfSymbol,
- getResolvedSymbol,
- getConstraintOfTypeParameter,
- getFirstIdentifier,
- getTypeArguments,
- ),
- getAmbientModules,
- getJsxIntrinsicTagNamesAt,
- isOptionalParameter: nodeIn => {
- const node = getParseTreeNode(nodeIn, isParameter);
- return node ? isOptionalParameter(node) : false;
- },
- tryGetMemberInModuleExports: (name, symbol) => tryGetMemberInModuleExports(escapeLeadingUnderscores(name), symbol),
- tryGetMemberInModuleExportsAndProperties: (name, symbol) => tryGetMemberInModuleExportsAndProperties(escapeLeadingUnderscores(name), symbol),
- tryFindAmbientModule: moduleName => tryFindAmbientModule(moduleName, /*withAugmentations*/ true),
- tryFindAmbientModuleWithoutAugmentations: moduleName => {
- // we deliberately exclude augmentations
- // since we are only interested in declarations of the module itself
- return tryFindAmbientModule(moduleName, /*withAugmentations*/ false);
- },
- getApparentType,
- getUnionType,
- isTypeAssignableTo,
- createAnonymousType,
- createSignature,
- createSymbol,
- createIndexInfo,
- getAnyType: () => anyType,
- getStringType: () => stringType,
- getNumberType: () => numberType,
- createPromiseType,
- createArrayType,
- getElementTypeOfArrayType,
- getBooleanType: () => booleanType,
- getFalseType: (fresh?) => fresh ? falseType : regularFalseType,
- getTrueType: (fresh?) => fresh ? trueType : regularTrueType,
- getVoidType: () => voidType,
- getUndefinedType: () => undefinedType,
- getNullType: () => nullType,
- getESSymbolType: () => esSymbolType,
- getNeverType: () => neverType,
- getOptionalType: () => optionalType,
- getPromiseType: () => getGlobalPromiseType(/*reportErrors*/ false),
- getPromiseLikeType: () => getGlobalPromiseLikeType(/*reportErrors*/ false),
- isSymbolAccessible,
- isArrayType,
- isTupleType,
- isArrayLikeType,
- isTypeInvalidDueToUnionDiscriminant,
- getExactOptionalProperties,
- getAllPossiblePropertiesOfTypes,
- getSuggestedSymbolForNonexistentProperty,
- getSuggestionForNonexistentProperty,
- getSuggestedSymbolForNonexistentJSXAttribute,
- getSuggestedSymbolForNonexistentSymbol: (location, name, meaning) => getSuggestedSymbolForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning),
- getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning),
- getSuggestedSymbolForNonexistentModule,
- getSuggestionForNonexistentExport,
- getSuggestedSymbolForNonexistentClassMember,
- getBaseConstraintOfType,
- getDefaultFromTypeParameter: type => type && type.flags & TypeFlags.TypeParameter ? getDefaultFromTypeParameter(type as TypeParameter) : undefined,
- resolveName(name, location, meaning, excludeGlobals) {
- return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false, excludeGlobals);
- },
- getJsxNamespace: n => unescapeLeadingUnderscores(getJsxNamespace(n)),
- getJsxFragmentFactory: n => {
- const jsxFragmentFactory = getJsxFragmentFactoryEntity(n);
- return jsxFragmentFactory && unescapeLeadingUnderscores(getFirstIdentifier(jsxFragmentFactory).escapedText);
- },
- getAccessibleSymbolChain,
- getTypePredicateOfSignature,
- resolveExternalModuleName: moduleSpecifierIn => {
- const moduleSpecifier = getParseTreeNode(moduleSpecifierIn, isExpression);
- return moduleSpecifier && resolveExternalModuleName(moduleSpecifier, moduleSpecifier, /*ignoreErrors*/ true);
- },
- resolveExternalModuleSymbol,
- tryGetThisTypeAt: (nodeIn, includeGlobalThis) => {
- const node = getParseTreeNode(nodeIn);
- return node && tryGetThisTypeAt(node, includeGlobalThis);
- },
- getTypeArgumentConstraint: nodeIn => {
- const node = getParseTreeNode(nodeIn, isTypeNode);
- return node && getTypeArgumentConstraint(node);
- },
- getSuggestionDiagnostics: (fileIn, ct) => {
- const file = getParseTreeNode(fileIn, isSourceFile) || Debug.fail("Could not determine parsed source file.");
- if (skipTypeChecking(file, compilerOptions, host)) {
- return emptyArray;
- }
+/* @internal */
+function SymbolLinks(this: ts.SymbolLinks) {
+}
- let diagnostics: DiagnosticWithLocation[] | undefined;
- try {
- // Record the cancellation token so it can be checked later on during checkSourceElement.
- // Do this in a finally block so we can ensure that it gets reset back to nothing after
- // this call is done.
- cancellationToken = ct;
+/* @internal */
+function NodeLinks(this: ts.NodeLinks) {
+ this.flags = 0;
+}
- // Ensure file is type checked
- checkSourceFile(file);
- Debug.assert(!!(getNodeLinks(file).flags & NodeCheckFlags.TypeChecked));
+/* @internal */
+export function getNodeId(node: Node): number {
+ if (!node.id) {
+ node.id = nextNodeId;
+ nextNodeId++;
+ }
+ return node.id;
+}
- diagnostics = addRange(diagnostics, suggestionDiagnostics.getDiagnostics(file.fileName));
- checkUnusedIdentifiers(getPotentiallyUnusedIdentifiers(file), (containingNode, kind, diag) => {
- if (!containsParseError(containingNode) && !unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) {
- (diagnostics || (diagnostics = [])).push({ ...diag, category: DiagnosticCategory.Suggestion });
- }
- });
+/* @internal */
+export function getSymbolId(symbol: Symbol): SymbolId {
+ if (!symbol.id) {
+ symbol.id = nextSymbolId;
+ nextSymbolId++;
+ }
- return diagnostics || emptyArray;
- }
- finally {
- cancellationToken = undefined;
- }
- },
+ return symbol.id;
+}
- runWithCancellationToken: (token, callback) => {
- try {
- cancellationToken = token;
- return callback(checker);
- }
- finally {
- cancellationToken = undefined;
- }
- },
+/* @internal */
+export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) {
+ const moduleState = getModuleInstanceState(node);
+ return moduleState === ModuleInstanceState.Instantiated ||
+ (preserveConstEnums && moduleState === ModuleInstanceState.ConstEnumOnly);
+}
- getLocalTypeParametersOfClassOrInterfaceOrTypeAlias,
- isDeclarationVisible,
- isPropertyAccessible,
- getTypeOnlyAliasDeclaration,
- getMemberOverrideModifierStatus,
- };
+/* @internal */
+export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker {
+ const getPackagesMap = memoize(() => {
+ // A package name maps to true when we detect it has .d.ts files.
+ // This is useful as an approximation of whether a package bundles its own types.
+ // Note: we only look at files already found by module resolution,
+ // so there may be files we did not consider.
+ const map = new ts.Map();
+ host.getSourceFiles().forEach(sf => {
+ if (!sf.resolvedModules)
+ return;
- function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): Signature | undefined {
+ sf.resolvedModules.forEach(r => {
+ if (r && r.packageId)
+ map.set(r.packageId.name, r.extension === Extension.Dts || !!map.get(r.packageId.name));
+ });
+ });
+ return map;
+ });
+
+ // Cancellation that controls whether or not we can cancel in the middle of type checking.
+ // In general cancelling is *not* safe for the type checker. We might be in the middle of
+ // computing something, and we will leave our internals in an inconsistent state. Callers
+ // who set the cancellation token should catch if a cancellation exception occurs, and
+ // should throw away and create a new TypeChecker.
+ //
+ // Currently we only support setting the cancellation token when getting diagnostics. This
+ // is because diagnostics can be quite expensive, and we want to allow hosts to bail out if
+ // they no longer need the information (for example, if the user started editing again).
+ let cancellationToken: CancellationToken | undefined;
+ let requestedExternalEmitHelpers: ExternalEmitHelpers;
+ let externalHelpersModule: ts.Symbol;
+
+ const Symbol = objectAllocator.getSymbolConstructor();
+ const Type = objectAllocator.getTypeConstructor();
+ const Signature = objectAllocator.getSignatureConstructor();
+
+ let typeCount = 0;
+ let symbolCount = 0;
+ let enumCount = 0;
+ let totalInstantiationCount = 0;
+ let instantiationCount = 0;
+ let instantiationDepth = 0;
+ let inlineLevel = 0;
+ let currentNode: Node | undefined;
+
+ const emptySymbols = createSymbolTable();
+ const arrayVariances = [VarianceFlags.Covariant];
+
+ const compilerOptions = host.getCompilerOptions();
+ const languageVersion = getEmitScriptTarget(compilerOptions);
+ const moduleKind = getEmitModuleKind(compilerOptions);
+ const useDefineForClassFields = getUseDefineForClassFields(compilerOptions);
+ const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
+ const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
+ const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes");
+ const strictBindCallApply = getStrictOptionValue(compilerOptions, "strictBindCallApply");
+ const strictPropertyInitialization = getStrictOptionValue(compilerOptions, "strictPropertyInitialization");
+ const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny");
+ const noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis");
+ const useUnknownInCatchVariables = getStrictOptionValue(compilerOptions, "useUnknownInCatchVariables");
+ const keyofStringsOnly = !!compilerOptions.keyofStringsOnly;
+ const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : ObjectFlags.FreshLiteral;
+ const exactOptionalPropertyTypes = compilerOptions.exactOptionalPropertyTypes;
+
+ const checkBinaryExpression = createCheckBinaryExpression();
+ const emitResolver = createResolver();
+ const nodeBuilder = createNodeBuilder();
+
+ const globals = createSymbolTable();
+ const undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined" as __String);
+ undefinedSymbol.declarations = [];
+
+ const globalThisSymbol = createSymbol(SymbolFlags.Module, "globalThis" as __String, CheckFlags.Readonly);
+ globalThisSymbol.exports = globals;
+ globalThisSymbol.declarations = [];
+ globals.set(globalThisSymbol.escapedName, globalThisSymbol);
+
+ const argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments" as __String);
+ const requireSymbol = createSymbol(SymbolFlags.Property, "require" as __String);
+
+ /** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */
+ let apparentArgumentCount: number | undefined;
+
+ // for public members that accept a Node or one of its subtypes, we must guard against
+ // synthetic nodes created during transformations by calling `getParseTreeNode`.
+ // for most of these, we perform the guard only on `checker` to avoid any possible
+ // extra cost of calling `getParseTreeNode` when calling these functions from inside the
+ // checker.
+ const checker: TypeChecker = {
+ getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"),
+ getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"),
+ getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount,
+ getTypeCount: () => typeCount,
+ getInstantiationCount: () => totalInstantiationCount,
+ getRelationCacheSizes: () => ({
+ assignable: assignableRelation.size,
+ identity: identityRelation.size,
+ subtype: subtypeRelation.size,
+ strictSubtype: strictSubtypeRelation.size,
+ }),
+ isUndefinedSymbol: symbol => symbol === undefinedSymbol,
+ isArgumentsSymbol: symbol => symbol === argumentsSymbol,
+ isUnknownSymbol: symbol => symbol === unknownSymbol,
+ getMergedSymbol,
+ getDiagnostics,
+ getGlobalDiagnostics,
+ getRecursionIdentity,
+ getUnmatchedProperties,
+ getTypeOfSymbolAtLocation: (symbol, locationIn) => {
+ const location = getParseTreeNode(locationIn);
+ return location ? getTypeOfSymbolAtLocation(symbol, location) : errorType;
+ },
+ getTypeOfSymbol,
+ getSymbolsOfParameterPropertyDeclaration: (parameterIn, parameterName) => {
+ const parameter = getParseTreeNode(parameterIn, isParameter);
+ if (parameter === undefined)
+ return Debug.fail("Cannot get symbols of a synthetic parameter that cannot be resolved to a parse-tree node.");
+ return getSymbolsOfParameterPropertyDeclaration(parameter, escapeLeadingUnderscores(parameterName));
+ },
+ getDeclaredTypeOfSymbol,
+ getPropertiesOfType,
+ getPropertyOfType: (type, name) => getPropertyOfType(type, escapeLeadingUnderscores(name)),
+ getPrivateIdentifierPropertyOfType: (leftType: ts.Type, name: string, location: Node) => {
+ const node = getParseTreeNode(location);
+ if (!node) {
+ return undefined;
+ }
+ const propName = escapeLeadingUnderscores(name);
+ const lexicallyScopedIdentifier = lookupSymbolForPrivateIdentifierDeclaration(propName, node);
+ return lexicallyScopedIdentifier ? getPrivateIdentifierPropertyOfType(leftType, lexicallyScopedIdentifier) : undefined;
+ },
+ getTypeOfPropertyOfType: (type, name) => getTypeOfPropertyOfType(type, escapeLeadingUnderscores(name)),
+ getIndexInfoOfType: (type, kind) => getIndexInfoOfType(type, kind === IndexKind.String ? stringType : numberType),
+ getIndexInfosOfType,
+ getSignaturesOfType,
+ getIndexTypeOfType: (type, kind) => getIndexTypeOfType(type, kind === IndexKind.String ? stringType : numberType),
+ getIndexType: type => getIndexType(type),
+ getBaseTypes,
+ getBaseTypeOfLiteralType,
+ getWidenedType,
+ getTypeFromTypeNode: nodeIn => {
+ const node = getParseTreeNode(nodeIn, isTypeNode);
+ return node ? getTypeFromTypeNode(node) : errorType;
+ },
+ getParameterType: getTypeAtPosition,
+ getParameterIdentifierNameAtPosition,
+ getPromisedTypeOfPromise,
+ getAwaitedType: type => getAwaitedType(type),
+ getReturnTypeOfSignature,
+ isNullableType,
+ getNullableType,
+ getNonNullableType,
+ getNonOptionalType: removeOptionalTypeMarker,
+ getTypeArguments,
+ typeToTypeNode: nodeBuilder.typeToTypeNode,
+ indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration,
+ signatureToSignatureDeclaration: nodeBuilder.signatureToSignatureDeclaration,
+ symbolToEntityName: nodeBuilder.symbolToEntityName,
+ symbolToExpression: nodeBuilder.symbolToExpression,
+ symbolToTypeParameterDeclarations: nodeBuilder.symbolToTypeParameterDeclarations,
+ symbolToParameterDeclaration: nodeBuilder.symbolToParameterDeclaration,
+ typeParameterToDeclaration: nodeBuilder.typeParameterToDeclaration,
+ getSymbolsInScope: (locationIn, meaning) => {
+ const location = getParseTreeNode(locationIn);
+ return location ? getSymbolsInScope(location, meaning) : [];
+ },
+ getSymbolAtLocation: nodeIn => {
+ const node = getParseTreeNode(nodeIn);
+ // set ignoreErrors: true because any lookups invoked by the API shouldn't cause any new errors
+ return node ? getSymbolAtLocation(node, /*ignoreErrors*/ true) : undefined;
+ },
+ getIndexInfosAtLocation: nodeIn => {
+ const node = getParseTreeNode(nodeIn);
+ return node ? getIndexInfosAtLocation(node) : undefined;
+ },
+ getShorthandAssignmentValueSymbol: nodeIn => {
+ const node = getParseTreeNode(nodeIn);
+ return node ? getShorthandAssignmentValueSymbol(node) : undefined;
+ },
+ getExportSpecifierLocalTargetSymbol: nodeIn => {
+ const node = getParseTreeNode(nodeIn, isExportSpecifier);
+ return node ? getExportSpecifierLocalTargetSymbol(node) : undefined;
+ },
+ getExportSymbolOfSymbol(symbol) {
+ return getMergedSymbol(symbol.exportSymbol || symbol);
+ },
+ getTypeAtLocation: nodeIn => {
+ const node = getParseTreeNode(nodeIn);
+ return node ? getTypeOfNode(node) : errorType;
+ },
+ getTypeOfAssignmentPattern: nodeIn => {
+ const node = getParseTreeNode(nodeIn, isAssignmentPattern);
+ return node && getTypeOfAssignmentPattern(node) || errorType;
+ },
+ getPropertySymbolOfDestructuringAssignment: locationIn => {
+ const location = getParseTreeNode(locationIn, isIdentifier);
+ return location ? getPropertySymbolOfDestructuringAssignment(location) : undefined;
+ },
+ signatureToString: (signature, enclosingDeclaration, flags, kind) => {
+ return signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind);
+ },
+ typeToString: (type, enclosingDeclaration, flags) => {
+ return typeToString(type, getParseTreeNode(enclosingDeclaration), flags);
+ },
+ symbolToString: (symbol, enclosingDeclaration, meaning, flags) => {
+ return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning, flags);
+ },
+ typePredicateToString: (predicate, enclosingDeclaration, flags) => {
+ return typePredicateToString(predicate, getParseTreeNode(enclosingDeclaration), flags);
+ },
+ writeSignature: (signature, enclosingDeclaration, flags, kind, writer) => {
+ return signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind, writer);
+ },
+ writeType: (type, enclosingDeclaration, flags, writer) => {
+ return typeToString(type, getParseTreeNode(enclosingDeclaration), flags, writer);
+ },
+ writeSymbol: (symbol, enclosingDeclaration, meaning, flags, writer) => {
+ return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning, flags, writer);
+ },
+ writeTypePredicate: (predicate, enclosingDeclaration, flags, writer) => {
+ return typePredicateToString(predicate, getParseTreeNode(enclosingDeclaration), flags, writer);
+ },
+ getAugmentedPropertiesOfType,
+ getRootSymbols,
+ getSymbolOfExpando,
+ getContextualType: (nodeIn: Expression, contextFlags?: ContextFlags) => {
+ const node = getParseTreeNode(nodeIn, isExpression);
+ if (!node) {
+ return undefined;
+ }
+ const containingCall = findAncestor(node, isCallLikeExpression);
+ const containingCallResolvedSignature = containingCall && getNodeLinks(containingCall).resolvedSignature;
+ if (contextFlags! & ContextFlags.Completions && containingCall) {
+ let toMarkSkip = node as Node;
+ do {
+ getNodeLinks(toMarkSkip).skipDirectInference = true;
+ toMarkSkip = toMarkSkip.parent;
+ } while (toMarkSkip && toMarkSkip !== containingCall);
+ getNodeLinks(containingCall).resolvedSignature = undefined;
+ }
+ const result = getContextualType(node, contextFlags);
+ if (contextFlags! & ContextFlags.Completions && containingCall) {
+ let toMarkSkip = node as Node;
+ do {
+ getNodeLinks(toMarkSkip).skipDirectInference = undefined;
+ toMarkSkip = toMarkSkip.parent;
+ } while (toMarkSkip && toMarkSkip !== containingCall);
+ getNodeLinks(containingCall).resolvedSignature = containingCallResolvedSignature;
+ }
+ return result;
+ },
+ getContextualTypeForObjectLiteralElement: nodeIn => {
+ const node = getParseTreeNode(nodeIn, isObjectLiteralElementLike);
+ return node ? getContextualTypeForObjectLiteralElement(node) : undefined;
+ },
+ getContextualTypeForArgumentAtIndex: (nodeIn, argIndex) => {
const node = getParseTreeNode(nodeIn, isCallLikeExpression);
- apparentArgumentCount = argumentCount;
- const res = node ? getResolvedSignature(node, candidatesOutArray, checkMode) : undefined;
- apparentArgumentCount = undefined;
- return res;
- }
+ return node && getContextualTypeForArgumentAtIndex(node, argIndex);
+ },
+ getContextualTypeForJsxAttribute: (nodeIn) => {
+ const node = getParseTreeNode(nodeIn, isJsxAttributeLike);
+ return node && getContextualTypeForJsxAttribute(node);
+ },
+ isContextSensitive,
+ getTypeOfPropertyOfContextualType,
+ getFullyQualifiedName,
+ getResolvedSignature: (node, candidatesOutArray, argumentCount) => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal),
+ getResolvedSignatureForSignatureHelp: (node, candidatesOutArray, argumentCount) => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.IsForSignatureHelp),
+ getExpandedParameters,
+ hasEffectiveRestParameter,
+ containsArgumentsReference,
+ getConstantValue: nodeIn => {
+ const node = getParseTreeNode(nodeIn, canHaveConstantValue);
+ return node ? getConstantValue(node) : undefined;
+ },
+ isValidPropertyAccess: (nodeIn, propertyName) => {
+ const node = getParseTreeNode(nodeIn, isPropertyAccessOrQualifiedNameOrImportTypeNode);
+ return !!node && isValidPropertyAccess(node, escapeLeadingUnderscores(propertyName));
+ },
+ isValidPropertyAccessForCompletions: (nodeIn, type, property) => {
+ const node = getParseTreeNode(nodeIn, isPropertyAccessExpression);
+ return !!node && isValidPropertyAccessForCompletions(node, type, property);
+ },
+ getSignatureFromDeclaration: declarationIn => {
+ const declaration = getParseTreeNode(declarationIn, isFunctionLike);
+ return declaration ? getSignatureFromDeclaration(declaration) : undefined;
+ },
+ isImplementationOfOverload: nodeIn => {
+ const node = getParseTreeNode(nodeIn, isFunctionLike);
+ return node ? isImplementationOfOverload(node) : undefined;
+ },
+ getImmediateAliasedSymbol,
+ getAliasedSymbol: resolveAlias,
+ getEmitResolver,
+ getExportsOfModule: getExportsOfModuleAsArray,
+ getExportsAndPropertiesOfModule,
+ forEachExportAndPropertyOfModule,
+ getSymbolWalker: createGetSymbolWalker(getRestTypeOfSignature, getTypePredicateOfSignature, getReturnTypeOfSignature, getBaseTypes, resolveStructuredTypeMembers, getTypeOfSymbol, getResolvedSymbol, getConstraintOfTypeParameter, getFirstIdentifier, getTypeArguments),
+ getAmbientModules,
+ getJsxIntrinsicTagNamesAt,
+ isOptionalParameter: nodeIn => {
+ const node = getParseTreeNode(nodeIn, isParameter);
+ return node ? isOptionalParameter(node) : false;
+ },
+ tryGetMemberInModuleExports: (name, symbol) => tryGetMemberInModuleExports(escapeLeadingUnderscores(name), symbol),
+ tryGetMemberInModuleExportsAndProperties: (name, symbol) => tryGetMemberInModuleExportsAndProperties(escapeLeadingUnderscores(name), symbol),
+ tryFindAmbientModule: moduleName => tryFindAmbientModule(moduleName, /*withAugmentations*/ true),
+ tryFindAmbientModuleWithoutAugmentations: moduleName => {
+ // we deliberately exclude augmentations
+ // since we are only interested in declarations of the module itself
+ return tryFindAmbientModule(moduleName, /*withAugmentations*/ false);
+ },
+ getApparentType,
+ getUnionType,
+ isTypeAssignableTo,
+ createAnonymousType,
+ createSignature,
+ createSymbol,
+ createIndexInfo,
+ getAnyType: () => anyType,
+ getStringType: () => stringType,
+ getNumberType: () => numberType,
+ createPromiseType,
+ createArrayType,
+ getElementTypeOfArrayType,
+ getBooleanType: () => booleanType,
+ getFalseType: (fresh?) => fresh ? falseType : regularFalseType,
+ getTrueType: (fresh?) => fresh ? trueType : regularTrueType,
+ getVoidType: () => voidType,
+ getUndefinedType: () => undefinedType,
+ getNullType: () => nullType,
+ getESSymbolType: () => esSymbolType,
+ getNeverType: () => neverType,
+ getOptionalType: () => optionalType,
+ getPromiseType: () => getGlobalPromiseType(/*reportErrors*/ false),
+ getPromiseLikeType: () => getGlobalPromiseLikeType(/*reportErrors*/ false),
+ isSymbolAccessible,
+ isArrayType,
+ isTupleType,
+ isArrayLikeType,
+ isTypeInvalidDueToUnionDiscriminant,
+ getExactOptionalProperties,
+ getAllPossiblePropertiesOfTypes,
+ getSuggestedSymbolForNonexistentProperty,
+ getSuggestionForNonexistentProperty,
+ getSuggestedSymbolForNonexistentJSXAttribute,
+ getSuggestedSymbolForNonexistentSymbol: (location, name, meaning) => getSuggestedSymbolForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning),
+ getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning),
+ getSuggestedSymbolForNonexistentModule,
+ getSuggestionForNonexistentExport,
+ getSuggestedSymbolForNonexistentClassMember,
+ getBaseConstraintOfType,
+ getDefaultFromTypeParameter: type => type && type.flags & TypeFlags.TypeParameter ? getDefaultFromTypeParameter(type as TypeParameter) : undefined,
+ resolveName(name, location, meaning, excludeGlobals) {
+ return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false, excludeGlobals);
+ },
+ getJsxNamespace: n => unescapeLeadingUnderscores(getJsxNamespace(n)),
+ getJsxFragmentFactory: n => {
+ const jsxFragmentFactory = getJsxFragmentFactoryEntity(n);
+ return jsxFragmentFactory && unescapeLeadingUnderscores(getFirstIdentifier(jsxFragmentFactory).escapedText);
+ },
+ getAccessibleSymbolChain,
+ getTypePredicateOfSignature,
+ resolveExternalModuleName: moduleSpecifierIn => {
+ const moduleSpecifier = getParseTreeNode(moduleSpecifierIn, isExpression);
+ return moduleSpecifier && resolveExternalModuleName(moduleSpecifier, moduleSpecifier, /*ignoreErrors*/ true);
+ },
+ resolveExternalModuleSymbol,
+ tryGetThisTypeAt: (nodeIn, includeGlobalThis) => {
+ const node = getParseTreeNode(nodeIn);
+ return node && tryGetThisTypeAt(node, includeGlobalThis);
+ },
+ getTypeArgumentConstraint: nodeIn => {
+ const node = getParseTreeNode(nodeIn, isTypeNode);
+ return node && getTypeArgumentConstraint(node);
+ },
+ getSuggestionDiagnostics: (fileIn, ct) => {
+ const file = getParseTreeNode(fileIn, isSourceFile) || Debug.fail("Could not determine parsed source file.");
+ if (skipTypeChecking(file, compilerOptions, host)) {
+ return emptyArray;
+ }
- const tupleTypes = new Map();
- const unionTypes = new Map();
- const intersectionTypes = new Map();
- const stringLiteralTypes = new Map();
- const numberLiteralTypes = new Map();
- const bigIntLiteralTypes = new Map();
- const enumLiteralTypes = new Map();
- const indexedAccessTypes = new Map();
- const templateLiteralTypes = new Map();
- const stringMappingTypes = new Map();
- const substitutionTypes = new Map();
- const subtypeReductionCache = new Map();
- const evolvingArrayTypes: EvolvingArrayType[] = [];
- const undefinedProperties: SymbolTable = new Map();
-
- const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown" as __String);
- const resolvingSymbol = createSymbol(0, InternalSymbolName.Resolving);
- const unresolvedSymbols = new Map();
- const errorTypes = new Map();
-
- const anyType = createIntrinsicType(TypeFlags.Any, "any");
- const autoType = createIntrinsicType(TypeFlags.Any, "any");
- const wildcardType = createIntrinsicType(TypeFlags.Any, "any");
- const errorType = createIntrinsicType(TypeFlags.Any, "error");
- const unresolvedType = createIntrinsicType(TypeFlags.Any, "unresolved");
- const nonInferrableAnyType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.ContainsWideningType);
- const intrinsicMarkerType = createIntrinsicType(TypeFlags.Any, "intrinsic");
- const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown");
- const nonNullUnknownType = createIntrinsicType(TypeFlags.Unknown, "unknown");
- const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
- const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined, "undefined", ObjectFlags.ContainsWideningType);
- const optionalType = createIntrinsicType(TypeFlags.Undefined, "undefined");
- const missingType = exactOptionalPropertyTypes ? createIntrinsicType(TypeFlags.Undefined, "undefined") : undefinedType;
- const nullType = createIntrinsicType(TypeFlags.Null, "null");
- const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null, "null", ObjectFlags.ContainsWideningType);
- const stringType = createIntrinsicType(TypeFlags.String, "string");
- const numberType = createIntrinsicType(TypeFlags.Number, "number");
- const bigintType = createIntrinsicType(TypeFlags.BigInt, "bigint");
- const falseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false") as FreshableIntrinsicType;
- const regularFalseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false") as FreshableIntrinsicType;
- const trueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true") as FreshableIntrinsicType;
- const regularTrueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true") as FreshableIntrinsicType;
- trueType.regularType = regularTrueType;
- trueType.freshType = trueType;
- regularTrueType.regularType = regularTrueType;
- regularTrueType.freshType = trueType;
- falseType.regularType = regularFalseType;
- falseType.freshType = falseType;
- regularFalseType.regularType = regularFalseType;
- regularFalseType.freshType = falseType;
- const booleanType = getUnionType([regularFalseType, regularTrueType]);
- const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
- const voidType = createIntrinsicType(TypeFlags.Void, "void");
- const neverType = createIntrinsicType(TypeFlags.Never, "never");
- const silentNeverType = createIntrinsicType(TypeFlags.Never, "never");
- const nonInferrableType = createIntrinsicType(TypeFlags.Never, "never", ObjectFlags.NonInferrableType);
- const implicitNeverType = createIntrinsicType(TypeFlags.Never, "never");
- const unreachableNeverType = createIntrinsicType(TypeFlags.Never, "never");
- const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
- const stringOrNumberType = getUnionType([stringType, numberType]);
- const stringNumberSymbolType = getUnionType([stringType, numberType, esSymbolType]);
- const keyofConstraintType = keyofStringsOnly ? stringType : stringNumberSymbolType;
- const numberOrBigIntType = getUnionType([numberType, bigintType]);
- const templateConstraintType = getUnionType([stringType, numberType, booleanType, bigintType, nullType, undefinedType]) as UnionType;
-
- const restrictiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(t as TypeParameter) : t);
- const permissiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? wildcardType : t);
-
- const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
- const emptyJsxObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
- emptyJsxObjectType.objectFlags |= ObjectFlags.JsxAttributes;
-
- const emptyTypeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type);
- emptyTypeLiteralSymbol.members = createSymbolTable();
- const emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, emptyArray);
-
- const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray) as ObjectType as GenericType;
- emptyGenericType.instantiations = new Map();
-
- const anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
- // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated
- // in getPropagatingFlagsOfTypes, and it is checked in inferFromTypes.
- anyFunctionType.objectFlags |= ObjectFlags.NonInferrableType;
-
- const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
- const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
- const resolvingDefaultType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
-
- const markerSuperType = createTypeParameter();
- const markerSubType = createTypeParameter();
- markerSubType.constraint = markerSuperType;
- const markerOtherType = createTypeParameter();
-
- const noTypePredicate = createTypePredicate(TypePredicateKind.Identifier, "<>", 0, anyType);
-
- const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None);
- const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, errorType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None);
- const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None);
- const silentNeverSignature = createSignature(undefined, undefined, undefined, emptyArray, silentNeverType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None);
-
- const enumNumberIndexInfo = createIndexInfo(numberType, stringType, /*isReadonly*/ true);
-
- const iterationTypesCache = new Map(); // cache for common IterationTypes instances
- const noIterationTypes: IterationTypes = {
- get yieldType(): Type { return Debug.fail("Not supported"); },
- get returnType(): Type { return Debug.fail("Not supported"); },
- get nextType(): Type { return Debug.fail("Not supported"); },
- };
+ let diagnostics: DiagnosticWithLocation[] | undefined;
+ try {
+ // Record the cancellation token so it can be checked later on during checkSourceElement.
+ // Do this in a finally block so we can ensure that it gets reset back to nothing after
+ // this call is done.
+ cancellationToken = ct;
- const anyIterationTypes = createIterationTypes(anyType, anyType, anyType);
- const anyIterationTypesExceptNext = createIterationTypes(anyType, anyType, unknownType);
- const defaultIterationTypes = createIterationTypes(neverType, anyType, undefinedType); // default iteration types for `Iterator`.
-
- const asyncIterationTypesResolver: IterationTypesResolver = {
- iterableCacheKey: "iterationTypesOfAsyncIterable",
- iteratorCacheKey: "iterationTypesOfAsyncIterator",
- iteratorSymbolName: "asyncIterator",
- getGlobalIteratorType: getGlobalAsyncIteratorType,
- getGlobalIterableType: getGlobalAsyncIterableType,
- getGlobalIterableIteratorType: getGlobalAsyncIterableIteratorType,
- getGlobalGeneratorType: getGlobalAsyncGeneratorType,
- resolveIterationType: getAwaitedType,
- mustHaveANextMethodDiagnostic: Diagnostics.An_async_iterator_must_have_a_next_method,
- mustBeAMethodDiagnostic: Diagnostics.The_0_property_of_an_async_iterator_must_be_a_method,
- mustHaveAValueDiagnostic: Diagnostics.The_type_returned_by_the_0_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property,
- };
+ // Ensure file is type checked
+ checkSourceFile(file);
+ Debug.assert(!!(getNodeLinks(file).flags & NodeCheckFlags.TypeChecked));
- const syncIterationTypesResolver: IterationTypesResolver = {
- iterableCacheKey: "iterationTypesOfIterable",
- iteratorCacheKey: "iterationTypesOfIterator",
- iteratorSymbolName: "iterator",
- getGlobalIteratorType,
- getGlobalIterableType,
- getGlobalIterableIteratorType,
- getGlobalGeneratorType,
- resolveIterationType: (type, _errorNode) => type,
- mustHaveANextMethodDiagnostic: Diagnostics.An_iterator_must_have_a_next_method,
- mustBeAMethodDiagnostic: Diagnostics.The_0_property_of_an_iterator_must_be_a_method,
- mustHaveAValueDiagnostic: Diagnostics.The_type_returned_by_the_0_method_of_an_iterator_must_have_a_value_property,
- };
+ diagnostics = addRange(diagnostics, suggestionDiagnostics.getDiagnostics(file.fileName));
+ checkUnusedIdentifiers(getPotentiallyUnusedIdentifiers(file), (containingNode, kind, diag) => {
+ if (!containsParseError(containingNode) && !unusedIsError(kind, !!(containingNode.flags & NodeFlags.Ambient))) {
+ (diagnostics || (diagnostics = [])).push({ ...diag, category: DiagnosticCategory.Suggestion });
+ }
+ });
- interface DuplicateInfoForSymbol {
- readonly firstFileLocations: Declaration[];
- readonly secondFileLocations: Declaration[];
- readonly isBlockScoped: boolean;
- }
- interface DuplicateInfoForFiles {
- readonly firstFile: SourceFile;
- readonly secondFile: SourceFile;
- /** Key is symbol name. */
- readonly conflictingSymbols: ESMap;
- }
- /** Key is "/path/to/a.ts|/path/to/b.ts". */
- let amalgamatedDuplicates: ESMap | undefined;
- const reverseMappedCache = new Map();
- let inInferTypeForHomomorphicMappedType = false;
- let ambientModulesCache: Symbol[] | undefined;
- /**
- * List of every ambient module with a "*" wildcard.
- * Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches.
- * This is only used if there is no exact match.
- */
- let patternAmbientModules: PatternAmbientModule[];
- let patternAmbientModuleAugmentations: ESMap | undefined;
-
- let globalObjectType: ObjectType;
- let globalFunctionType: ObjectType;
- let globalCallableFunctionType: ObjectType;
- let globalNewableFunctionType: ObjectType;
- let globalArrayType: GenericType;
- let globalReadonlyArrayType: GenericType;
- let globalStringType: ObjectType;
- let globalNumberType: ObjectType;
- let globalBooleanType: ObjectType;
- let globalRegExpType: ObjectType;
- let globalThisType: GenericType;
- let anyArrayType: Type;
- let autoArrayType: Type;
- let anyReadonlyArrayType: Type;
- let deferredGlobalNonNullableTypeAlias: Symbol;
-
- // The library files are only loaded when the feature is used.
- // This allows users to just specify library files they want to used through --lib
- // and they will not get an error from not having unrelated library files
- let deferredGlobalESSymbolConstructorSymbol: Symbol | undefined;
- let deferredGlobalESSymbolConstructorTypeSymbol: Symbol | undefined;
- let deferredGlobalESSymbolType: ObjectType | undefined;
- let deferredGlobalTypedPropertyDescriptorType: GenericType;
- let deferredGlobalPromiseType: GenericType | undefined;
- let deferredGlobalPromiseLikeType: GenericType | undefined;
- let deferredGlobalPromiseConstructorSymbol: Symbol | undefined;
- let deferredGlobalPromiseConstructorLikeType: ObjectType | undefined;
- let deferredGlobalIterableType: GenericType | undefined;
- let deferredGlobalIteratorType: GenericType | undefined;
- let deferredGlobalIterableIteratorType: GenericType | undefined;
- let deferredGlobalGeneratorType: GenericType | undefined;
- let deferredGlobalIteratorYieldResultType: GenericType | undefined;
- let deferredGlobalIteratorReturnResultType: GenericType | undefined;
- let deferredGlobalAsyncIterableType: GenericType | undefined;
- let deferredGlobalAsyncIteratorType: GenericType | undefined;
- let deferredGlobalAsyncIterableIteratorType: GenericType | undefined;
- let deferredGlobalAsyncGeneratorType: GenericType | undefined;
- let deferredGlobalTemplateStringsArrayType: ObjectType | undefined;
- let deferredGlobalImportMetaType: ObjectType;
- let deferredGlobalImportMetaExpressionType: ObjectType;
- let deferredGlobalImportCallOptionsType: ObjectType | undefined;
- let deferredGlobalExtractSymbol: Symbol | undefined;
- let deferredGlobalOmitSymbol: Symbol | undefined;
- let deferredGlobalAwaitedSymbol: Symbol | undefined;
- let deferredGlobalBigIntType: ObjectType | undefined;
-
- const allPotentiallyUnusedIdentifiers = new Map(); // key is file name
-
- let flowLoopStart = 0;
- let flowLoopCount = 0;
- let sharedFlowCount = 0;
- let flowAnalysisDisabled = false;
- let flowInvocationCount = 0;
- let lastFlowNode: FlowNode | undefined;
- let lastFlowNodeReachable: boolean;
- let flowTypeCache: Type[] | undefined;
-
- const emptyStringType = getStringLiteralType("");
- const zeroType = getNumberLiteralType(0);
- const zeroBigIntType = getBigIntLiteralType({ negative: false, base10Value: "0" });
-
- const resolutionTargets: TypeSystemEntity[] = [];
- const resolutionResults: boolean[] = [];
- const resolutionPropertyNames: TypeSystemPropertyName[] = [];
-
- let suggestionCount = 0;
- const maximumSuggestionCount = 10;
- const mergedSymbols: Symbol[] = [];
- const symbolLinks: SymbolLinks[] = [];
- const nodeLinks: NodeLinks[] = [];
- const flowLoopCaches: ESMap[] = [];
- const flowLoopNodes: FlowNode[] = [];
- const flowLoopKeys: string[] = [];
- const flowLoopTypes: Type[][] = [];
- const sharedFlowNodes: FlowNode[] = [];
- const sharedFlowTypes: FlowType[] = [];
- const flowNodeReachable: (boolean | undefined)[] = [];
- const flowNodePostSuper: (boolean | undefined)[] = [];
- const potentialThisCollisions: Node[] = [];
- const potentialNewTargetCollisions: Node[] = [];
- const potentialWeakMapSetCollisions: Node[] = [];
- const potentialReflectCollisions: Node[] = [];
- const awaitedTypeStack: number[] = [];
-
- const diagnostics = createDiagnosticCollection();
- const suggestionDiagnostics = createDiagnosticCollection();
-
- const typeofTypesByName: ReadonlyESMap = new Map(getEntries({
- string: stringType,
- number: numberType,
- bigint: bigintType,
- boolean: booleanType,
- symbol: esSymbolType,
- undefined: undefinedType
- }));
- const typeofType = createTypeofType();
-
- let _jsxNamespace: __String;
- let _jsxFactoryEntity: EntityName | undefined;
- let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined;
-
- const subtypeRelation = new Map();
- const strictSubtypeRelation = new Map();
- const assignableRelation = new Map();
- const comparableRelation = new Map();
- const identityRelation = new Map();
- const enumRelation = new Map();
-
- const builtinGlobals = createSymbolTable();
- builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol);
-
- // Extensions suggested for path imports when module resolution is node12 or higher.
- // The first element of each tuple is the extension a file has.
- // The second element of each tuple is the extension that should be used in a path import.
- // e.g. if we want to import file `foo.mts`, we should write `import {} from "./foo.mjs".
- const suggestedExtensions: [string, string][] = [
- [".mts", ".mjs"],
- [".ts", ".js"],
- [".cts", ".cjs"],
- [".mjs", ".mjs"],
- [".js", ".js"],
- [".cjs", ".cjs"],
- [".tsx", compilerOptions.jsx === JsxEmit.Preserve ? ".jsx" : ".js"],
- [".jsx", ".jsx"],
- [".json", ".json"],
- ];
-
- initializeTypeChecker();
-
- return checker;
-
- function getJsxNamespace(location: Node | undefined): __String {
- if (location) {
- const file = getSourceFileOfNode(location);
- if (file) {
- if (isJsxOpeningFragment(location)) {
- if (file.localJsxFragmentNamespace) {
- return file.localJsxFragmentNamespace;
- }
- const jsxFragmentPragma = file.pragmas.get("jsxfrag");
- if (jsxFragmentPragma) {
- const chosenPragma = isArray(jsxFragmentPragma) ? jsxFragmentPragma[0] : jsxFragmentPragma;
- file.localJsxFragmentFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion);
- visitNode(file.localJsxFragmentFactory, markAsSynthetic);
- if (file.localJsxFragmentFactory) {
- return file.localJsxFragmentNamespace = getFirstIdentifier(file.localJsxFragmentFactory).escapedText;
- }
- }
- const entity = getJsxFragmentFactoryEntity(location);
- if (entity) {
- file.localJsxFragmentFactory = entity;
- return file.localJsxFragmentNamespace = getFirstIdentifier(entity).escapedText;
- }
+ return diagnostics || emptyArray;
+ }
+ finally {
+ cancellationToken = undefined;
+ }
+ },
+
+ runWithCancellationToken: (token, callback) => {
+ try {
+ cancellationToken = token;
+ return callback(checker);
+ }
+ finally {
+ cancellationToken = undefined;
+ }
+ },
+
+ getLocalTypeParametersOfClassOrInterfaceOrTypeAlias,
+ isDeclarationVisible,
+ isPropertyAccessible,
+ getTypeOnlyAliasDeclaration,
+ getMemberOverrideModifierStatus,
+ };
+
+ function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: ts.Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): ts.Signature | undefined {
+ const node = getParseTreeNode(nodeIn, isCallLikeExpression);
+ apparentArgumentCount = argumentCount;
+ const res = node ? getResolvedSignature(node, candidatesOutArray, checkMode) : undefined;
+ apparentArgumentCount = undefined;
+ return res;
+ }
+
+ const tupleTypes = new ts.Map();
+ const unionTypes = new ts.Map();
+ const intersectionTypes = new ts.Map();
+ const stringLiteralTypes = new ts.Map();
+ const numberLiteralTypes = new ts.Map();
+ const bigIntLiteralTypes = new ts.Map();
+ const enumLiteralTypes = new ts.Map();
+ const indexedAccessTypes = new ts.Map();
+ const templateLiteralTypes = new ts.Map();
+ const stringMappingTypes = new ts.Map();
+ const substitutionTypes = new ts.Map();
+ const subtypeReductionCache = new ts.Map();
+ const evolvingArrayTypes: EvolvingArrayType[] = [];
+ const undefinedProperties: SymbolTable = new ts.Map();
+
+ const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown" as __String);
+ const resolvingSymbol = createSymbol(0, InternalSymbolName.Resolving);
+ const unresolvedSymbols = new ts.Map();
+ const errorTypes = new ts.Map();
+
+ const anyType = createIntrinsicType(TypeFlags.Any, "any");
+ const autoType = createIntrinsicType(TypeFlags.Any, "any");
+ const wildcardType = createIntrinsicType(TypeFlags.Any, "any");
+ const errorType = createIntrinsicType(TypeFlags.Any, "error");
+ const unresolvedType = createIntrinsicType(TypeFlags.Any, "unresolved");
+ const nonInferrableAnyType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.ContainsWideningType);
+ const intrinsicMarkerType = createIntrinsicType(TypeFlags.Any, "intrinsic");
+ const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown");
+ const nonNullUnknownType = createIntrinsicType(TypeFlags.Unknown, "unknown");
+ const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
+ const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined, "undefined", ObjectFlags.ContainsWideningType);
+ const optionalType = createIntrinsicType(TypeFlags.Undefined, "undefined");
+ const missingType = exactOptionalPropertyTypes ? createIntrinsicType(TypeFlags.Undefined, "undefined") : undefinedType;
+ const nullType = createIntrinsicType(TypeFlags.Null, "null");
+ const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null, "null", ObjectFlags.ContainsWideningType);
+ const stringType = createIntrinsicType(TypeFlags.String, "string");
+ const numberType = createIntrinsicType(TypeFlags.Number, "number");
+ const bigintType = createIntrinsicType(TypeFlags.BigInt, "bigint");
+ const falseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false") as FreshableIntrinsicType;
+ const regularFalseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false") as FreshableIntrinsicType;
+ const trueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true") as FreshableIntrinsicType;
+ const regularTrueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true") as FreshableIntrinsicType;
+ trueType.regularType = regularTrueType;
+ trueType.freshType = trueType;
+ regularTrueType.regularType = regularTrueType;
+ regularTrueType.freshType = trueType;
+ falseType.regularType = regularFalseType;
+ falseType.freshType = falseType;
+ regularFalseType.regularType = regularFalseType;
+ regularFalseType.freshType = falseType;
+ const booleanType = getUnionType([regularFalseType, regularTrueType]);
+ const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
+ const voidType = createIntrinsicType(TypeFlags.Void, "void");
+ const neverType = createIntrinsicType(TypeFlags.Never, "never");
+ const silentNeverType = createIntrinsicType(TypeFlags.Never, "never");
+ const nonInferrableType = createIntrinsicType(TypeFlags.Never, "never", ObjectFlags.NonInferrableType);
+ const implicitNeverType = createIntrinsicType(TypeFlags.Never, "never");
+ const unreachableNeverType = createIntrinsicType(TypeFlags.Never, "never");
+ const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
+ const stringOrNumberType = getUnionType([stringType, numberType]);
+ const stringNumberSymbolType = getUnionType([stringType, numberType, esSymbolType]);
+ const keyofConstraintType = keyofStringsOnly ? stringType : stringNumberSymbolType;
+ const numberOrBigIntType = getUnionType([numberType, bigintType]);
+ const templateConstraintType = getUnionType([stringType, numberType, booleanType, bigintType, nullType, undefinedType]) as UnionType;
+
+ const restrictiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(t as TypeParameter) : t);
+ const permissiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? wildcardType : t);
+
+ const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
+ const emptyJsxObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
+ emptyJsxObjectType.objectFlags |= ObjectFlags.JsxAttributes;
+
+ const emptyTypeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type);
+ emptyTypeLiteralSymbol.members = createSymbolTable();
+ const emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, emptyArray);
+
+ const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray) as ObjectType as GenericType;
+ emptyGenericType.instantiations = new ts.Map();
+
+ const anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
+ // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated
+ // in getPropagatingFlagsOfTypes, and it is checked in inferFromTypes.
+ anyFunctionType.objectFlags |= ObjectFlags.NonInferrableType;
+
+ const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
+ const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
+ const resolvingDefaultType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
+
+ const markerSuperType = createTypeParameter();
+ const markerSubType = createTypeParameter();
+ markerSubType.constraint = markerSuperType;
+ const markerOtherType = createTypeParameter();
+
+ const noTypePredicate = createTypePredicate(TypePredicateKind.Identifier, "<>", 0, anyType);
+
+ const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None);
+ const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, errorType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None);
+ const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None);
+ const silentNeverSignature = createSignature(undefined, undefined, undefined, emptyArray, silentNeverType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None);
+
+ const enumNumberIndexInfo = createIndexInfo(numberType, stringType, /*isReadonly*/ true);
+
+ const iterationTypesCache = new ts.Map(); // cache for common IterationTypes instances
+ const noIterationTypes: IterationTypes = {
+ get yieldType(): ts.Type { return Debug.fail("Not supported"); },
+ get returnType(): ts.Type { return Debug.fail("Not supported"); },
+ get nextType(): ts.Type { return Debug.fail("Not supported"); },
+ };
+
+ const anyIterationTypes = createIterationTypes(anyType, anyType, anyType);
+ const anyIterationTypesExceptNext = createIterationTypes(anyType, anyType, unknownType);
+ const defaultIterationTypes = createIterationTypes(neverType, anyType, undefinedType); // default iteration types for `Iterator`.
+
+ const asyncIterationTypesResolver: IterationTypesResolver = {
+ iterableCacheKey: "iterationTypesOfAsyncIterable",
+ iteratorCacheKey: "iterationTypesOfAsyncIterator",
+ iteratorSymbolName: "asyncIterator",
+ getGlobalIteratorType: getGlobalAsyncIteratorType,
+ getGlobalIterableType: getGlobalAsyncIterableType,
+ getGlobalIterableIteratorType: getGlobalAsyncIterableIteratorType,
+ getGlobalGeneratorType: getGlobalAsyncGeneratorType,
+ resolveIterationType: getAwaitedType,
+ mustHaveANextMethodDiagnostic: Diagnostics.An_async_iterator_must_have_a_next_method,
+ mustBeAMethodDiagnostic: Diagnostics.The_0_property_of_an_async_iterator_must_be_a_method,
+ mustHaveAValueDiagnostic: Diagnostics.The_type_returned_by_the_0_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property,
+ };
+
+ const syncIterationTypesResolver: IterationTypesResolver = {
+ iterableCacheKey: "iterationTypesOfIterable",
+ iteratorCacheKey: "iterationTypesOfIterator",
+ iteratorSymbolName: "iterator",
+ getGlobalIteratorType,
+ getGlobalIterableType,
+ getGlobalIterableIteratorType,
+ getGlobalGeneratorType,
+ resolveIterationType: (type, _errorNode) => type,
+ mustHaveANextMethodDiagnostic: Diagnostics.An_iterator_must_have_a_next_method,
+ mustBeAMethodDiagnostic: Diagnostics.The_0_property_of_an_iterator_must_be_a_method,
+ mustHaveAValueDiagnostic: Diagnostics.The_type_returned_by_the_0_method_of_an_iterator_must_have_a_value_property,
+ };
+
+ interface DuplicateInfoForSymbol {
+ readonly firstFileLocations: Declaration[];
+ readonly secondFileLocations: Declaration[];
+ readonly isBlockScoped: boolean;
+ }
+ interface DuplicateInfoForFiles {
+ readonly firstFile: SourceFile;
+ readonly secondFile: SourceFile;
+ /** Key is symbol name. */
+ readonly conflictingSymbols: ESMap;
+ }
+ /** Key is "/path/to/a.ts|/path/to/b.ts". */
+ let amalgamatedDuplicates: ESMap | undefined;
+ const reverseMappedCache = new ts.Map();
+ let inInferTypeForHomomorphicMappedType = false;
+ let ambientModulesCache: ts.Symbol[] | undefined;
+ /**
+ * List of every ambient module with a "*" wildcard.
+ * Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches.
+ * This is only used if there is no exact match.
+ */
+ let patternAmbientModules: PatternAmbientModule[];
+ let patternAmbientModuleAugmentations: ESMap | undefined;
+
+ let globalObjectType: ObjectType;
+ let globalFunctionType: ObjectType;
+ let globalCallableFunctionType: ObjectType;
+ let globalNewableFunctionType: ObjectType;
+ let globalArrayType: GenericType;
+ let globalReadonlyArrayType: GenericType;
+ let globalStringType: ObjectType;
+ let globalNumberType: ObjectType;
+ let globalBooleanType: ObjectType;
+ let globalRegExpType: ObjectType;
+ let globalThisType: GenericType;
+ let anyArrayType: ts.Type;
+ let autoArrayType: ts.Type;
+ let anyReadonlyArrayType: ts.Type;
+ let deferredGlobalNonNullableTypeAlias: ts.Symbol;
+
+ // The library files are only loaded when the feature is used.
+ // This allows users to just specify library files they want to used through --lib
+ // and they will not get an error from not having unrelated library files
+ let deferredGlobalESSymbolConstructorSymbol: ts.Symbol | undefined;
+ let deferredGlobalESSymbolConstructorTypeSymbol: ts.Symbol | undefined;
+ let deferredGlobalESSymbolType: ObjectType | undefined;
+ let deferredGlobalTypedPropertyDescriptorType: GenericType;
+ let deferredGlobalPromiseType: GenericType | undefined;
+ let deferredGlobalPromiseLikeType: GenericType | undefined;
+ let deferredGlobalPromiseConstructorSymbol: ts.Symbol | undefined;
+ let deferredGlobalPromiseConstructorLikeType: ObjectType | undefined;
+ let deferredGlobalIterableType: GenericType | undefined;
+ let deferredGlobalIteratorType: GenericType | undefined;
+ let deferredGlobalIterableIteratorType: GenericType | undefined;
+ let deferredGlobalGeneratorType: GenericType | undefined;
+ let deferredGlobalIteratorYieldResultType: GenericType | undefined;
+ let deferredGlobalIteratorReturnResultType: GenericType | undefined;
+ let deferredGlobalAsyncIterableType: GenericType | undefined;
+ let deferredGlobalAsyncIteratorType: GenericType | undefined;
+ let deferredGlobalAsyncIterableIteratorType: GenericType | undefined;
+ let deferredGlobalAsyncGeneratorType: GenericType | undefined;
+ let deferredGlobalTemplateStringsArrayType: ObjectType | undefined;
+ let deferredGlobalImportMetaType: ObjectType;
+ let deferredGlobalImportMetaExpressionType: ObjectType;
+ let deferredGlobalImportCallOptionsType: ObjectType | undefined;
+ let deferredGlobalExtractSymbol: ts.Symbol | undefined;
+ let deferredGlobalOmitSymbol: ts.Symbol | undefined;
+ let deferredGlobalAwaitedSymbol: ts.Symbol | undefined;
+ let deferredGlobalBigIntType: ObjectType | undefined;
+
+ const allPotentiallyUnusedIdentifiers = new ts.Map(); // key is file name
+
+ let flowLoopStart = 0;
+ let flowLoopCount = 0;
+ let sharedFlowCount = 0;
+ let flowAnalysisDisabled = false;
+ let flowInvocationCount = 0;
+ let lastFlowNode: FlowNode | undefined;
+ let lastFlowNodeReachable: boolean;
+ let flowTypeCache: ts.Type[] | undefined;
+
+ const emptyStringType = getStringLiteralType("");
+ const zeroType = getNumberLiteralType(0);
+ const zeroBigIntType = getBigIntLiteralType({ negative: false, base10Value: "0" });
+
+ const resolutionTargets: TypeSystemEntity[] = [];
+ const resolutionResults: boolean[] = [];
+ const resolutionPropertyNames: TypeSystemPropertyName[] = [];
+
+ let suggestionCount = 0;
+ const maximumSuggestionCount = 10;
+ const mergedSymbols: ts.Symbol[] = [];
+ const symbolLinks: ts.SymbolLinks[] = [];
+ const nodeLinks: ts.NodeLinks[] = [];
+ const flowLoopCaches: ESMap[] = [];
+ const flowLoopNodes: FlowNode[] = [];
+ const flowLoopKeys: string[] = [];
+ const flowLoopTypes: ts.Type[][] = [];
+ const sharedFlowNodes: FlowNode[] = [];
+ const sharedFlowTypes: FlowType[] = [];
+ const flowNodeReachable: (boolean | undefined)[] = [];
+ const flowNodePostSuper: (boolean | undefined)[] = [];
+ const potentialThisCollisions: Node[] = [];
+ const potentialNewTargetCollisions: Node[] = [];
+ const potentialWeakMapSetCollisions: Node[] = [];
+ const potentialReflectCollisions: Node[] = [];
+ const awaitedTypeStack: number[] = [];
+
+ const diagnostics = createDiagnosticCollection();
+ const suggestionDiagnostics = createDiagnosticCollection();
+
+ const typeofTypesByName: ReadonlyESMap = new ts.Map(getEntries({
+ string: stringType,
+ number: numberType,
+ bigint: bigintType,
+ boolean: booleanType,
+ symbol: esSymbolType,
+ undefined: undefinedType
+ }));
+ const typeofType = createTypeofType();
+
+ let _jsxNamespace: __String;
+ let _jsxFactoryEntity: EntityName | undefined;
+ let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined;
+
+ const subtypeRelation = new ts.Map();
+ const strictSubtypeRelation = new ts.Map();
+ const assignableRelation = new ts.Map();
+ const comparableRelation = new ts.Map();
+ const identityRelation = new ts.Map();
+ const enumRelation = new ts.Map();
+
+ const builtinGlobals = createSymbolTable();
+ builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol);
+
+ // Extensions suggested for path imports when module resolution is node12 or higher.
+ // The first element of each tuple is the extension a file has.
+ // The second element of each tuple is the extension that should be used in a path import.
+ // e.g. if we want to import file `foo.mts`, we should write `import {} from "./foo.mjs".
+ const suggestedExtensions: [
+ string,
+ string
+ ][] = [
+ [".mts", ".mjs"],
+ [".ts", ".js"],
+ [".cts", ".cjs"],
+ [".mjs", ".mjs"],
+ [".js", ".js"],
+ [".cjs", ".cjs"],
+ [".tsx", compilerOptions.jsx === JsxEmit.Preserve ? ".jsx" : ".js"],
+ [".jsx", ".jsx"],
+ [".json", ".json"],
+ ];
+
+ initializeTypeChecker();
+
+ return checker;
+
+ function getJsxNamespace(location: Node | undefined): __String {
+ if (location) {
+ const file = getSourceFileOfNode(location);
+ if (file) {
+ if (isJsxOpeningFragment(location)) {
+ if (file.localJsxFragmentNamespace) {
+ return file.localJsxFragmentNamespace;
}
- else {
- const localJsxNamespace = getLocalJsxNamespace(file);
- if (localJsxNamespace) {
- return file.localJsxNamespace = localJsxNamespace;
+ const jsxFragmentPragma = file.pragmas.get("jsxfrag");
+ if (jsxFragmentPragma) {
+ const chosenPragma = isArray(jsxFragmentPragma) ? jsxFragmentPragma[0] : jsxFragmentPragma;
+ file.localJsxFragmentFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion);
+ visitNode(file.localJsxFragmentFactory, markAsSynthetic);
+ if (file.localJsxFragmentFactory) {
+ return file.localJsxFragmentNamespace = getFirstIdentifier(file.localJsxFragmentFactory).escapedText;
}
}
- }
- }
- if (!_jsxNamespace) {
- _jsxNamespace = "React" as __String;
- if (compilerOptions.jsxFactory) {
- _jsxFactoryEntity = parseIsolatedEntityName(compilerOptions.jsxFactory, languageVersion);
- visitNode(_jsxFactoryEntity, markAsSynthetic);
- if (_jsxFactoryEntity) {
- _jsxNamespace = getFirstIdentifier(_jsxFactoryEntity).escapedText;
+ const entity = getJsxFragmentFactoryEntity(location);
+ if (entity) {
+ file.localJsxFragmentFactory = entity;
+ return file.localJsxFragmentNamespace = getFirstIdentifier(entity).escapedText;
}
}
- else if (compilerOptions.reactNamespace) {
- _jsxNamespace = escapeLeadingUnderscores(compilerOptions.reactNamespace);
+ else {
+ const localJsxNamespace = getLocalJsxNamespace(file);
+ if (localJsxNamespace) {
+ return file.localJsxNamespace = localJsxNamespace;
+ }
}
}
- if (!_jsxFactoryEntity) {
- _jsxFactoryEntity = factory.createQualifiedName(factory.createIdentifier(unescapeLeadingUnderscores(_jsxNamespace)), "createElement");
- }
- return _jsxNamespace;
}
-
- function getLocalJsxNamespace(file: SourceFile): __String | undefined {
- if (file.localJsxNamespace) {
- return file.localJsxNamespace;
- }
- const jsxPragma = file.pragmas.get("jsx");
- if (jsxPragma) {
- const chosenPragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma;
- file.localJsxFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion);
- visitNode(file.localJsxFactory, markAsSynthetic);
- if (file.localJsxFactory) {
- return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText;
+ if (!_jsxNamespace) {
+ _jsxNamespace = "React" as __String;
+ if (compilerOptions.jsxFactory) {
+ _jsxFactoryEntity = parseIsolatedEntityName(compilerOptions.jsxFactory, languageVersion);
+ visitNode(_jsxFactoryEntity, markAsSynthetic);
+ if (_jsxFactoryEntity) {
+ _jsxNamespace = getFirstIdentifier(_jsxFactoryEntity).escapedText;
}
}
+ else if (compilerOptions.reactNamespace) {
+ _jsxNamespace = escapeLeadingUnderscores(compilerOptions.reactNamespace);
+ }
}
-
- function markAsSynthetic(node: Node): VisitResult {
- setTextRangePosEnd(node, -1, -1);
- return visitEachChild(node, markAsSynthetic, nullTransformationContext);
+ if (!_jsxFactoryEntity) {
+ _jsxFactoryEntity = factory.createQualifiedName(factory.createIdentifier(unescapeLeadingUnderscores(_jsxNamespace)), "createElement");
}
+ return _jsxNamespace;
+ }
- function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) {
- // Ensure we have all the type information in place for this file so that all the
- // emitter questions of this resolver will return the right information.
- getDiagnostics(sourceFile, cancellationToken);
- return emitResolver;
+ function getLocalJsxNamespace(file: SourceFile): __String | undefined {
+ if (file.localJsxNamespace) {
+ return file.localJsxNamespace;
}
-
- function lookupOrIssueError(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
- const diagnostic = location
- ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)
- : createCompilerDiagnostic(message, arg0, arg1, arg2, arg3);
- const existing = diagnostics.lookup(diagnostic);
- if (existing) {
- return existing;
- }
- else {
- diagnostics.add(diagnostic);
- return diagnostic;
+ const jsxPragma = file.pragmas.get("jsx");
+ if (jsxPragma) {
+ const chosenPragma = isArray(jsxPragma) ? jsxPragma[0] : jsxPragma;
+ file.localJsxFactory = parseIsolatedEntityName(chosenPragma.arguments.factory, languageVersion);
+ visitNode(file.localJsxFactory, markAsSynthetic);
+ if (file.localJsxFactory) {
+ return file.localJsxNamespace = getFirstIdentifier(file.localJsxFactory).escapedText;
}
}
+ }
- function errorSkippedOn(key: keyof CompilerOptions, location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
- const diagnostic = error(location, message, arg0, arg1, arg2, arg3);
- diagnostic.skippedOn = key;
- return diagnostic;
- }
+ function markAsSynthetic(node: Node): VisitResult {
+ setTextRangePosEnd(node, -1, -1);
+ return visitEachChild(node, markAsSynthetic, nullTransformationContext);
+ }
- function createError(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
- return location
- ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)
- : createCompilerDiagnostic(message, arg0, arg1, arg2, arg3);
- }
+ function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) {
+ // Ensure we have all the type information in place for this file so that all the
+ // emitter questions of this resolver will return the right information.
+ getDiagnostics(sourceFile, cancellationToken);
+ return emitResolver;
+ }
- function error(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
- const diagnostic = createError(location, message, arg0, arg1, arg2, arg3);
+ function lookupOrIssueError(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
+ const diagnostic = location
+ ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)
+ : createCompilerDiagnostic(message, arg0, arg1, arg2, arg3);
+ const existing = diagnostics.lookup(diagnostic);
+ if (existing) {
+ return existing;
+ }
+ else {
diagnostics.add(diagnostic);
return diagnostic;
}
+ }
- function addErrorOrSuggestion(isError: boolean, diagnostic: Diagnostic) {
- if (isError) {
- diagnostics.add(diagnostic);
- }
- else {
- suggestionDiagnostics.add({ ...diagnostic, category: DiagnosticCategory.Suggestion });
- }
+ function errorSkippedOn(key: keyof CompilerOptions, location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
+ const diagnostic = error(location, message, arg0, arg1, arg2, arg3);
+ diagnostic.skippedOn = key;
+ return diagnostic;
+ }
+
+ function createError(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
+ return location
+ ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)
+ : createCompilerDiagnostic(message, arg0, arg1, arg2, arg3);
+ }
+
+ function error(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
+ const diagnostic = createError(location, message, arg0, arg1, arg2, arg3);
+ diagnostics.add(diagnostic);
+ return diagnostic;
+ }
+
+ function addErrorOrSuggestion(isError: boolean, diagnostic: Diagnostic) {
+ if (isError) {
+ diagnostics.add(diagnostic);
}
- function errorOrSuggestion(isError: boolean, location: Node, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void {
- // Pseudo-synthesized input node
- if (location.pos < 0 || location.end < 0) {
- if (!isError) {
- return; // Drop suggestions (we have no span to suggest on)
- }
- // Issue errors globally
- const file = getSourceFileOfNode(location);
- addErrorOrSuggestion(isError, "message" in message ? createFileDiagnostic(file, 0, 0, message, arg0, arg1, arg2, arg3) : createDiagnosticForFileFromMessageChain(file, message)); // eslint-disable-line no-in-operator
- return;
- }
- addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message)); // eslint-disable-line no-in-operator
+ else {
+ suggestionDiagnostics.add({ ...diagnostic, category: DiagnosticCategory.Suggestion });
}
+ }
+ function errorOrSuggestion(isError: boolean, location: Node, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void {
+ // Pseudo-synthesized input node
+ if (location.pos < 0 || location.end < 0) {
+ if (!isError) {
+ return; // Drop suggestions (we have no span to suggest on)
+ }
+ // Issue errors globally
+ const file = getSourceFileOfNode(location);
+ addErrorOrSuggestion(isError, "message" in message ? createFileDiagnostic(file, 0, 0, message, arg0, arg1, arg2, arg3) : createDiagnosticForFileFromMessageChain(file, message)); // eslint-disable-line no-in-operator
+ return;
+ }
+ addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message)); // eslint-disable-line no-in-operator
+ }
- function errorAndMaybeSuggestAwait(
- location: Node,
- maybeMissingAwait: boolean,
- message: DiagnosticMessage,
- arg0?: string | number | undefined, arg1?: string | number | undefined, arg2?: string | number | undefined, arg3?: string | number | undefined): Diagnostic {
- const diagnostic = error(location, message, arg0, arg1, arg2, arg3);
- if (maybeMissingAwait) {
- const related = createDiagnosticForNode(location, Diagnostics.Did_you_forget_to_use_await);
- addRelatedInfo(diagnostic, related);
- }
- return diagnostic;
+ function errorAndMaybeSuggestAwait(location: Node, maybeMissingAwait: boolean, message: DiagnosticMessage, arg0?: string | number | undefined, arg1?: string | number | undefined, arg2?: string | number | undefined, arg3?: string | number | undefined): Diagnostic {
+ const diagnostic = error(location, message, arg0, arg1, arg2, arg3);
+ if (maybeMissingAwait) {
+ const related = createDiagnosticForNode(location, Diagnostics.Did_you_forget_to_use_await);
+ addRelatedInfo(diagnostic, related);
}
+ return diagnostic;
+ }
- function addDeprecatedSuggestionWorker(declarations: Node | Node[], diagnostic: DiagnosticWithLocation) {
- const deprecatedTag = Array.isArray(declarations) ? forEach(declarations, getJSDocDeprecatedTag) : getJSDocDeprecatedTag(declarations);
- if (deprecatedTag) {
- addRelatedInfo(
- diagnostic,
- createDiagnosticForNode(deprecatedTag, Diagnostics.The_declaration_was_marked_as_deprecated_here)
- );
- }
- // We call `addRelatedInfo()` before adding the diagnostic to prevent duplicates.
- suggestionDiagnostics.add(diagnostic);
- return diagnostic;
+ function addDeprecatedSuggestionWorker(declarations: Node | Node[], diagnostic: DiagnosticWithLocation) {
+ const deprecatedTag = Array.isArray(declarations) ? forEach(declarations, getJSDocDeprecatedTag) : getJSDocDeprecatedTag(declarations);
+ if (deprecatedTag) {
+ addRelatedInfo(diagnostic, createDiagnosticForNode(deprecatedTag, Diagnostics.The_declaration_was_marked_as_deprecated_here));
}
+ // We call `addRelatedInfo()` before adding the diagnostic to prevent duplicates.
+ suggestionDiagnostics.add(diagnostic);
+ return diagnostic;
+ }
- function addDeprecatedSuggestion(location: Node, declarations: Node[], deprecatedEntity: string) {
- const diagnostic = createDiagnosticForNode(location, Diagnostics._0_is_deprecated, deprecatedEntity);
- return addDeprecatedSuggestionWorker(declarations, diagnostic);
- }
+ function addDeprecatedSuggestion(location: Node, declarations: Node[], deprecatedEntity: string) {
+ const diagnostic = createDiagnosticForNode(location, Diagnostics._0_is_deprecated, deprecatedEntity);
+ return addDeprecatedSuggestionWorker(declarations, diagnostic);
+ }
- function addDeprecatedSuggestionWithSignature(location: Node, declaration: Node, deprecatedEntity: string | undefined, signatureString: string) {
- const diagnostic = deprecatedEntity
- ? createDiagnosticForNode(location, Diagnostics.The_signature_0_of_1_is_deprecated, signatureString, deprecatedEntity)
- : createDiagnosticForNode(location, Diagnostics._0_is_deprecated, signatureString);
- return addDeprecatedSuggestionWorker(declaration, diagnostic);
- }
+ function addDeprecatedSuggestionWithSignature(location: Node, declaration: Node, deprecatedEntity: string | undefined, signatureString: string) {
+ const diagnostic = deprecatedEntity
+ ? createDiagnosticForNode(location, Diagnostics.The_signature_0_of_1_is_deprecated, signatureString, deprecatedEntity)
+ : createDiagnosticForNode(location, Diagnostics._0_is_deprecated, signatureString);
+ return addDeprecatedSuggestionWorker(declaration, diagnostic);
+ }
- function createSymbol(flags: SymbolFlags, name: __String, checkFlags?: CheckFlags) {
- symbolCount++;
- const symbol = (new Symbol(flags | SymbolFlags.Transient, name) as TransientSymbol);
- symbol.checkFlags = checkFlags || 0;
- return symbol;
- }
+ function createSymbol(flags: SymbolFlags, name: __String, checkFlags?: CheckFlags) {
+ symbolCount++;
+ const symbol = (new Symbol(flags | SymbolFlags.Transient, name) as TransientSymbol);
+ symbol.checkFlags = checkFlags || 0;
+ return symbol;
+ }
- function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags {
- let result: SymbolFlags = 0;
- if (flags & SymbolFlags.BlockScopedVariable) result |= SymbolFlags.BlockScopedVariableExcludes;
- if (flags & SymbolFlags.FunctionScopedVariable) result |= SymbolFlags.FunctionScopedVariableExcludes;
- if (flags & SymbolFlags.Property) result |= SymbolFlags.PropertyExcludes;
- if (flags & SymbolFlags.EnumMember) result |= SymbolFlags.EnumMemberExcludes;
- if (flags & SymbolFlags.Function) result |= SymbolFlags.FunctionExcludes;
- if (flags & SymbolFlags.Class) result |= SymbolFlags.ClassExcludes;
- if (flags & SymbolFlags.Interface) result |= SymbolFlags.InterfaceExcludes;
- if (flags & SymbolFlags.RegularEnum) result |= SymbolFlags.RegularEnumExcludes;
- if (flags & SymbolFlags.ConstEnum) result |= SymbolFlags.ConstEnumExcludes;
- if (flags & SymbolFlags.ValueModule) result |= SymbolFlags.ValueModuleExcludes;
- if (flags & SymbolFlags.Method) result |= SymbolFlags.MethodExcludes;
- if (flags & SymbolFlags.GetAccessor) result |= SymbolFlags.GetAccessorExcludes;
- if (flags & SymbolFlags.SetAccessor) result |= SymbolFlags.SetAccessorExcludes;
- if (flags & SymbolFlags.TypeParameter) result |= SymbolFlags.TypeParameterExcludes;
- if (flags & SymbolFlags.TypeAlias) result |= SymbolFlags.TypeAliasExcludes;
- if (flags & SymbolFlags.Alias) result |= SymbolFlags.AliasExcludes;
- return result;
- }
+ function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags {
+ let result: SymbolFlags = 0;
+ if (flags & SymbolFlags.BlockScopedVariable)
+ result |= SymbolFlags.BlockScopedVariableExcludes;
+ if (flags & SymbolFlags.FunctionScopedVariable)
+ result |= SymbolFlags.FunctionScopedVariableExcludes;
+ if (flags & SymbolFlags.Property)
+ result |= SymbolFlags.PropertyExcludes;
+ if (flags & SymbolFlags.EnumMember)
+ result |= SymbolFlags.EnumMemberExcludes;
+ if (flags & SymbolFlags.Function)
+ result |= SymbolFlags.FunctionExcludes;
+ if (flags & SymbolFlags.Class)
+ result |= SymbolFlags.ClassExcludes;
+ if (flags & SymbolFlags.Interface)
+ result |= SymbolFlags.InterfaceExcludes;
+ if (flags & SymbolFlags.RegularEnum)
+ result |= SymbolFlags.RegularEnumExcludes;
+ if (flags & SymbolFlags.ConstEnum)
+ result |= SymbolFlags.ConstEnumExcludes;
+ if (flags & SymbolFlags.ValueModule)
+ result |= SymbolFlags.ValueModuleExcludes;
+ if (flags & SymbolFlags.Method)
+ result |= SymbolFlags.MethodExcludes;
+ if (flags & SymbolFlags.GetAccessor)
+ result |= SymbolFlags.GetAccessorExcludes;
+ if (flags & SymbolFlags.SetAccessor)
+ result |= SymbolFlags.SetAccessorExcludes;
+ if (flags & SymbolFlags.TypeParameter)
+ result |= SymbolFlags.TypeParameterExcludes;
+ if (flags & SymbolFlags.TypeAlias)
+ result |= SymbolFlags.TypeAliasExcludes;
+ if (flags & SymbolFlags.Alias)
+ result |= SymbolFlags.AliasExcludes;
+ return result;
+ }
- function recordMergedSymbol(target: Symbol, source: Symbol) {
- if (!source.mergeId) {
- source.mergeId = nextMergeId;
- nextMergeId++;
- }
- mergedSymbols[source.mergeId] = target;
+ function recordMergedSymbol(target: ts.Symbol, source: ts.Symbol) {
+ if (!source.mergeId) {
+ source.mergeId = nextMergeId;
+ nextMergeId++;
}
+ mergedSymbols[source.mergeId] = target;
+ }
- function cloneSymbol(symbol: Symbol): Symbol {
- const result = createSymbol(symbol.flags, symbol.escapedName);
- result.declarations = symbol.declarations ? symbol.declarations.slice() : [];
- result.parent = symbol.parent;
- if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
- if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true;
- if (symbol.members) result.members = new Map(symbol.members);
- if (symbol.exports) result.exports = new Map(symbol.exports);
- recordMergedSymbol(result, symbol);
- return result;
- }
+ function cloneSymbol(symbol: ts.Symbol): ts.Symbol {
+ const result = createSymbol(symbol.flags, symbol.escapedName);
+ result.declarations = symbol.declarations ? symbol.declarations.slice() : [];
+ result.parent = symbol.parent;
+ if (symbol.valueDeclaration)
+ result.valueDeclaration = symbol.valueDeclaration;
+ if (symbol.constEnumOnlyModule)
+ result.constEnumOnlyModule = true;
+ if (symbol.members)
+ result.members = new ts.Map(symbol.members);
+ if (symbol.exports)
+ result.exports = new ts.Map(symbol.exports);
+ recordMergedSymbol(result, symbol);
+ return result;
+ }
- /**
- * Note: if target is transient, then it is mutable, and mergeSymbol with both mutate and return it.
- * If target is not transient, mergeSymbol will produce a transient clone, mutate that and return it.
- */
- function mergeSymbol(target: Symbol, source: Symbol, unidirectional = false): Symbol {
- if (!(target.flags & getExcludedSymbolFlags(source.flags)) ||
- (source.flags | target.flags) & SymbolFlags.Assignment) {
- if (source === target) {
- // This can happen when an export assigned namespace exports something also erroneously exported at the top level
- // See `declarationFileNoCrashOnExtraExportModifier` for an example
- return target;
- }
- if (!(target.flags & SymbolFlags.Transient)) {
- const resolvedTarget = resolveSymbol(target);
- if (resolvedTarget === unknownSymbol) {
- return source;
- }
- target = cloneSymbol(resolvedTarget);
- }
- // Javascript static-property-assignment declarations always merge, even though they are also values
- if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) {
- // reset flag when merging instantiated module into value module that has only const enums
- target.constEnumOnlyModule = false;
- }
- target.flags |= source.flags;
- if (source.valueDeclaration) {
- setValueDeclaration(target, source.valueDeclaration);
- }
- addRange(target.declarations, source.declarations);
- if (source.members) {
- if (!target.members) target.members = createSymbolTable();
- mergeSymbolTable(target.members, source.members, unidirectional);
- }
- if (source.exports) {
- if (!target.exports) target.exports = createSymbolTable();
- mergeSymbolTable(target.exports, source.exports, unidirectional);
- }
- if (!unidirectional) {
- recordMergedSymbol(target, source);
- }
- }
- else if (target.flags & SymbolFlags.NamespaceModule) {
- // Do not report an error when merging `var globalThis` with the built-in `globalThis`,
- // as we will already report a "Declaration name conflicts..." error, and this error
- // won't make much sense.
- if (target !== globalThisSymbol) {
- error(
- source.declarations && getNameOfDeclaration(source.declarations[0]),
- Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity,
- symbolToString(target));
- }
- }
- else { // error
- const isEitherEnum = !!(target.flags & SymbolFlags.Enum || source.flags & SymbolFlags.Enum);
- const isEitherBlockScoped = !!(target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable);
- const message = isEitherEnum
- ? Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations
- : isEitherBlockScoped
- ? Diagnostics.Cannot_redeclare_block_scoped_variable_0
- : Diagnostics.Duplicate_identifier_0;
- const sourceSymbolFile = source.declarations && getSourceFileOfNode(source.declarations[0]);
- const targetSymbolFile = target.declarations && getSourceFileOfNode(target.declarations[0]);
- const symbolName = symbolToString(source);
-
- // Collect top-level duplicate identifier errors into one mapping, so we can then merge their diagnostics if there are a bunch
- if (sourceSymbolFile && targetSymbolFile && amalgamatedDuplicates && !isEitherEnum && sourceSymbolFile !== targetSymbolFile) {
- const firstFile = comparePaths(sourceSymbolFile.path, targetSymbolFile.path) === Comparison.LessThan ? sourceSymbolFile : targetSymbolFile;
- const secondFile = firstFile === sourceSymbolFile ? targetSymbolFile : sourceSymbolFile;
- const filesDuplicates = getOrUpdate(amalgamatedDuplicates, `${firstFile.path}|${secondFile.path}`, () =>
- ({ firstFile, secondFile, conflictingSymbols: new Map() } as DuplicateInfoForFiles));
- const conflictingSymbolInfo = getOrUpdate(filesDuplicates.conflictingSymbols, symbolName, () =>
- ({ isBlockScoped: isEitherBlockScoped, firstFileLocations: [], secondFileLocations: [] } as DuplicateInfoForSymbol));
- addDuplicateLocations(conflictingSymbolInfo.firstFileLocations, source);
- addDuplicateLocations(conflictingSymbolInfo.secondFileLocations, target);
- }
- else {
- addDuplicateDeclarationErrorsForSymbols(source, message, symbolName, target);
- addDuplicateDeclarationErrorsForSymbols(target, message, symbolName, source);
+ /**
+ * Note: if target is transient, then it is mutable, and mergeSymbol with both mutate and return it.
+ * If target is not transient, mergeSymbol will produce a transient clone, mutate that and return it.
+ */
+ function mergeSymbol(target: ts.Symbol, source: ts.Symbol, unidirectional = false): ts.Symbol {
+ if (!(target.flags & getExcludedSymbolFlags(source.flags)) ||
+ (source.flags | target.flags) & SymbolFlags.Assignment) {
+ if (source === target) {
+ // This can happen when an export assigned namespace exports something also erroneously exported at the top level
+ // See `declarationFileNoCrashOnExtraExportModifier` for an example
+ return target;
+ }
+ if (!(target.flags & SymbolFlags.Transient)) {
+ const resolvedTarget = resolveSymbol(target);
+ if (resolvedTarget === unknownSymbol) {
+ return source;
}
+ target = cloneSymbol(resolvedTarget);
}
- return target;
+ // Javascript static-property-assignment declarations always merge, even though they are also values
+ if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) {
+ // reset flag when merging instantiated module into value module that has only const enums
+ target.constEnumOnlyModule = false;
+ }
+ target.flags |= source.flags;
+ if (source.valueDeclaration) {
+ setValueDeclaration(target, source.valueDeclaration);
+ }
+ addRange(target.declarations, source.declarations);
+ if (source.members) {
+ if (!target.members)
+ target.members = createSymbolTable();
+ mergeSymbolTable(target.members, source.members, unidirectional);
+ }
+ if (source.exports) {
+ if (!target.exports)
+ target.exports = createSymbolTable();
+ mergeSymbolTable(target.exports, source.exports, unidirectional);
+ }
+ if (!unidirectional) {
+ recordMergedSymbol(target, source);
+ }
+ }
+ else if (target.flags & SymbolFlags.NamespaceModule) {
+ // Do not report an error when merging `var globalThis` with the built-in `globalThis`,
+ // as we will already report a "Declaration name conflicts..." error, and this error
+ // won't make much sense.
+ if (target !== globalThisSymbol) {
+ error(source.declarations && getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target));
+ }
+ }
+ else { // error
+ const isEitherEnum = !!(target.flags & SymbolFlags.Enum || source.flags & SymbolFlags.Enum);
+ const isEitherBlockScoped = !!(target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable);
+ const message = isEitherEnum
+ ? Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations
+ : isEitherBlockScoped
+ ? Diagnostics.Cannot_redeclare_block_scoped_variable_0
+ : Diagnostics.Duplicate_identifier_0;
+ const sourceSymbolFile = source.declarations && getSourceFileOfNode(source.declarations[0]);
+ const targetSymbolFile = target.declarations && getSourceFileOfNode(target.declarations[0]);
+ const symbolName = symbolToString(source);
+
+ // Collect top-level duplicate identifier errors into one mapping, so we can then merge their diagnostics if there are a bunch
+ if (sourceSymbolFile && targetSymbolFile && amalgamatedDuplicates && !isEitherEnum && sourceSymbolFile !== targetSymbolFile) {
+ const firstFile = comparePaths(sourceSymbolFile.path, targetSymbolFile.path) === Comparison.LessThan ? sourceSymbolFile : targetSymbolFile;
+ const secondFile = firstFile === sourceSymbolFile ? targetSymbolFile : sourceSymbolFile;
+ const filesDuplicates = getOrUpdate(amalgamatedDuplicates, `${firstFile.path}|${secondFile.path}`, () => ({ firstFile, secondFile, conflictingSymbols: new ts.Map() } as DuplicateInfoForFiles));
+ const conflictingSymbolInfo = getOrUpdate(filesDuplicates.conflictingSymbols, symbolName, () => ({ isBlockScoped: isEitherBlockScoped, firstFileLocations: [], secondFileLocations: [] } as DuplicateInfoForSymbol));
+ addDuplicateLocations(conflictingSymbolInfo.firstFileLocations, source);
+ addDuplicateLocations(conflictingSymbolInfo.secondFileLocations, target);
+ }
+ else {
+ addDuplicateDeclarationErrorsForSymbols(source, message, symbolName, target);
+ addDuplicateDeclarationErrorsForSymbols(target, message, symbolName, source);
+ }
+ }
+ return target;
- function addDuplicateLocations(locs: Declaration[], symbol: Symbol): void {
- if (symbol.declarations) {
- for (const decl of symbol.declarations) {
- pushIfUnique(locs, decl);
- }
+ function addDuplicateLocations(locs: Declaration[], symbol: ts.Symbol): void {
+ if (symbol.declarations) {
+ for (const decl of symbol.declarations) {
+ pushIfUnique(locs, decl);
}
}
}
+ }
- function addDuplicateDeclarationErrorsForSymbols(target: Symbol, message: DiagnosticMessage, symbolName: string, source: Symbol) {
- forEach(target.declarations, node => {
- addDuplicateDeclarationError(node, message, symbolName, source.declarations);
- });
- }
+ function addDuplicateDeclarationErrorsForSymbols(target: ts.Symbol, message: DiagnosticMessage, symbolName: string, source: ts.Symbol) {
+ forEach(target.declarations, node => {
+ addDuplicateDeclarationError(node, message, symbolName, source.declarations);
+ });
+ }
- function addDuplicateDeclarationError(node: Declaration, message: DiagnosticMessage, symbolName: string, relatedNodes: readonly Declaration[] | undefined) {
- const errorNode = (getExpandoInitializer(node, /*isPrototypeAssignment*/ false) ? getNameOfExpando(node) : getNameOfDeclaration(node)) || node;
- const err = lookupOrIssueError(errorNode, message, symbolName);
- for (const relatedNode of relatedNodes || emptyArray) {
- const adjustedNode = (getExpandoInitializer(relatedNode, /*isPrototypeAssignment*/ false) ? getNameOfExpando(relatedNode) : getNameOfDeclaration(relatedNode)) || relatedNode;
- if (adjustedNode === errorNode) continue;
- err.relatedInformation = err.relatedInformation || [];
- const leadingMessage = createDiagnosticForNode(adjustedNode, Diagnostics._0_was_also_declared_here, symbolName);
- const followOnMessage = createDiagnosticForNode(adjustedNode, Diagnostics.and_here);
- if (length(err.relatedInformation) >= 5 || some(err.relatedInformation, r => compareDiagnostics(r, followOnMessage) === Comparison.EqualTo || compareDiagnostics(r, leadingMessage) === Comparison.EqualTo)) continue;
- addRelatedInfo(err, !length(err.relatedInformation) ? leadingMessage : followOnMessage);
- }
+ function addDuplicateDeclarationError(node: Declaration, message: DiagnosticMessage, symbolName: string, relatedNodes: readonly Declaration[] | undefined) {
+ const errorNode = (getExpandoInitializer(node, /*isPrototypeAssignment*/ false) ? getNameOfExpando(node) : getNameOfDeclaration(node)) || node;
+ const err = lookupOrIssueError(errorNode, message, symbolName);
+ for (const relatedNode of relatedNodes || emptyArray) {
+ const adjustedNode = (getExpandoInitializer(relatedNode, /*isPrototypeAssignment*/ false) ? getNameOfExpando(relatedNode) : getNameOfDeclaration(relatedNode)) || relatedNode;
+ if (adjustedNode === errorNode)
+ continue;
+ err.relatedInformation = err.relatedInformation || [];
+ const leadingMessage = createDiagnosticForNode(adjustedNode, Diagnostics._0_was_also_declared_here, symbolName);
+ const followOnMessage = createDiagnosticForNode(adjustedNode, Diagnostics.and_here);
+ if (length(err.relatedInformation) >= 5 || some(err.relatedInformation, r => compareDiagnostics(r, followOnMessage) === Comparison.EqualTo || compareDiagnostics(r, leadingMessage) === Comparison.EqualTo))
+ continue;
+ addRelatedInfo(err, !length(err.relatedInformation) ? leadingMessage : followOnMessage);
}
+ }
- function combineSymbolTables(first: SymbolTable | undefined, second: SymbolTable | undefined): SymbolTable | undefined {
- if (!first?.size) return second;
- if (!second?.size) return first;
- const combined = createSymbolTable();
- mergeSymbolTable(combined, first);
- mergeSymbolTable(combined, second);
- return combined;
- }
+ function combineSymbolTables(first: SymbolTable | undefined, second: SymbolTable | undefined): SymbolTable | undefined {
+ if (!first?.size)
+ return second;
+ if (!second?.size)
+ return first;
+ const combined = createSymbolTable();
+ mergeSymbolTable(combined, first);
+ mergeSymbolTable(combined, second);
+ return combined;
+ }
- function mergeSymbolTable(target: SymbolTable, source: SymbolTable, unidirectional = false) {
- source.forEach((sourceSymbol, id) => {
- const targetSymbol = target.get(id);
- target.set(id, targetSymbol ? mergeSymbol(targetSymbol, sourceSymbol, unidirectional) : sourceSymbol);
- });
+ function mergeSymbolTable(target: SymbolTable, source: SymbolTable, unidirectional = false) {
+ source.forEach((sourceSymbol, id) => {
+ const targetSymbol = target.get(id);
+ target.set(id, targetSymbol ? mergeSymbol(targetSymbol, sourceSymbol, unidirectional) : sourceSymbol);
+ });
+ }
+
+ function mergeModuleAugmentation(moduleName: StringLiteral | Identifier): void {
+ const moduleAugmentation = moduleName.parent as ModuleDeclaration;
+ if (moduleAugmentation.symbol.declarations?.[0] !== moduleAugmentation) {
+ // this is a combined symbol for multiple augmentations within the same file.
+ // its symbol already has accumulated information for all declarations
+ // so we need to add it just once - do the work only for first declaration
+ Debug.assert(moduleAugmentation.symbol.declarations!.length > 1);
+ return;
}
- function mergeModuleAugmentation(moduleName: StringLiteral | Identifier): void {
- const moduleAugmentation = moduleName.parent as ModuleDeclaration;
- if (moduleAugmentation.symbol.declarations?.[0] !== moduleAugmentation) {
- // this is a combined symbol for multiple augmentations within the same file.
- // its symbol already has accumulated information for all declarations
- // so we need to add it just once - do the work only for first declaration
- Debug.assert(moduleAugmentation.symbol.declarations!.length > 1);
+ if (isGlobalScopeAugmentation(moduleAugmentation)) {
+ mergeSymbolTable(globals, moduleAugmentation.symbol.exports!);
+ }
+ else {
+ // find a module that about to be augmented
+ // do not validate names of augmentations that are defined in ambient context
+ const moduleNotFoundError = !(moduleName.parent.parent.flags & NodeFlags.Ambient)
+ ? Diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found
+ : undefined;
+ let mainModule = resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError, /*isForAugmentation*/ true);
+ if (!mainModule) {
return;
}
-
- if (isGlobalScopeAugmentation(moduleAugmentation)) {
- mergeSymbolTable(globals, moduleAugmentation.symbol.exports!);
- }
- else {
- // find a module that about to be augmented
- // do not validate names of augmentations that are defined in ambient context
- const moduleNotFoundError = !(moduleName.parent.parent.flags & NodeFlags.Ambient)
- ? Diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found
- : undefined;
- let mainModule = resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError, /*isForAugmentation*/ true);
- if (!mainModule) {
- return;
- }
- // obtain item referenced by 'export='
- mainModule = resolveExternalModuleSymbol(mainModule);
- if (mainModule.flags & SymbolFlags.Namespace) {
- // If we're merging an augmentation to a pattern ambient module, we want to
- // perform the merge unidirectionally from the augmentation ('a.foo') to
- // the pattern ('*.foo'), so that 'getMergedSymbol()' on a.foo gives you
- // all the exports both from the pattern and from the augmentation, but
- // 'getMergedSymbol()' on *.foo only gives you exports from *.foo.
- if (some(patternAmbientModules, module => mainModule === module.symbol)) {
- const merged = mergeSymbol(moduleAugmentation.symbol, mainModule, /*unidirectional*/ true);
- if (!patternAmbientModuleAugmentations) {
- patternAmbientModuleAugmentations = new Map();
- }
- // moduleName will be a StringLiteral since this is not `declare global`.
- patternAmbientModuleAugmentations.set((moduleName as StringLiteral).text, merged);
+ // obtain item referenced by 'export='
+ mainModule = resolveExternalModuleSymbol(mainModule);
+ if (mainModule.flags & SymbolFlags.Namespace) {
+ // If we're merging an augmentation to a pattern ambient module, we want to
+ // perform the merge unidirectionally from the augmentation ('a.foo') to
+ // the pattern ('*.foo'), so that 'getMergedSymbol()' on a.foo gives you
+ // all the exports both from the pattern and from the augmentation, but
+ // 'getMergedSymbol()' on *.foo only gives you exports from *.foo.
+ if (some(patternAmbientModules, module => mainModule === module.symbol)) {
+ const merged = mergeSymbol(moduleAugmentation.symbol, mainModule, /*unidirectional*/ true);
+ if (!patternAmbientModuleAugmentations) {
+ patternAmbientModuleAugmentations = new ts.Map();
}
- else {
- if (mainModule.exports?.get(InternalSymbolName.ExportStar) && moduleAugmentation.symbol.exports?.size) {
- // We may need to merge the module augmentation's exports into the target symbols of the resolved exports
- const resolvedExports = getResolvedMembersOrExportsOfSymbol(mainModule, MembersOrExportsResolutionKind.resolvedExports);
- for (const [key, value] of arrayFrom(moduleAugmentation.symbol.exports.entries())) {
- if (resolvedExports.has(key) && !mainModule.exports.has(key)) {
- mergeSymbol(resolvedExports.get(key)!, value);
- }
+ // moduleName will be a StringLiteral since this is not `declare global`.
+ patternAmbientModuleAugmentations.set((moduleName as StringLiteral).text, merged);
+ }
+ else {
+ if (mainModule.exports?.get(InternalSymbolName.ExportStar) && moduleAugmentation.symbol.exports?.size) {
+ // We may need to merge the module augmentation's exports into the target symbols of the resolved exports
+ const resolvedExports = getResolvedMembersOrExportsOfSymbol(mainModule, MembersOrExportsResolutionKind.resolvedExports);
+ for (const [key, value] of arrayFrom(moduleAugmentation.symbol.exports.entries())) {
+ if (resolvedExports.has(key) && !mainModule.exports.has(key)) {
+ mergeSymbol(resolvedExports.get(key)!, value);
}
}
- mergeSymbol(mainModule, moduleAugmentation.symbol);
}
+ mergeSymbol(mainModule, moduleAugmentation.symbol);
}
- else {
- // moduleName will be a StringLiteral since this is not `declare global`.
- error(moduleName, Diagnostics.Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, (moduleName as StringLiteral).text);
- }
+ }
+ else {
+ // moduleName will be a StringLiteral since this is not `declare global`.
+ error(moduleName, Diagnostics.Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, (moduleName as StringLiteral).text);
}
}
+ }
- function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) {
- source.forEach((sourceSymbol, id) => {
- const targetSymbol = target.get(id);
- if (targetSymbol) {
- // Error on redeclarations
- forEach(targetSymbol.declarations, addDeclarationDiagnostic(unescapeLeadingUnderscores(id), message));
- }
- else {
- target.set(id, sourceSymbol);
- }
- });
-
- function addDeclarationDiagnostic(id: string, message: DiagnosticMessage) {
- return (declaration: Declaration) => diagnostics.add(createDiagnosticForNode(declaration, message, id));
+ function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) {
+ source.forEach((sourceSymbol, id) => {
+ const targetSymbol = target.get(id);
+ if (targetSymbol) {
+ // Error on redeclarations
+ forEach(targetSymbol.declarations, addDeclarationDiagnostic(unescapeLeadingUnderscores(id), message));
}
- }
+ else {
+ target.set(id, sourceSymbol);
+ }
+ });
- function getSymbolLinks(symbol: Symbol): SymbolLinks {
- if (symbol.flags & SymbolFlags.Transient) return symbol as TransientSymbol;
- const id = getSymbolId(symbol);
- return symbolLinks[id] || (symbolLinks[id] = new (SymbolLinks as any)());
+ function addDeclarationDiagnostic(id: string, message: DiagnosticMessage) {
+ return (declaration: Declaration) => diagnostics.add(createDiagnosticForNode(declaration, message, id));
}
+ }
- function getNodeLinks(node: Node): NodeLinks {
- const nodeId = getNodeId(node);
- return nodeLinks[nodeId] || (nodeLinks[nodeId] = new (NodeLinks as any)());
- }
+ function getSymbolLinks(symbol: ts.Symbol): ts.SymbolLinks {
+ if (symbol.flags & SymbolFlags.Transient)
+ return symbol as TransientSymbol;
+ const id = getSymbolId(symbol);
+ return symbolLinks[id] || (symbolLinks[id] = new (SymbolLinks as any)());
+ }
- function isGlobalSourceFile(node: Node) {
- return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node as SourceFile);
- }
+ function getNodeLinks(node: Node): ts.NodeLinks {
+ const nodeId = getNodeId(node);
+ return nodeLinks[nodeId] || (nodeLinks[nodeId] = new (NodeLinks as any)());
+ }
- function getSymbol(symbols: SymbolTable, name: __String, meaning: SymbolFlags): Symbol | undefined {
- if (meaning) {
- const symbol = getMergedSymbol(symbols.get(name));
- if (symbol) {
- Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
- if (symbol.flags & meaning) {
+ function isGlobalSourceFile(node: Node) {
+ return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node as SourceFile);
+ }
+
+ function getSymbol(symbols: SymbolTable, name: __String, meaning: SymbolFlags): ts.Symbol | undefined {
+ if (meaning) {
+ const symbol = getMergedSymbol(symbols.get(name));
+ if (symbol) {
+ Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
+ if (symbol.flags & meaning) {
+ return symbol;
+ }
+ if (symbol.flags & SymbolFlags.Alias) {
+ const target = resolveAlias(symbol);
+ // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors
+ if (target === unknownSymbol || target.flags & meaning) {
return symbol;
}
- if (symbol.flags & SymbolFlags.Alias) {
- const target = resolveAlias(symbol);
- // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors
- if (target === unknownSymbol || target.flags & meaning) {
- return symbol;
- }
- }
}
}
- // return undefined if we can't find a symbol.
}
+ // return undefined if we can't find a symbol.
+ }
- /**
- * Get symbols that represent parameter-property-declaration as parameter and as property declaration
- * @param parameter a parameterDeclaration node
- * @param parameterName a name of the parameter to get the symbols for.
- * @return a tuple of two symbols
- */
- function getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: __String): [Symbol, Symbol] {
- const constructorDeclaration = parameter.parent;
- const classDeclaration = parameter.parent.parent;
-
- const parameterSymbol = getSymbol(constructorDeclaration.locals!, parameterName, SymbolFlags.Value);
- const propertySymbol = getSymbol(getMembersOfSymbol(classDeclaration.symbol), parameterName, SymbolFlags.Value);
+ /**
+ * Get symbols that represent parameter-property-declaration as parameter and as property declaration
+ * @param parameter a parameterDeclaration node
+ * @param parameterName a name of the parameter to get the symbols for.
+ * @return a tuple of two symbols
+ */
+ function getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: __String): [
+ ts.Symbol,
+ ts.Symbol
+ ] {
+ const constructorDeclaration = parameter.parent;
+ const classDeclaration = parameter.parent.parent;
- if (parameterSymbol && propertySymbol) {
- return [parameterSymbol, propertySymbol];
- }
+ const parameterSymbol = getSymbol(constructorDeclaration.locals!, parameterName, SymbolFlags.Value);
+ const propertySymbol = getSymbol(getMembersOfSymbol(classDeclaration.symbol), parameterName, SymbolFlags.Value);
- return Debug.fail("There should exist two symbols, one as property declaration and one as parameter declaration");
+ if (parameterSymbol && propertySymbol) {
+ return [parameterSymbol, propertySymbol];
}
- function isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean {
- const declarationFile = getSourceFileOfNode(declaration);
- const useFile = getSourceFileOfNode(usage);
- const declContainer = getEnclosingBlockScopeContainer(declaration);
- if (declarationFile !== useFile) {
- if ((moduleKind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) ||
- (!outFile(compilerOptions)) ||
- isInTypeQuery(usage) ||
- declaration.flags & NodeFlags.Ambient) {
- // nodes are in different files and order cannot be determined
- return true;
- }
- // declaration is after usage
- // can be legal if usage is deferred (i.e. inside function or in initializer of instance property)
- if (isUsedInFunctionOrInstanceProperty(usage, declaration)) {
- return true;
- }
- const sourceFiles = host.getSourceFiles();
- return sourceFiles.indexOf(declarationFile) <= sourceFiles.indexOf(useFile);
- }
-
- if (declaration.pos <= usage.pos && !(isPropertyDeclaration(declaration) && isThisProperty(usage.parent) && !declaration.initializer && !declaration.exclamationToken)) {
- // declaration is before usage
- if (declaration.kind === SyntaxKind.BindingElement) {
- // still might be illegal if declaration and usage are both binding elements (eg var [a = b, b = b] = [1, 2])
- const errorBindingElement = getAncestor(usage, SyntaxKind.BindingElement) as BindingElement;
- if (errorBindingElement) {
- return findAncestor(errorBindingElement, isBindingElement) !== findAncestor(declaration, isBindingElement) ||
- declaration.pos < errorBindingElement.pos;
- }
- // or it might be illegal if usage happens before parent variable is declared (eg var [a] = a)
- return isBlockScopedNameDeclaredBeforeUse(getAncestor(declaration, SyntaxKind.VariableDeclaration) as Declaration, usage);
- }
- else if (declaration.kind === SyntaxKind.VariableDeclaration) {
- // still might be illegal if usage is in the initializer of the variable declaration (eg var a = a)
- return !isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration as VariableDeclaration, usage);
- }
- else if (isClassDeclaration(declaration)) {
- // still might be illegal if the usage is within a computed property name in the class (eg class A { static p = "a"; [A.p]() {} })
- return !findAncestor(usage, n => isComputedPropertyName(n) && n.parent.parent === declaration);
- }
- else if (isPropertyDeclaration(declaration)) {
- // still might be illegal if a self-referencing property initializer (eg private x = this.x)
- return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, /*stopAtAnyPropertyDeclaration*/ false);
- }
- else if (isParameterPropertyDeclaration(declaration, declaration.parent)) {
- // foo = this.bar is illegal in esnext+useDefineForClassFields when bar is a parameter property
- return !(getEmitScriptTarget(compilerOptions) === ScriptTarget.ESNext && useDefineForClassFields
- && getContainingClass(declaration) === getContainingClass(usage)
- && isUsedInFunctionOrInstanceProperty(usage, declaration));
- }
- return true;
- }
-
+ return Debug.fail("There should exist two symbols, one as property declaration and one as parameter declaration");
+ }
- // declaration is after usage, but it can still be legal if usage is deferred:
- // 1. inside an export specifier
- // 2. inside a function
- // 3. inside an instance property initializer, a reference to a non-instance property
- // (except when target: "esnext" and useDefineForClassFields: true and the reference is to a parameter property)
- // 4. inside a static property initializer, a reference to a static method in the same class
- // 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ)
- // or if usage is in a type context:
- // 1. inside a type query (typeof in type position)
- // 2. inside a jsdoc comment
- if (usage.parent.kind === SyntaxKind.ExportSpecifier || (usage.parent.kind === SyntaxKind.ExportAssignment && (usage.parent as ExportAssignment).isExportEquals)) {
- // export specifiers do not use the variable, they only make it available for use
+ function isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean {
+ const declarationFile = getSourceFileOfNode(declaration);
+ const useFile = getSourceFileOfNode(usage);
+ const declContainer = getEnclosingBlockScopeContainer(declaration);
+ if (declarationFile !== useFile) {
+ if ((moduleKind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) ||
+ (!outFile(compilerOptions)) ||
+ isInTypeQuery(usage) ||
+ declaration.flags & NodeFlags.Ambient) {
+ // nodes are in different files and order cannot be determined
return true;
}
- // When resolving symbols for exports, the `usage` location passed in can be the export site directly
- if (usage.kind === SyntaxKind.ExportAssignment && (usage as ExportAssignment).isExportEquals) {
+ // declaration is after usage
+ // can be legal if usage is deferred (i.e. inside function or in initializer of instance property)
+ if (isUsedInFunctionOrInstanceProperty(usage, declaration)) {
return true;
}
+ const sourceFiles = host.getSourceFiles();
+ return sourceFiles.indexOf(declarationFile) <= sourceFiles.indexOf(useFile);
+ }
- if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || usageInTypeDeclaration()) {
- return true;
- }
- if (isUsedInFunctionOrInstanceProperty(usage, declaration)) {
- if (getEmitScriptTarget(compilerOptions) === ScriptTarget.ESNext && useDefineForClassFields
- && getContainingClass(declaration)
- && (isPropertyDeclaration(declaration) || isParameterPropertyDeclaration(declaration, declaration.parent))) {
- return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, /*stopAtAnyPropertyDeclaration*/ true);
- }
- else {
- return true;
+ if (declaration.pos <= usage.pos && !(isPropertyDeclaration(declaration) && isThisProperty(usage.parent) && !declaration.initializer && !declaration.exclamationToken)) {
+ // declaration is before usage
+ if (declaration.kind === SyntaxKind.BindingElement) {
+ // still might be illegal if declaration and usage are both binding elements (eg var [a = b, b = b] = [1, 2])
+ const errorBindingElement = getAncestor(usage, SyntaxKind.BindingElement) as BindingElement;
+ if (errorBindingElement) {
+ return findAncestor(errorBindingElement, isBindingElement) !== findAncestor(declaration, isBindingElement) ||
+ declaration.pos < errorBindingElement.pos;
}
+ // or it might be illegal if usage happens before parent variable is declared (eg var [a] = a)
+ return isBlockScopedNameDeclaredBeforeUse(getAncestor(declaration, SyntaxKind.VariableDeclaration) as Declaration, usage);
}
- return false;
-
- function usageInTypeDeclaration() {
- return !!findAncestor(usage, node => isInterfaceDeclaration(node) || isTypeAliasDeclaration(node));
+ else if (declaration.kind === SyntaxKind.VariableDeclaration) {
+ // still might be illegal if usage is in the initializer of the variable declaration (eg var a = a)
+ return !isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration as VariableDeclaration, usage);
+ }
+ else if (isClassDeclaration(declaration)) {
+ // still might be illegal if the usage is within a computed property name in the class (eg class A { static p = "a"; [A.p]() {} })
+ return !findAncestor(usage, n => isComputedPropertyName(n) && n.parent.parent === declaration);
}
+ else if (isPropertyDeclaration(declaration)) {
+ // still might be illegal if a self-referencing property initializer (eg private x = this.x)
+ return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, /*stopAtAnyPropertyDeclaration*/ false);
+ }
+ else if (isParameterPropertyDeclaration(declaration, declaration.parent)) {
+ // foo = this.bar is illegal in esnext+useDefineForClassFields when bar is a parameter property
+ return !(getEmitScriptTarget(compilerOptions) === ScriptTarget.ESNext && useDefineForClassFields
+ && getContainingClass(declaration) === getContainingClass(usage)
+ && isUsedInFunctionOrInstanceProperty(usage, declaration));
+ }
+ return true;
+ }
- function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean {
- switch (declaration.parent.parent.kind) {
- case SyntaxKind.VariableStatement:
- case SyntaxKind.ForStatement:
- case SyntaxKind.ForOfStatement:
- // variable statement/for/for-of statement case,
- // use site should not be inside variable declaration (initializer of declaration or binding element)
- if (isSameScopeDescendentOf(usage, declaration, declContainer)) {
- return true;
- }
- break;
- }
- // ForIn/ForOf case - use site should not be used in expression part
- const grandparent = declaration.parent.parent;
- return isForInOrOfStatement(grandparent) && isSameScopeDescendentOf(usage, grandparent.expression, declContainer);
+ // declaration is after usage, but it can still be legal if usage is deferred:
+ // 1. inside an export specifier
+ // 2. inside a function
+ // 3. inside an instance property initializer, a reference to a non-instance property
+ // (except when target: "esnext" and useDefineForClassFields: true and the reference is to a parameter property)
+ // 4. inside a static property initializer, a reference to a static method in the same class
+ // 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ)
+ // or if usage is in a type context:
+ // 1. inside a type query (typeof in type position)
+ // 2. inside a jsdoc comment
+ if (usage.parent.kind === SyntaxKind.ExportSpecifier || (usage.parent.kind === SyntaxKind.ExportAssignment && (usage.parent as ExportAssignment).isExportEquals)) {
+ // export specifiers do not use the variable, they only make it available for use
+ return true;
+ }
+ // When resolving symbols for exports, the `usage` location passed in can be the export site directly
+ if (usage.kind === SyntaxKind.ExportAssignment && (usage as ExportAssignment).isExportEquals) {
+ return true;
+ }
+
+ if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || usageInTypeDeclaration()) {
+ return true;
+ }
+ if (isUsedInFunctionOrInstanceProperty(usage, declaration)) {
+ if (getEmitScriptTarget(compilerOptions) === ScriptTarget.ESNext && useDefineForClassFields
+ && getContainingClass(declaration)
+ && (isPropertyDeclaration(declaration) || isParameterPropertyDeclaration(declaration, declaration.parent))) {
+ return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, /*stopAtAnyPropertyDeclaration*/ true);
}
+ else {
+ return true;
+ }
+ }
+ return false;
- function isUsedInFunctionOrInstanceProperty(usage: Node, declaration: Node): boolean {
- return !!findAncestor(usage, current => {
- if (current === declContainer) {
- return "quit";
- }
- if (isFunctionLike(current)) {
+ function usageInTypeDeclaration() {
+ return !!findAncestor(usage, node => isInterfaceDeclaration(node) || isTypeAliasDeclaration(node));
+ }
+
+ function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean {
+ switch (declaration.parent.parent.kind) {
+ case SyntaxKind.VariableStatement:
+ case SyntaxKind.ForStatement:
+ case SyntaxKind.ForOfStatement:
+ // variable statement/for/for-of statement case,
+ // use site should not be inside variable declaration (initializer of declaration or binding element)
+ if (isSameScopeDescendentOf(usage, declaration, declContainer)) {
return true;
}
- if (isClassStaticBlockDeclaration(current)) {
- return declaration.pos < usage.pos;
- }
+ break;
+ }
- const propertyDeclaration = tryCast(current.parent, isPropertyDeclaration);
- if (propertyDeclaration) {
- const initializerOfProperty = propertyDeclaration.initializer === current;
- if (initializerOfProperty) {
- if (isStatic(current.parent)) {
- if (declaration.kind === SyntaxKind.MethodDeclaration) {
- return true;
- }
- if (isPropertyDeclaration(declaration) && getContainingClass(usage) === getContainingClass(declaration)) {
- const propName = declaration.name;
- if (isIdentifier(propName) || isPrivateIdentifier(propName)) {
- const type = getTypeOfSymbol(getSymbolOfNode(declaration));
- const staticBlocks = filter(declaration.parent.members, isClassStaticBlockDeclaration);
- if (isPropertyInitializedInStaticBlocks(propName, type, staticBlocks, declaration.parent.pos, current.pos)) {
- return true;
- }
+ // ForIn/ForOf case - use site should not be used in expression part
+ const grandparent = declaration.parent.parent;
+ return isForInOrOfStatement(grandparent) && isSameScopeDescendentOf(usage, grandparent.expression, declContainer);
+ }
+
+ function isUsedInFunctionOrInstanceProperty(usage: Node, declaration: Node): boolean {
+ return !!findAncestor(usage, current => {
+ if (current === declContainer) {
+ return "quit";
+ }
+ if (isFunctionLike(current)) {
+ return true;
+ }
+ if (isClassStaticBlockDeclaration(current)) {
+ return declaration.pos < usage.pos;
+ }
+
+ const propertyDeclaration = tryCast(current.parent, isPropertyDeclaration);
+ if (propertyDeclaration) {
+ const initializerOfProperty = propertyDeclaration.initializer === current;
+ if (initializerOfProperty) {
+ if (isStatic(current.parent)) {
+ if (declaration.kind === SyntaxKind.MethodDeclaration) {
+ return true;
+ }
+ if (isPropertyDeclaration(declaration) && getContainingClass(usage) === getContainingClass(declaration)) {
+ const propName = declaration.name;
+ if (isIdentifier(propName) || isPrivateIdentifier(propName)) {
+ const type = getTypeOfSymbol(getSymbolOfNode(declaration));
+ const staticBlocks = filter(declaration.parent.members, isClassStaticBlockDeclaration);
+ if (isPropertyInitializedInStaticBlocks(propName, type, staticBlocks, declaration.parent.pos, current.pos)) {
+ return true;
}
}
}
- else {
- const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !isStatic(declaration);
- if (!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) {
- return true;
- }
+ }
+ else {
+ const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !isStatic(declaration);
+ if (!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) {
+ return true;
}
}
}
- return false;
- });
- }
-
- /** stopAtAnyPropertyDeclaration is used for detecting ES-standard class field use-before-def errors */
- function isPropertyImmediatelyReferencedWithinDeclaration(declaration: PropertyDeclaration | ParameterPropertyDeclaration, usage: Node, stopAtAnyPropertyDeclaration: boolean) {
- // always legal if usage is after declaration
- if (usage.end > declaration.end) {
- return false;
}
-
- // still might be legal if usage is deferred (e.g. x: any = () => this.x)
- // otherwise illegal if immediately referenced within the declaration (e.g. x: any = this.x)
- const ancestorChangingReferenceScope = findAncestor(usage, (node: Node) => {
- if (node === declaration) {
- return "quit";
- }
-
- switch (node.kind) {
- case SyntaxKind.ArrowFunction:
- return true;
- case SyntaxKind.PropertyDeclaration:
- // even when stopping at any property declaration, they need to come from the same class
- return stopAtAnyPropertyDeclaration &&
- (isPropertyDeclaration(declaration) && node.parent === declaration.parent
- || isParameterPropertyDeclaration(declaration, declaration.parent) && node.parent === declaration.parent.parent)
- ? "quit": true;
- case SyntaxKind.Block:
- switch (node.parent.kind) {
- case SyntaxKind.GetAccessor:
- case SyntaxKind.MethodDeclaration:
- case SyntaxKind.SetAccessor:
- return true;
- default:
- return false;
- }
- default:
- return false;
- }
- });
-
- return ancestorChangingReferenceScope === undefined;
- }
+ return false;
+ });
}
- function useOuterVariableScopeInParameter(result: Symbol, location: Node, lastLocation: Node) {
- const target = getEmitScriptTarget(compilerOptions);
- const functionLocation = location as FunctionLikeDeclaration;
- if (isParameter(lastLocation)
- && functionLocation.body
- && result.valueDeclaration
- && result.valueDeclaration.pos >= functionLocation.body.pos
- && result.valueDeclaration.end <= functionLocation.body.end) {
- // check for several cases where we introduce temporaries that require moving the name/initializer of the parameter to the body
- // - static field in a class expression
- // - optional chaining pre-es2020
- // - nullish coalesce pre-es2020
- // - spread assignment in binding pattern pre-es2017
- if (target >= ScriptTarget.ES2015) {
- const links = getNodeLinks(functionLocation);
- if (links.declarationRequiresScopeChange === undefined) {
- links.declarationRequiresScopeChange = forEach(functionLocation.parameters, requiresScopeChange) || false;
- }
- return !links.declarationRequiresScopeChange;
- }
+ /** stopAtAnyPropertyDeclaration is used for detecting ES-standard class field use-before-def errors */
+ function isPropertyImmediatelyReferencedWithinDeclaration(declaration: PropertyDeclaration | ParameterPropertyDeclaration, usage: Node, stopAtAnyPropertyDeclaration: boolean) {
+ // always legal if usage is after declaration
+ if (usage.end > declaration.end) {
+ return false;
}
- return false;
- function requiresScopeChange(node: ParameterDeclaration): boolean {
- return requiresScopeChangeWorker(node.name)
- || !!node.initializer && requiresScopeChangeWorker(node.initializer);
- }
+ // still might be legal if usage is deferred (e.g. x: any = () => this.x)
+ // otherwise illegal if immediately referenced within the declaration (e.g. x: any = this.x)
+ const ancestorChangingReferenceScope = findAncestor(usage, (node: Node) => {
+ if (node === declaration) {
+ return "quit";
+ }
- function requiresScopeChangeWorker(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ArrowFunction:
- case SyntaxKind.FunctionExpression:
- case SyntaxKind.FunctionDeclaration:
- case SyntaxKind.Constructor:
- // do not descend into these
- return false;
- case SyntaxKind.MethodDeclaration:
- case SyntaxKind.GetAccessor:
- case SyntaxKind.SetAccessor:
- case SyntaxKind.PropertyAssignment:
- return requiresScopeChangeWorker((node as MethodDeclaration | AccessorDeclaration | PropertyAssignment).name);
+ return true;
case SyntaxKind.PropertyDeclaration:
- // static properties in classes introduce temporary variables
- if (hasStaticModifier(node)) {
- return target < ScriptTarget.ESNext || !useDefineForClassFields;
+ // even when stopping at any property declaration, they need to come from the same class
+ return stopAtAnyPropertyDeclaration &&
+ (isPropertyDeclaration(declaration) && node.parent === declaration.parent
+ || isParameterPropertyDeclaration(declaration, declaration.parent) && node.parent === declaration.parent.parent)
+ ? "quit": true;
+ case SyntaxKind.Block:
+ switch (node.parent.kind) {
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.SetAccessor:
+ return true;
+ default:
+ return false;
}
- return requiresScopeChangeWorker((node as PropertyDeclaration).name);
default:
- // null coalesce and optional chain pre-es2020 produce temporary variables
- if (isNullishCoalesce(node) || isOptionalChain(node)) {
- return target < ScriptTarget.ES2020;
- }
- if (isBindingElement(node) && node.dotDotDotToken && isObjectBindingPattern(node.parent)) {
- return target < ScriptTarget.ES2017;
- }
- if (isTypeNode(node)) return false;
- return forEachChild(node, requiresScopeChangeWorker) || false;
+ return false;
+ }
+ });
+
+ return ancestorChangingReferenceScope === undefined;
+ }
+ }
+
+ function useOuterVariableScopeInParameter(result: ts.Symbol, location: Node, lastLocation: Node) {
+ const target = getEmitScriptTarget(compilerOptions);
+ const functionLocation = location as FunctionLikeDeclaration;
+ if (isParameter(lastLocation)
+ && functionLocation.body
+ && result.valueDeclaration
+ && result.valueDeclaration.pos >= functionLocation.body.pos
+ && result.valueDeclaration.end <= functionLocation.body.end) {
+ // check for several cases where we introduce temporaries that require moving the name/initializer of the parameter to the body
+ // - static field in a class expression
+ // - optional chaining pre-es2020
+ // - nullish coalesce pre-es2020
+ // - spread assignment in binding pattern pre-es2017
+ if (target >= ScriptTarget.ES2015) {
+ const links = getNodeLinks(functionLocation);
+ if (links.declarationRequiresScopeChange === undefined) {
+ links.declarationRequiresScopeChange = forEach(functionLocation.parameters, requiresScopeChange) || false;
}
+ return !links.declarationRequiresScopeChange;
}
}
+ return false;
- /**
- * Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
- * the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with
- * the given name can be found.
- *
- * @param isUse If true, this will count towards --noUnusedLocals / --noUnusedParameters.
- */
- function resolveName(
- location: Node | undefined,
- name: __String,
- meaning: SymbolFlags,
- nameNotFoundMessage: DiagnosticMessage | undefined,
- nameArg: __String | Identifier | undefined,
- isUse: boolean,
- excludeGlobals = false,
- getSpellingSuggstions = true): Symbol | undefined {
- return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSpellingSuggstions, getSymbol);
- }
-
- function resolveNameHelper(
- location: Node | undefined,
- name: __String,
- meaning: SymbolFlags,
- nameNotFoundMessage: DiagnosticMessage | undefined,
- nameArg: __String | Identifier | undefined,
- isUse: boolean,
- excludeGlobals: boolean,
- getSpellingSuggestions: boolean,
- lookup: typeof getSymbol): Symbol | undefined {
- const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location
- let result: Symbol | undefined;
- let lastLocation: Node | undefined;
- let lastSelfReferenceLocation: Node | undefined;
- let propertyWithInvalidInitializer: Node | undefined;
- let associatedDeclarationForContainingInitializerOrBindingName: ParameterDeclaration | BindingElement | undefined;
- let withinDeferredContext = false;
- const errorLocation = location;
- let grandparent: Node;
- let isInExternalModule = false;
-
- loop: while (location) {
- // Locals of a source file are not in scope (because they get merged into the global symbol table)
- if (location.locals && !isGlobalSourceFile(location)) {
- if (result = lookup(location.locals, name, meaning)) {
- let useResult = true;
- if (isFunctionLike(location) && lastLocation && lastLocation !== (location as FunctionLikeDeclaration).body) {
- // symbol lookup restrictions for function-like declarations
- // - Type parameters of a function are in scope in the entire function declaration, including the parameter
- // list and return type. However, local types are only in scope in the function body.
- // - parameters are only in the scope of function body
- // This restriction does not apply to JSDoc comment types because they are parented
- // at a higher level than type parameters would normally be
- if (meaning & result.flags & SymbolFlags.Type && lastLocation.kind !== SyntaxKind.JSDocComment) {
- useResult = result.flags & SymbolFlags.TypeParameter
- // type parameters are visible in parameter list, return type and type parameter list
- ? lastLocation === (location as FunctionLikeDeclaration).type ||
+ function requiresScopeChange(node: ParameterDeclaration): boolean {
+ return requiresScopeChangeWorker(node.name)
+ || !!node.initializer && requiresScopeChangeWorker(node.initializer);
+ }
+
+ function requiresScopeChangeWorker(node: Node): boolean {
+ switch (node.kind) {
+ case SyntaxKind.ArrowFunction:
+ case SyntaxKind.FunctionExpression:
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.Constructor:
+ // do not descend into these
+ return false;
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ case SyntaxKind.PropertyAssignment:
+ return requiresScopeChangeWorker((node as MethodDeclaration | AccessorDeclaration | PropertyAssignment).name);
+ case SyntaxKind.PropertyDeclaration:
+ // static properties in classes introduce temporary variables
+ if (hasStaticModifier(node)) {
+ return target < ScriptTarget.ESNext || !useDefineForClassFields;
+ }
+ return requiresScopeChangeWorker((node as PropertyDeclaration).name);
+ default:
+ // null coalesce and optional chain pre-es2020 produce temporary variables
+ if (isNullishCoalesce(node) || isOptionalChain(node)) {
+ return target < ScriptTarget.ES2020;
+ }
+ if (isBindingElement(node) && node.dotDotDotToken && isObjectBindingPattern(node.parent)) {
+ return target < ScriptTarget.ES2017;
+ }
+ if (isTypeNode(node))
+ return false;
+ return forEachChild(node, requiresScopeChangeWorker) || false;
+ }
+ }
+ }
+
+ /**
+ * Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
+ * the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with
+ * the given name can be found.
+ *
+ * @param isUse If true, this will count towards --noUnusedLocals / --noUnusedParameters.
+ */
+ function resolveName(location: Node | undefined, name: __String, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage | undefined, nameArg: __String | Identifier | undefined, isUse: boolean, excludeGlobals = false, getSpellingSuggstions = true): ts.Symbol | undefined {
+ return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSpellingSuggstions, getSymbol);
+ }
+
+ function resolveNameHelper(location: Node | undefined, name: __String, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage | undefined, nameArg: __String | Identifier | undefined, isUse: boolean, excludeGlobals: boolean, getSpellingSuggestions: boolean, lookup: typeof getSymbol): ts.Symbol | undefined {
+ const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location
+ let result: ts.Symbol | undefined;
+ let lastLocation: Node | undefined;
+ let lastSelfReferenceLocation: Node | undefined;
+ let propertyWithInvalidInitializer: Node | undefined;
+ let associatedDeclarationForContainingInitializerOrBindingName: ParameterDeclaration | BindingElement | undefined;
+ let withinDeferredContext = false;
+ const errorLocation = location;
+ let grandparent: Node;
+ let isInExternalModule = false;
+
+ loop: while (location) {
+ // Locals of a source file are not in scope (because they get merged into the global symbol table)
+ if (location.locals && !isGlobalSourceFile(location)) {
+ if (result = lookup(location.locals, name, meaning)) {
+ let useResult = true;
+ if (isFunctionLike(location) && lastLocation && lastLocation !== (location as FunctionLikeDeclaration).body) {
+ // symbol lookup restrictions for function-like declarations
+ // - Type parameters of a function are in scope in the entire function declaration, including the parameter
+ // list and return type. However, local types are only in scope in the function body.
+ // - parameters are only in the scope of function body
+ // This restriction does not apply to JSDoc comment types because they are parented
+ // at a higher level than type parameters would normally be
+ if (meaning & result.flags & SymbolFlags.Type && lastLocation.kind !== SyntaxKind.JSDocComment) {
+ useResult = result.flags & SymbolFlags.TypeParameter
+ // type parameters are visible in parameter list, return type and type parameter list
+ ? lastLocation === (location as FunctionLikeDeclaration).type ||
+ lastLocation.kind === SyntaxKind.Parameter ||
+ lastLocation.kind === SyntaxKind.TypeParameter
+ // local types not visible outside the function body
+ : false;
+ }
+ if (meaning & result.flags & SymbolFlags.Variable) {
+ // expression inside parameter will lookup as normal variable scope when targeting es2015+
+ if (useOuterVariableScopeInParameter(result, location, lastLocation)) {
+ useResult = false;
+ }
+ else if (result.flags & SymbolFlags.FunctionScopedVariable) {
+ // parameters are visible only inside function body, parameter list and return type
+ // technically for parameter list case here we might mix parameters and variables declared in function,
+ // however it is detected separately when checking initializers of parameters
+ // to make sure that they reference no variables declared after them.
+ useResult =
lastLocation.kind === SyntaxKind.Parameter ||
- lastLocation.kind === SyntaxKind.TypeParameter
- // local types not visible outside the function body
- : false;
+ (lastLocation === (location as FunctionLikeDeclaration).type &&
+ !!findAncestor(result.valueDeclaration, isParameter));
}
- if (meaning & result.flags & SymbolFlags.Variable) {
- // expression inside parameter will lookup as normal variable scope when targeting es2015+
- if (useOuterVariableScopeInParameter(result, location, lastLocation)) {
- useResult = false;
- }
- else if (result.flags & SymbolFlags.FunctionScopedVariable) {
- // parameters are visible only inside function body, parameter list and return type
- // technically for parameter list case here we might mix parameters and variables declared in function,
- // however it is detected separately when checking initializers of parameters
- // to make sure that they reference no variables declared after them.
- useResult =
- lastLocation.kind === SyntaxKind.Parameter ||
- (
- lastLocation === (location as FunctionLikeDeclaration).type &&
- !!findAncestor(result.valueDeclaration, isParameter)
- );
- }
+ }
+ }
+ else if (location.kind === SyntaxKind.ConditionalType) {
+ // A type parameter declared using 'infer T' in a conditional type is visible only in
+ // the true branch of the conditional type.
+ useResult = lastLocation === (location as ConditionalTypeNode).trueType;
+ }
+
+ if (useResult) {
+ break loop;
+ }
+ else {
+ result = undefined;
+ }
+ }
+ }
+ withinDeferredContext = withinDeferredContext || getIsDeferredContext(location, lastLocation);
+ switch (location.kind) {
+ case SyntaxKind.SourceFile:
+ if (!isExternalOrCommonJsModule(location as SourceFile))
+ break;
+ isInExternalModule = true;
+ // falls through
+ case SyntaxKind.ModuleDeclaration:
+ const moduleExports = getSymbolOfNode(location as SourceFile | ModuleDeclaration)?.exports || emptySymbols;
+ if (location.kind === SyntaxKind.SourceFile || (isModuleDeclaration(location) && location.flags & NodeFlags.Ambient && !isGlobalScopeAugmentation(location))) {
+
+ // It's an external module. First see if the module has an export default and if the local
+ // name of that export default matches.
+ if (result = moduleExports.get(InternalSymbolName.Default)) {
+ const localSymbol = getLocalSymbolForExportDefault(result);
+ if (localSymbol && (result.flags & meaning) && localSymbol.escapedName === name) {
+ break loop;
}
+ result = undefined;
}
- else if (location.kind === SyntaxKind.ConditionalType) {
- // A type parameter declared using 'infer T' in a conditional type is visible only in
- // the true branch of the conditional type.
- useResult = lastLocation === (location as ConditionalTypeNode).trueType;
+
+ // Because of module/namespace merging, a module's exports are in scope,
+ // yet we never want to treat an export specifier as putting a member in scope.
+ // Therefore, if the name we find is purely an export specifier, it is not actually considered in scope.
+ // Two things to note about this:
+ // 1. We have to check this without calling getSymbol. The problem with calling getSymbol
+ // on an export specifier is that it might find the export specifier itself, and try to
+ // resolve it as an alias. This will cause the checker to consider the export specifier
+ // a circular alias reference when it might not be.
+ // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely*
+ // an alias. If we used &, we'd be throwing out symbols that have non alias aspects,
+ // which is not the desired behavior.
+ const moduleExport = moduleExports.get(name);
+ if (moduleExport &&
+ moduleExport.flags === SymbolFlags.Alias &&
+ (getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier) || getDeclarationOfKind(moduleExport, SyntaxKind.NamespaceExport))) {
+ break;
}
+ }
- if (useResult) {
- break loop;
+ // ES6 exports are also visible locally (except for 'default'), but commonjs exports are not (except typedefs)
+ if (name !== InternalSymbolName.Default && (result = lookup(moduleExports, name, meaning & SymbolFlags.ModuleMember))) {
+ if (isSourceFile(location) && location.commonJsModuleIndicator && !result.declarations?.some(isJSDocTypeAlias)) {
+ result = undefined;
}
else {
- result = undefined;
+ break loop;
}
}
- }
- withinDeferredContext = withinDeferredContext || getIsDeferredContext(location, lastLocation);
- switch (location.kind) {
- case SyntaxKind.SourceFile:
- if (!isExternalOrCommonJsModule(location as SourceFile)) break;
- isInExternalModule = true;
- // falls through
- case SyntaxKind.ModuleDeclaration:
- const moduleExports = getSymbolOfNode(location as SourceFile | ModuleDeclaration)?.exports || emptySymbols;
- if (location.kind === SyntaxKind.SourceFile || (isModuleDeclaration(location) && location.flags & NodeFlags.Ambient && !isGlobalScopeAugmentation(location))) {
-
- // It's an external module. First see if the module has an export default and if the local
- // name of that export default matches.
- if (result = moduleExports.get(InternalSymbolName.Default)) {
- const localSymbol = getLocalSymbolForExportDefault(result);
- if (localSymbol && (result.flags & meaning) && localSymbol.escapedName === name) {
- break loop;
- }
- result = undefined;
- }
-
- // Because of module/namespace merging, a module's exports are in scope,
- // yet we never want to treat an export specifier as putting a member in scope.
- // Therefore, if the name we find is purely an export specifier, it is not actually considered in scope.
- // Two things to note about this:
- // 1. We have to check this without calling getSymbol. The problem with calling getSymbol
- // on an export specifier is that it might find the export specifier itself, and try to
- // resolve it as an alias. This will cause the checker to consider the export specifier
- // a circular alias reference when it might not be.
- // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely*
- // an alias. If we used &, we'd be throwing out symbols that have non alias aspects,
- // which is not the desired behavior.
- const moduleExport = moduleExports.get(name);
- if (moduleExport &&
- moduleExport.flags === SymbolFlags.Alias &&
- (getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier) || getDeclarationOfKind(moduleExport, SyntaxKind.NamespaceExport))) {
- break;
- }
- }
-
- // ES6 exports are also visible locally (except for 'default'), but commonjs exports are not (except typedefs)
- if (name !== InternalSymbolName.Default && (result = lookup(moduleExports, name, meaning & SymbolFlags.ModuleMember))) {
- if (isSourceFile(location) && location.commonJsModuleIndicator && !result.declarations?.some(isJSDocTypeAlias)) {
- result = undefined;
- }
- else {
- break loop;
+ break;
+ case SyntaxKind.EnumDeclaration:
+ if (result = lookup(getSymbolOfNode(location)?.exports || emptySymbols, name, meaning & SymbolFlags.EnumMember)) {
+ break loop;
+ }
+ break;
+ case SyntaxKind.PropertyDeclaration:
+ // TypeScript 1.0 spec (April 2014): 8.4.1
+ // Initializer expressions for instance member variables are evaluated in the scope
+ // of the class constructor body but are not permitted to reference parameters or
+ // local variables of the constructor. This effectively means that entities from outer scopes
+ // by the same name as a constructor parameter or local variable are inaccessible
+ // in initializer expressions for instance member variables.
+ if (!isStatic(location)) {
+ const ctor = findConstructorDeclaration(location.parent as ClassLikeDeclaration);
+ if (ctor && ctor.locals) {
+ if (lookup(ctor.locals, name, meaning & SymbolFlags.Value)) {
+ // Remember the property node, it will be used later to report appropriate error
+ propertyWithInvalidInitializer = location;
}
}
- break;
- case SyntaxKind.EnumDeclaration:
- if (result = lookup(getSymbolOfNode(location)?.exports || emptySymbols, name, meaning & SymbolFlags.EnumMember)) {
- break loop;
+ }
+ break;
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.ClassExpression:
+ case SyntaxKind.InterfaceDeclaration:
+ // The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals
+ // These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would
+ // trigger resolving late-bound names, which we may already be in the process of doing while we're here!
+ if (result = lookup(getSymbolOfNode(location as ClassLikeDeclaration | InterfaceDeclaration).members || emptySymbols, name, meaning & SymbolFlags.Type)) {
+ if (!isTypeParameterSymbolDeclaredInContainer(result, location)) {
+ // ignore type parameters not declared in this container
+ result = undefined;
+ break;
}
- break;
- case SyntaxKind.PropertyDeclaration:
- // TypeScript 1.0 spec (April 2014): 8.4.1
- // Initializer expressions for instance member variables are evaluated in the scope
- // of the class constructor body but are not permitted to reference parameters or
- // local variables of the constructor. This effectively means that entities from outer scopes
- // by the same name as a constructor parameter or local variable are inaccessible
- // in initializer expressions for instance member variables.
- if (!isStatic(location)) {
- const ctor = findConstructorDeclaration(location.parent as ClassLikeDeclaration);
- if (ctor && ctor.locals) {
- if (lookup(ctor.locals, name, meaning & SymbolFlags.Value)) {
- // Remember the property node, it will be used later to report appropriate error
- propertyWithInvalidInitializer = location;
- }
- }
+ if (lastLocation && isStatic(lastLocation)) {
+ // TypeScript 1.0 spec (April 2014): 3.4.1
+ // The scope of a type parameter extends over the entire declaration with which the type
+ // parameter list is associated, with the exception of static member declarations in classes.
+ error(errorLocation, Diagnostics.Static_members_cannot_reference_class_type_parameters);
+ return undefined;
}
- break;
- case SyntaxKind.ClassDeclaration:
- case SyntaxKind.ClassExpression:
- case SyntaxKind.InterfaceDeclaration:
- // The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals
- // These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would
- // trigger resolving late-bound names, which we may already be in the process of doing while we're here!
- if (result = lookup(getSymbolOfNode(location as ClassLikeDeclaration | InterfaceDeclaration).members || emptySymbols, name, meaning & SymbolFlags.Type)) {
- if (!isTypeParameterSymbolDeclaredInContainer(result, location)) {
- // ignore type parameters not declared in this container
- result = undefined;
- break;
- }
- if (lastLocation && isStatic(lastLocation)) {
- // TypeScript 1.0 spec (April 2014): 3.4.1
- // The scope of a type parameter extends over the entire declaration with which the type
- // parameter list is associated, with the exception of static member declarations in classes.
- error(errorLocation, Diagnostics.Static_members_cannot_reference_class_type_parameters);
- return undefined;
- }
+ break loop;
+ }
+ if (location.kind === SyntaxKind.ClassExpression && meaning & SymbolFlags.Class) {
+ const className = (location as ClassExpression).name;
+ if (className && name === className.escapedText) {
+ result = location.symbol;
break loop;
}
- if (location.kind === SyntaxKind.ClassExpression && meaning & SymbolFlags.Class) {
- const className = (location as ClassExpression).name;
- if (className && name === className.escapedText) {
- result = location.symbol;
- break loop;
+ }
+ break;
+ case SyntaxKind.ExpressionWithTypeArguments:
+ // The type parameters of a class are not in scope in the base class expression.
+ if (lastLocation === (location as ExpressionWithTypeArguments).expression && (location.parent as HeritageClause).token === SyntaxKind.ExtendsKeyword) {
+ const container = location.parent.parent;
+ if (isClassLike(container) && (result = lookup(getSymbolOfNode(container).members!, name, meaning & SymbolFlags.Type))) {
+ if (nameNotFoundMessage) {
+ error(errorLocation, Diagnostics.Base_class_expressions_cannot_reference_class_type_parameters);
}
+ return undefined;
}
- break;
- case SyntaxKind.ExpressionWithTypeArguments:
- // The type parameters of a class are not in scope in the base class expression.
- if (lastLocation === (location as ExpressionWithTypeArguments).expression && (location.parent as HeritageClause).token === SyntaxKind.ExtendsKeyword) {
- const container = location.parent.parent;
- if (isClassLike(container) && (result = lookup(getSymbolOfNode(container).members!, name, meaning & SymbolFlags.Type))) {
- if (nameNotFoundMessage) {
- error(errorLocation, Diagnostics.Base_class_expressions_cannot_reference_class_type_parameters);
- }
- return undefined;
- }
+ }
+ break;
+ // It is not legal to reference a class's own type parameters from a computed property name that
+ // belongs to the class. For example:
+ //
+ // function foo() { return '' }
+ // class C { // <-- Class's own type parameter T
+ // [foo()]() { } // <-- Reference to T from class's own computed property
+ // }
+ //
+ case SyntaxKind.ComputedPropertyName:
+ grandparent = location.parent.parent;
+ if (isClassLike(grandparent) || grandparent.kind === SyntaxKind.InterfaceDeclaration) {
+ // A reference to this grandparent's type parameters would be an error
+ if (result = lookup(getSymbolOfNode(grandparent as ClassLikeDeclaration | InterfaceDeclaration).members!, name, meaning & SymbolFlags.Type)) {
+ error(errorLocation, Diagnostics.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type);
+ return undefined;
}
+ }
+ break;
+ case SyntaxKind.ArrowFunction:
+ // when targeting ES6 or higher there is no 'arguments' in an arrow function
+ // for lower compile targets the resolved symbol is used to emit an error
+ if (getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015) {
break;
- // It is not legal to reference a class's own type parameters from a computed property name that
- // belongs to the class. For example:
+ }
+ // falls through
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.Constructor:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ case SyntaxKind.FunctionDeclaration:
+ if (meaning & SymbolFlags.Variable && name === "arguments") {
+ result = argumentsSymbol;
+ break loop;
+ }
+ break;
+ case SyntaxKind.FunctionExpression:
+ if (meaning & SymbolFlags.Variable && name === "arguments") {
+ result = argumentsSymbol;
+ break loop;
+ }
+
+ if (meaning & SymbolFlags.Function) {
+ const functionName = (location as FunctionExpression).name;
+ if (functionName && name === functionName.escapedText) {
+ result = location.symbol;
+ break loop;
+ }
+ }
+ break;
+ case SyntaxKind.Decorator:
+ // Decorators are resolved at the class declaration. Resolving at the parameter
+ // or member would result in looking up locals in the method.
//
- // function foo() { return '' }
- // class C { // <-- Class's own type parameter T
- // [foo()]() { } // <-- Reference to T from class's own computed property
+ // function y() {}
+ // class C {
+ // method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter.
+ // }
+ //
+ if (location.parent && location.parent.kind === SyntaxKind.Parameter) {
+ location = location.parent;
+ }
+ //
+ // function y() {}
+ // class C {
+ // @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method.
// }
//
- case SyntaxKind.ComputedPropertyName:
- grandparent = location.parent.parent;
- if (isClassLike(grandparent) || grandparent.kind === SyntaxKind.InterfaceDeclaration) {
- // A reference to this grandparent's type parameters would be an error
- if (result = lookup(getSymbolOfNode(grandparent as ClassLikeDeclaration | InterfaceDeclaration).members!, name, meaning & SymbolFlags.Type)) {
- error(errorLocation, Diagnostics.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type);
- return undefined;
- }
- }
- break;
- case SyntaxKind.ArrowFunction:
- // when targeting ES6 or higher there is no 'arguments' in an arrow function
- // for lower compile targets the resolved symbol is used to emit an error
- if (getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015) {
- break;
- }
- // falls through
- case SyntaxKind.MethodDeclaration:
- case SyntaxKind.Constructor:
- case SyntaxKind.GetAccessor:
- case SyntaxKind.SetAccessor:
- case SyntaxKind.FunctionDeclaration:
- if (meaning & SymbolFlags.Variable && name === "arguments") {
- result = argumentsSymbol;
- break loop;
- }
- break;
- case SyntaxKind.FunctionExpression:
- if (meaning & SymbolFlags.Variable && name === "arguments") {
- result = argumentsSymbol;
- break loop;
- }
-
- if (meaning & SymbolFlags.Function) {
- const functionName = (location as FunctionExpression).name;
- if (functionName && name === functionName.escapedText) {
- result = location.symbol;
- break loop;
- }
- }
- break;
- case SyntaxKind.Decorator:
- // Decorators are resolved at the class declaration. Resolving at the parameter
- // or member would result in looking up locals in the method.
- //
- // function y() {}
- // class C {
- // method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter.
- // }
- //
- if (location.parent && location.parent.kind === SyntaxKind.Parameter) {
- location = location.parent;
- }
- //
- // function y() {}
- // class C {
- // @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method.
- // }
- //
- // class Decorators are resolved outside of the class to avoid referencing type parameters of that class.
- //
- // type T = number;
- // declare function y(x: T): any;
- // @param(1 as T) // <-- T should resolve to the type alias outside of class C
- // class C {}
- if (location.parent && (isClassElement(location.parent) || location.parent.kind === SyntaxKind.ClassDeclaration)) {
- location = location.parent;
- }
- break;
- case SyntaxKind.JSDocTypedefTag:
- case SyntaxKind.JSDocCallbackTag:
- case SyntaxKind.JSDocEnumTag:
- // js type aliases do not resolve names from their host, so skip past it
- const root = getJSDocRoot(location);
- if (root) {
- location = root.parent;
- }
- break;
- case SyntaxKind.Parameter:
- if (lastLocation && (
- lastLocation === (location as ParameterDeclaration).initializer ||
- lastLocation === (location as ParameterDeclaration).name && isBindingPattern(lastLocation))) {
- if (!associatedDeclarationForContainingInitializerOrBindingName) {
- associatedDeclarationForContainingInitializerOrBindingName = location as ParameterDeclaration;
- }
+ // class Decorators are resolved outside of the class to avoid referencing type parameters of that class.
+ //
+ // type T = number;
+ // declare function y(x: T): any;
+ // @param(1 as T) // <-- T should resolve to the type alias outside of class C
+ // class C {}
+ if (location.parent && (isClassElement(location.parent) || location.parent.kind === SyntaxKind.ClassDeclaration)) {
+ location = location.parent;
+ }
+ break;
+ case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocCallbackTag:
+ case SyntaxKind.JSDocEnumTag:
+ // js type aliases do not resolve names from their host, so skip past it
+ const root = getJSDocRoot(location);
+ if (root) {
+ location = root.parent;
+ }
+ break;
+ case SyntaxKind.Parameter:
+ if (lastLocation && (lastLocation === (location as ParameterDeclaration).initializer ||
+ lastLocation === (location as ParameterDeclaration).name && isBindingPattern(lastLocation))) {
+ if (!associatedDeclarationForContainingInitializerOrBindingName) {
+ associatedDeclarationForContainingInitializerOrBindingName = location as ParameterDeclaration;
}
- break;
- case SyntaxKind.BindingElement:
- if (lastLocation && (
- lastLocation === (location as BindingElement).initializer ||
- lastLocation === (location as BindingElement).name && isBindingPattern(lastLocation))) {
- if (isParameterDeclaration(location as BindingElement) && !associatedDeclarationForContainingInitializerOrBindingName) {
- associatedDeclarationForContainingInitializerOrBindingName = location as BindingElement;
- }
+ }
+ break;
+ case SyntaxKind.BindingElement:
+ if (lastLocation && (lastLocation === (location as BindingElement).initializer ||
+ lastLocation === (location as BindingElement).name && isBindingPattern(lastLocation))) {
+ if (isParameterDeclaration(location as BindingElement) && !associatedDeclarationForContainingInitializerOrBindingName) {
+ associatedDeclarationForContainingInitializerOrBindingName = location as BindingElement;
}
- break;
- case SyntaxKind.InferType:
- if (meaning & SymbolFlags.TypeParameter) {
- const parameterName = (location as InferTypeNode).typeParameter.name;
- if (parameterName && name === parameterName.escapedText) {
- result = (location as InferTypeNode).typeParameter.symbol;
- break loop;
- }
+ }
+ break;
+ case SyntaxKind.InferType:
+ if (meaning & SymbolFlags.TypeParameter) {
+ const parameterName = (location as InferTypeNode).typeParameter.name;
+ if (parameterName && name === parameterName.escapedText) {
+ result = (location as InferTypeNode).typeParameter.symbol;
+ break loop;
}
- break;
- }
- if (isSelfReferenceLocation(location)) {
- lastSelfReferenceLocation = location;
- }
- lastLocation = location;
- location = isJSDocTemplateTag(location) ?
- getEffectiveContainerForJSDocTemplateTag(location) || location.parent :
- location.parent;
+ }
+ break;
}
-
- // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`.
- // If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself.
- // That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used.
- if (isUse && result && (!lastSelfReferenceLocation || result !== lastSelfReferenceLocation.symbol)) {
- result.isReferenced! |= meaning;
+ if (isSelfReferenceLocation(location)) {
+ lastSelfReferenceLocation = location;
}
+ lastLocation = location;
+ location = isJSDocTemplateTag(location) ?
+ getEffectiveContainerForJSDocTemplateTag(location) || location.parent :
+ location.parent;
+ }
- if (!result) {
- if (lastLocation) {
- Debug.assert(lastLocation.kind === SyntaxKind.SourceFile);
- if ((lastLocation as SourceFile).commonJsModuleIndicator && name === "exports" && meaning & lastLocation.symbol.flags) {
- return lastLocation.symbol;
- }
- }
+ // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`.
+ // If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself.
+ // That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used.
+ if (isUse && result && (!lastSelfReferenceLocation || result !== lastSelfReferenceLocation.symbol)) {
+ result.isReferenced! |= meaning;
+ }
- if (!excludeGlobals) {
- result = lookup(globals, name, meaning);
+ if (!result) {
+ if (lastLocation) {
+ Debug.assert(lastLocation.kind === SyntaxKind.SourceFile);
+ if ((lastLocation as SourceFile).commonJsModuleIndicator && name === "exports" && meaning & lastLocation.symbol.flags) {
+ return lastLocation.symbol;
}
}
- if (!result) {
- if (originalLocation && isInJSFile(originalLocation) && originalLocation.parent) {
- if (isRequireCall(originalLocation.parent, /*checkArgumentIsStringLiteralLike*/ false)) {
- return requireSymbol;
- }
+
+ if (!excludeGlobals) {
+ result = lookup(globals, name, meaning);
+ }
+ }
+ if (!result) {
+ if (originalLocation && isInJSFile(originalLocation) && originalLocation.parent) {
+ if (isRequireCall(originalLocation.parent, /*checkArgumentIsStringLiteralLike*/ false)) {
+ return requireSymbol;
}
}
- if (!result) {
- if (nameNotFoundMessage && produceDiagnostics) {
- if (!errorLocation ||
- !checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg!) && // TODO: GH#18217
- !checkAndReportErrorForExtendingInterface(errorLocation) &&
- !checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) &&
- !checkAndReportErrorForExportingPrimitiveType(errorLocation, name) &&
- !checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) &&
- !checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning) &&
- !checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning)) {
- let suggestion: Symbol | undefined;
- if (getSpellingSuggestions && suggestionCount < maximumSuggestionCount) {
- suggestion = getSuggestedSymbolForNonexistentSymbol(originalLocation, name, meaning);
- const isGlobalScopeAugmentationDeclaration = suggestion?.valueDeclaration && isAmbientModule(suggestion.valueDeclaration) && isGlobalScopeAugmentation(suggestion.valueDeclaration);
- if (isGlobalScopeAugmentationDeclaration) {
- suggestion = undefined;
- }
- if (suggestion) {
- const suggestionName = symbolToString(suggestion);
- const isUncheckedJS = isUncheckedJSSuggestion(originalLocation, suggestion, /*excludeClasses*/ false);
- const message = meaning === SymbolFlags.Namespace || nameArg && typeof nameArg !== "string" && nodeIsSynthesized(nameArg) ? Diagnostics.Cannot_find_namespace_0_Did_you_mean_1
- : isUncheckedJS ? Diagnostics.Could_not_find_name_0_Did_you_mean_1
- : Diagnostics.Cannot_find_name_0_Did_you_mean_1;
- const diagnostic = createError(errorLocation, message, diagnosticName(nameArg!), suggestionName);
- addErrorOrSuggestion(!isUncheckedJS, diagnostic);
- if (suggestion.valueDeclaration) {
- addRelatedInfo(
- diagnostic,
- createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestionName)
- );
- }
+ }
+ if (!result) {
+ if (nameNotFoundMessage && produceDiagnostics) {
+ if (!errorLocation ||
+ !checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg!) && // TODO: GH#18217
+ !checkAndReportErrorForExtendingInterface(errorLocation) &&
+ !checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) &&
+ !checkAndReportErrorForExportingPrimitiveType(errorLocation, name) &&
+ !checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) &&
+ !checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning) &&
+ !checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning)) {
+ let suggestion: ts.Symbol | undefined;
+ if (getSpellingSuggestions && suggestionCount < maximumSuggestionCount) {
+ suggestion = getSuggestedSymbolForNonexistentSymbol(originalLocation, name, meaning);
+ const isGlobalScopeAugmentationDeclaration = suggestion?.valueDeclaration && isAmbientModule(suggestion.valueDeclaration) && isGlobalScopeAugmentation(suggestion.valueDeclaration);
+ if (isGlobalScopeAugmentationDeclaration) {
+ suggestion = undefined;
+ }
+ if (suggestion) {
+ const suggestionName = symbolToString(suggestion);
+ const isUncheckedJS = isUncheckedJSSuggestion(originalLocation, suggestion, /*excludeClasses*/ false);
+ const message = meaning === SymbolFlags.Namespace || nameArg && typeof nameArg !== "string" && nodeIsSynthesized(nameArg) ? Diagnostics.Cannot_find_namespace_0_Did_you_mean_1
+ : isUncheckedJS ? Diagnostics.Could_not_find_name_0_Did_you_mean_1
+ : Diagnostics.Cannot_find_name_0_Did_you_mean_1;
+ const diagnostic = createError(errorLocation, message, diagnosticName(nameArg!), suggestionName);
+ addErrorOrSuggestion(!isUncheckedJS, diagnostic);
+ if (suggestion.valueDeclaration) {
+ addRelatedInfo(diagnostic, createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestionName));
}
}
- if (!suggestion) {
- if (nameArg) {
- const lib = getSuggestedLibForNonExistentName(nameArg);
- if (lib) {
- error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg), lib);
- }
- else {
- error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg));
- }
+ }
+ if (!suggestion) {
+ if (nameArg) {
+ const lib = getSuggestedLibForNonExistentName(nameArg);
+ if (lib) {
+ error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg), lib);
+ }
+ else {
+ error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg));
}
}
- suggestionCount++;
}
+ suggestionCount++;
}
- return undefined;
}
+ return undefined;
+ }
- // Perform extra checks only if error reporting was requested
- if (nameNotFoundMessage && produceDiagnostics) {
- if (propertyWithInvalidInitializer && !(getEmitScriptTarget(compilerOptions) === ScriptTarget.ESNext && useDefineForClassFields)) {
- // We have a match, but the reference occurred within a property initializer and the identifier also binds
- // to a local variable in the constructor where the code will be emitted. Note that this is actually allowed
- // with ESNext+useDefineForClassFields because the scope semantics are different.
- const propertyName = (propertyWithInvalidInitializer as PropertyDeclaration).name;
- error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor,
- declarationNameToString(propertyName), diagnosticName(nameArg!));
- return undefined;
- }
+ // Perform extra checks only if error reporting was requested
+ if (nameNotFoundMessage && produceDiagnostics) {
+ if (propertyWithInvalidInitializer && !(getEmitScriptTarget(compilerOptions) === ScriptTarget.ESNext && useDefineForClassFields)) {
+ // We have a match, but the reference occurred within a property initializer and the identifier also binds
+ // to a local variable in the constructor where the code will be emitted. Note that this is actually allowed
+ // with ESNext+useDefineForClassFields because the scope semantics are different.
+ const propertyName = (propertyWithInvalidInitializer as PropertyDeclaration).name;
+ error(errorLocation, Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor, declarationNameToString(propertyName), diagnosticName(nameArg!));
+ return undefined;
+ }
- // Only check for block-scoped variable if we have an error location and are looking for the
- // name with variable meaning
- // For example,
- // declare module foo {
- // interface bar {}
- // }
- // const foo/*1*/: foo/*2*/.bar;
- // The foo at /*1*/ and /*2*/ will share same symbol with two meanings:
- // block-scoped variable and namespace module. However, only when we
- // try to resolve name in /*1*/ which is used in variable position,
- // we want to check for block-scoped
- if (errorLocation &&
- (meaning & SymbolFlags.BlockScopedVariable ||
- ((meaning & SymbolFlags.Class || meaning & SymbolFlags.Enum) && (meaning & SymbolFlags.Value) === SymbolFlags.Value))) {
- const exportOrLocalSymbol = getExportSymbolOfValueSymbolIfExported(result);
- if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable || exportOrLocalSymbol.flags & SymbolFlags.Class || exportOrLocalSymbol.flags & SymbolFlags.Enum) {
- checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation);
- }
+ // Only check for block-scoped variable if we have an error location and are looking for the
+ // name with variable meaning
+ // For example,
+ // declare module foo {
+ // interface bar {}
+ // }
+ // const foo/*1*/: foo/*2*/.bar;
+ // The foo at /*1*/ and /*2*/ will share same symbol with two meanings:
+ // block-scoped variable and namespace module. However, only when we
+ // try to resolve name in /*1*/ which is used in variable position,
+ // we want to check for block-scoped
+ if (errorLocation &&
+ (meaning & SymbolFlags.BlockScopedVariable ||
+ ((meaning & SymbolFlags.Class || meaning & SymbolFlags.Enum) && (meaning & SymbolFlags.Value) === SymbolFlags.Value))) {
+ const exportOrLocalSymbol = getExportSymbolOfValueSymbolIfExported(result);
+ if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable || exportOrLocalSymbol.flags & SymbolFlags.Class || exportOrLocalSymbol.flags & SymbolFlags.Enum) {
+ checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation);
}
+ }
- // If we're in an external module, we can't reference value symbols created from UMD export declarations
- if (result && isInExternalModule && (meaning & SymbolFlags.Value) === SymbolFlags.Value && !(originalLocation!.flags & NodeFlags.JSDoc)) {
- const merged = getMergedSymbol(result);
- if (length(merged.declarations) && every(merged.declarations, d => isNamespaceExportDeclaration(d) || isSourceFile(d) && !!d.symbol.globalExports)) {
- errorOrSuggestion(!compilerOptions.allowUmdGlobalAccess, errorLocation!, Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead, unescapeLeadingUnderscores(name));
- }
+ // If we're in an external module, we can't reference value symbols created from UMD export declarations
+ if (result && isInExternalModule && (meaning & SymbolFlags.Value) === SymbolFlags.Value && !(originalLocation!.flags & NodeFlags.JSDoc)) {
+ const merged = getMergedSymbol(result);
+ if (length(merged.declarations) && every(merged.declarations, d => isNamespaceExportDeclaration(d) || isSourceFile(d) && !!d.symbol.globalExports)) {
+ errorOrSuggestion(!compilerOptions.allowUmdGlobalAccess, errorLocation!, Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead, unescapeLeadingUnderscores(name));
}
+ }
- // If we're in a parameter initializer or binding name, we can't reference the values of the parameter whose initializer we're within or parameters to the right
- if (result && associatedDeclarationForContainingInitializerOrBindingName && !withinDeferredContext && (meaning & SymbolFlags.Value) === SymbolFlags.Value) {
- const candidate = getMergedSymbol(getLateBoundSymbol(result));
- const root = (getRootDeclaration(associatedDeclarationForContainingInitializerOrBindingName) as ParameterDeclaration);
- // A parameter initializer or binding pattern initializer within a parameter cannot refer to itself
- if (candidate === getSymbolOfNode(associatedDeclarationForContainingInitializerOrBindingName)) {
- error(errorLocation, Diagnostics.Parameter_0_cannot_reference_itself, declarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.name));
- }
- // And it cannot refer to any declarations which come after it
- else if (candidate.valueDeclaration && candidate.valueDeclaration.pos > associatedDeclarationForContainingInitializerOrBindingName.pos && root.parent.locals && lookup(root.parent.locals, candidate.escapedName, meaning) === candidate) {
- error(errorLocation, Diagnostics.Parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.name), declarationNameToString(errorLocation as Identifier));
- }
+ // If we're in a parameter initializer or binding name, we can't reference the values of the parameter whose initializer we're within or parameters to the right
+ if (result && associatedDeclarationForContainingInitializerOrBindingName && !withinDeferredContext && (meaning & SymbolFlags.Value) === SymbolFlags.Value) {
+ const candidate = getMergedSymbol(getLateBoundSymbol(result));
+ const root = (getRootDeclaration(associatedDeclarationForContainingInitializerOrBindingName) as ParameterDeclaration);
+ // A parameter initializer or binding pattern initializer within a parameter cannot refer to itself
+ if (candidate === getSymbolOfNode(associatedDeclarationForContainingInitializerOrBindingName)) {
+ error(errorLocation, Diagnostics.Parameter_0_cannot_reference_itself, declarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.name));
}
- if (result && errorLocation && meaning & SymbolFlags.Value && result.flags & SymbolFlags.Alias) {
- checkSymbolUsageInExpressionContext(result, name, errorLocation);
+ // And it cannot refer to any declarations which come after it
+ else if (candidate.valueDeclaration && candidate.valueDeclaration.pos > associatedDeclarationForContainingInitializerOrBindingName.pos && root.parent.locals && lookup(root.parent.locals, candidate.escapedName, meaning) === candidate) {
+ error(errorLocation, Diagnostics.Parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.name), declarationNameToString(errorLocation as Identifier));
}
}
- return result;
+ if (result && errorLocation && meaning & SymbolFlags.Value && result.flags & SymbolFlags.Alias) {
+ checkSymbolUsageInExpressionContext(result, name, errorLocation);
+ }
}
+ return result;
+ }
- function checkSymbolUsageInExpressionContext(symbol: Symbol, name: __String, useSite: Node) {
- if (!isValidTypeOnlyAliasUseSite(useSite)) {
- const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(symbol);
- if (typeOnlyDeclaration) {
- const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier
- ? Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type
- : Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type;
- const unescapedName = unescapeLeadingUnderscores(name);
- addTypeOnlyDeclarationRelatedInfo(
- error(useSite, message, unescapedName),
- typeOnlyDeclaration,
- unescapedName);
- }
+ function checkSymbolUsageInExpressionContext(symbol: ts.Symbol, name: __String, useSite: Node) {
+ if (!isValidTypeOnlyAliasUseSite(useSite)) {
+ const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(symbol);
+ if (typeOnlyDeclaration) {
+ const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier
+ ? Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type
+ : Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type;
+ const unescapedName = unescapeLeadingUnderscores(name);
+ addTypeOnlyDeclarationRelatedInfo(error(useSite, message, unescapedName), typeOnlyDeclaration, unescapedName);
}
}
+ }
+
+ function addTypeOnlyDeclarationRelatedInfo(diagnostic: Diagnostic, typeOnlyDeclaration: TypeOnlyCompatibleAliasDeclaration | undefined, unescapedName: string) {
+ if (!typeOnlyDeclaration)
+ return diagnostic;
+ return addRelatedInfo(diagnostic, createDiagnosticForNode(typeOnlyDeclaration, typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier ? Diagnostics._0_was_exported_here : Diagnostics._0_was_imported_here, unescapedName));
+ }
- function addTypeOnlyDeclarationRelatedInfo(diagnostic: Diagnostic, typeOnlyDeclaration: TypeOnlyCompatibleAliasDeclaration | undefined, unescapedName: string) {
- if (!typeOnlyDeclaration) return diagnostic;
- return addRelatedInfo(
- diagnostic,
- createDiagnosticForNode(
- typeOnlyDeclaration,
- typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier ? Diagnostics._0_was_exported_here : Diagnostics._0_was_imported_here,
- unescapedName));
+ function getIsDeferredContext(location: Node, lastLocation: Node | undefined): boolean {
+ if (location.kind !== SyntaxKind.ArrowFunction && location.kind !== SyntaxKind.FunctionExpression) {
+ // initializers in instance property declaration of class like entities are executed in constructor and thus deferred
+ return isTypeQueryNode(location) || ((isFunctionLikeDeclaration(location) ||
+ (location.kind === SyntaxKind.PropertyDeclaration && !isStatic(location))) && (!lastLocation || lastLocation !== (location as SignatureDeclaration | PropertyDeclaration).name)); // A name is evaluated within the enclosing scope - so it shouldn't count as deferred
+ }
+ if (lastLocation && lastLocation === (location as FunctionExpression | ArrowFunction).name) {
+ return false;
}
+ // generator functions and async functions are not inlined in control flow when immediately invoked
+ if ((location as FunctionExpression | ArrowFunction).asteriskToken || hasSyntacticModifier(location, ModifierFlags.Async)) {
+ return true;
+ }
+ return !getImmediatelyInvokedFunctionExpression(location);
+ }
- function getIsDeferredContext(location: Node, lastLocation: Node | undefined): boolean {
- if (location.kind !== SyntaxKind.ArrowFunction && location.kind !== SyntaxKind.FunctionExpression) {
- // initializers in instance property declaration of class like entities are executed in constructor and thus deferred
- return isTypeQueryNode(location) || ((
- isFunctionLikeDeclaration(location) ||
- (location.kind === SyntaxKind.PropertyDeclaration && !isStatic(location))
- ) && (!lastLocation || lastLocation !== (location as SignatureDeclaration | PropertyDeclaration).name)); // A name is evaluated within the enclosing scope - so it shouldn't count as deferred
- }
- if (lastLocation && lastLocation === (location as FunctionExpression | ArrowFunction).name) {
- return false;
- }
- // generator functions and async functions are not inlined in control flow when immediately invoked
- if ((location as FunctionExpression | ArrowFunction).asteriskToken || hasSyntacticModifier(location, ModifierFlags.Async)) {
+ function isSelfReferenceLocation(node: Node): boolean {
+ switch (node.kind) {
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.EnumDeclaration:
+ case SyntaxKind.TypeAliasDeclaration:
+ case SyntaxKind.ModuleDeclaration: // For `namespace N { N; }`
return true;
- }
- return !getImmediatelyInvokedFunctionExpression(location);
+ default:
+ return false;
}
+ }
- function isSelfReferenceLocation(node: Node): boolean {
- switch (node.kind) {
- case SyntaxKind.FunctionDeclaration:
- case SyntaxKind.ClassDeclaration:
- case SyntaxKind.InterfaceDeclaration:
- case SyntaxKind.EnumDeclaration:
- case SyntaxKind.TypeAliasDeclaration:
- case SyntaxKind.ModuleDeclaration: // For `namespace N { N; }`
- return true;
- default:
- return false;
+ function diagnosticName(nameArg: __String | Identifier | PrivateIdentifier) {
+ return isString(nameArg) ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier);
+ }
+
+ function isTypeParameterSymbolDeclaredInContainer(symbol: ts.Symbol, container: Node) {
+ if (symbol.declarations) {
+ for (const decl of symbol.declarations) {
+ if (decl.kind === SyntaxKind.TypeParameter) {
+ const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent;
+ if (parent === container) {
+ return !(isJSDocTemplateTag(decl.parent) && find((decl.parent.parent as JSDoc).tags!, isJSDocTypeAlias)); // TODO: GH#18217
+ }
+ }
}
}
- function diagnosticName(nameArg: __String | Identifier | PrivateIdentifier) {
- return isString(nameArg) ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier);
+ return false;
+ }
+
+ function checkAndReportErrorForMissingPrefix(errorLocation: Node, name: __String, nameArg: __String | Identifier): boolean {
+ if (!isIdentifier(errorLocation) || errorLocation.escapedText !== name || isTypeReferenceIdentifier(errorLocation) || isInTypeQuery(errorLocation)) {
+ return false;
}
- function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) {
- if (symbol.declarations) {
- for (const decl of symbol.declarations) {
- if (decl.kind === SyntaxKind.TypeParameter) {
- const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent;
- if (parent === container) {
- return !(isJSDocTemplateTag(decl.parent) && find((decl.parent.parent as JSDoc).tags!, isJSDocTypeAlias)); // TODO: GH#18217
- }
+ const container = getThisContainer(errorLocation, /*includeArrowFunctions*/ false);
+ let location = container;
+ while (location) {
+ if (isClassLike(location.parent)) {
+ const classSymbol = getSymbolOfNode(location.parent);
+ if (!classSymbol) {
+ break;
+ }
+
+ // Check to see if a static member exists.
+ const constructorType = getTypeOfSymbol(classSymbol);
+ if (getPropertyOfType(constructorType, name)) {
+ error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0, diagnosticName(nameArg), symbolToString(classSymbol));
+ return true;
+ }
+
+ // No static member is present.
+ // Check if we're in an instance method and look for a relevant instance member.
+ if (location === container && !isStatic(location)) {
+ const instanceType = (getDeclaredTypeOfSymbol(classSymbol) as InterfaceType).thisType!; // TODO: GH#18217
+ if (getPropertyOfType(instanceType, name)) {
+ error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, diagnosticName(nameArg));
+ return true;
}
}
}
- return false;
+ location = location.parent;
}
+ return false;
+ }
- function checkAndReportErrorForMissingPrefix(errorLocation: Node, name: __String, nameArg: __String | Identifier): boolean {
- if (!isIdentifier(errorLocation) || errorLocation.escapedText !== name || isTypeReferenceIdentifier(errorLocation) || isInTypeQuery(errorLocation)) {
- return false;
- }
- const container = getThisContainer(errorLocation, /*includeArrowFunctions*/ false);
- let location = container;
- while (location) {
- if (isClassLike(location.parent)) {
- const classSymbol = getSymbolOfNode(location.parent);
- if (!classSymbol) {
- break;
- }
+ function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean {
+ const expression = getEntityNameForExtendingInterface(errorLocation);
+ if (expression && resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)) {
+ error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression));
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression,
+ * but returns undefined if that expression is not an EntityNameExpression.
+ */
+ function getEntityNameForExtendingInterface(node: Node): EntityNameExpression | undefined {
+ switch (node.kind) {
+ case SyntaxKind.Identifier:
+ case SyntaxKind.PropertyAccessExpression:
+ return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined;
+ case SyntaxKind.ExpressionWithTypeArguments:
+ if (isEntityNameExpression((node as ExpressionWithTypeArguments).expression)) {
+ return (node as ExpressionWithTypeArguments).expression as EntityNameExpression;
+ }
+ // falls through
+ default:
+ return undefined;
+ }
+ }
- // Check to see if a static member exists.
- const constructorType = getTypeOfSymbol(classSymbol);
- if (getPropertyOfType(constructorType, name)) {
- error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0, diagnosticName(nameArg), symbolToString(classSymbol));
+ function checkAndReportErrorForUsingTypeAsNamespace(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
+ const namespaceMeaning = SymbolFlags.Namespace | (isInJSFile(errorLocation) ? SymbolFlags.Value : 0);
+ if (meaning === namespaceMeaning) {
+ const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~namespaceMeaning, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
+ const parent = errorLocation.parent;
+ if (symbol) {
+ if (isQualifiedName(parent)) {
+ Debug.assert(parent.left === errorLocation, "Should only be resolving left side of qualified name as a namespace");
+ const propName = parent.right.escapedText;
+ const propType = getPropertyOfType(getDeclaredTypeOfSymbol(symbol), propName);
+ if (propType) {
+ error(parent, Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1, unescapeLeadingUnderscores(name), unescapeLeadingUnderscores(propName));
return true;
}
-
- // No static member is present.
- // Check if we're in an instance method and look for a relevant instance member.
- if (location === container && !isStatic(location)) {
- const instanceType = (getDeclaredTypeOfSymbol(classSymbol) as InterfaceType).thisType!; // TODO: GH#18217
- if (getPropertyOfType(instanceType, name)) {
- error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, diagnosticName(nameArg));
- return true;
- }
- }
}
-
- location = location.parent;
+ error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, unescapeLeadingUnderscores(name));
+ return true;
}
- return false;
}
+ return false;
+ }
- function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean {
- const expression = getEntityNameForExtendingInterface(errorLocation);
- if (expression && resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)) {
- error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression));
+ function checkAndReportErrorForUsingValueAsType(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
+ if (meaning & (SymbolFlags.Type & ~SymbolFlags.Namespace)) {
+ const symbol = resolveSymbol(resolveName(errorLocation, name, ~SymbolFlags.Type & SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
+ if (symbol && !(symbol.flags & SymbolFlags.Namespace)) {
+ error(errorLocation, Diagnostics._0_refers_to_a_value_but_is_being_used_as_a_type_here_Did_you_mean_typeof_0, unescapeLeadingUnderscores(name));
return true;
}
- return false;
- }
- /**
- * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression,
- * but returns undefined if that expression is not an EntityNameExpression.
- */
- function getEntityNameForExtendingInterface(node: Node): EntityNameExpression | undefined {
- switch (node.kind) {
- case SyntaxKind.Identifier:
- case SyntaxKind.PropertyAccessExpression:
- return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined;
- case SyntaxKind.ExpressionWithTypeArguments:
- if (isEntityNameExpression((node as ExpressionWithTypeArguments).expression)) {
- return (node as ExpressionWithTypeArguments).expression as EntityNameExpression;
- }
- // falls through
- default:
- return undefined;
- }
}
+ return false;
+ }
- function checkAndReportErrorForUsingTypeAsNamespace(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
- const namespaceMeaning = SymbolFlags.Namespace | (isInJSFile(errorLocation) ? SymbolFlags.Value : 0);
- if (meaning === namespaceMeaning) {
- const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~namespaceMeaning, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
- const parent = errorLocation.parent;
- if (symbol) {
- if (isQualifiedName(parent)) {
- Debug.assert(parent.left === errorLocation, "Should only be resolving left side of qualified name as a namespace");
- const propName = parent.right.escapedText;
- const propType = getPropertyOfType(getDeclaredTypeOfSymbol(symbol), propName);
- if (propType) {
- error(
- parent,
- Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1,
- unescapeLeadingUnderscores(name),
- unescapeLeadingUnderscores(propName),
- );
- return true;
- }
- }
- error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, unescapeLeadingUnderscores(name));
- return true;
- }
- }
+ function isPrimitiveTypeName(name: __String) {
+ return name === "any" || name === "string" || name === "number" || name === "boolean" || name === "never" || name === "unknown";
+ }
- return false;
+ function checkAndReportErrorForExportingPrimitiveType(errorLocation: Node, name: __String): boolean {
+ if (isPrimitiveTypeName(name) && errorLocation.parent.kind === SyntaxKind.ExportSpecifier) {
+ error(errorLocation, Diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, name as string);
+ return true;
}
+ return false;
+ }
- function checkAndReportErrorForUsingValueAsType(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
- if (meaning & (SymbolFlags.Type & ~SymbolFlags.Namespace)) {
- const symbol = resolveSymbol(resolveName(errorLocation, name, ~SymbolFlags.Type & SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
- if (symbol && !(symbol.flags & SymbolFlags.Namespace)) {
- error(errorLocation, Diagnostics._0_refers_to_a_value_but_is_being_used_as_a_type_here_Did_you_mean_typeof_0, unescapeLeadingUnderscores(name));
- return true;
+ function checkAndReportErrorForUsingTypeAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
+ if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule)) {
+ if (isPrimitiveTypeName(name)) {
+ error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, unescapeLeadingUnderscores(name));
+ return true;
+ }
+ const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
+ if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) {
+ const rawName = unescapeLeadingUnderscores(name);
+ if (isES2015OrLaterConstructorName(name)) {
+ error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later, rawName);
+ }
+ else if (maybeMappedType(errorLocation, symbol)) {
+ error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Did_you_mean_to_use_1_in_0, rawName, rawName === "K" ? "P" : "K");
+ }
+ else {
+ error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, rawName);
}
+ return true;
}
- return false;
}
+ return false;
+ }
- function isPrimitiveTypeName(name: __String) {
- return name === "any" || name === "string" || name === "number" || name === "boolean" || name === "never" || name === "unknown";
+ function maybeMappedType(node: Node, symbol: ts.Symbol) {
+ const container = findAncestor(node.parent, n => isComputedPropertyName(n) || isPropertySignature(n) ? false : isTypeLiteralNode(n) || "quit") as TypeLiteralNode | undefined;
+ if (container && container.members.length === 1) {
+ const type = getDeclaredTypeOfSymbol(symbol);
+ return !!(type.flags & TypeFlags.Union) && allTypesAssignableToKind(type, TypeFlags.StringOrNumberLiteral, /*strict*/ true);
}
+ return false;
+ }
- function checkAndReportErrorForExportingPrimitiveType(errorLocation: Node, name: __String): boolean {
- if (isPrimitiveTypeName(name) && errorLocation.parent.kind === SyntaxKind.ExportSpecifier) {
- error(errorLocation, Diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, name as string);
+ function isES2015OrLaterConstructorName(n: __String) {
+ switch (n) {
+ case "Promise":
+ case "Symbol":
+ case "Map":
+ case "WeakMap":
+ case "Set":
+ case "WeakSet":
return true;
- }
- return false;
}
+ return false;
+ }
- function checkAndReportErrorForUsingTypeAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
- if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule)) {
- if (isPrimitiveTypeName(name)) {
- error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, unescapeLeadingUnderscores(name));
- return true;
- }
- const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
- if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) {
- const rawName = unescapeLeadingUnderscores(name);
- if (isES2015OrLaterConstructorName(name)) {
- error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later, rawName);
- }
- else if (maybeMappedType(errorLocation, symbol)) {
- error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Did_you_mean_to_use_1_in_0, rawName, rawName === "K" ? "P" : "K");
- }
- else {
- error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, rawName);
- }
- return true;
- }
+ function checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
+ if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Type)) {
+ const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
+ if (symbol) {
+ error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_value, unescapeLeadingUnderscores(name));
+ return true;
}
- return false;
}
-
- function maybeMappedType(node: Node, symbol: Symbol) {
- const container = findAncestor(node.parent, n =>
- isComputedPropertyName(n) || isPropertySignature(n) ? false : isTypeLiteralNode(n) || "quit") as TypeLiteralNode | undefined;
- if (container && container.members.length === 1) {
- const type = getDeclaredTypeOfSymbol(symbol);
- return !!(type.flags & TypeFlags.Union) && allTypesAssignableToKind(type, TypeFlags.StringOrNumberLiteral, /*strict*/ true);
+ else if (meaning & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Value)) {
+ const symbol = resolveSymbol(resolveName(errorLocation, name, (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
+ if (symbol) {
+ error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, unescapeLeadingUnderscores(name));
+ return true;
}
- return false;
}
+ return false;
+ }
- function isES2015OrLaterConstructorName(n: __String) {
- switch (n) {
- case "Promise":
- case "Symbol":
- case "Map":
- case "WeakMap":
- case "Set":
- case "WeakSet":
- return true;
- }
- return false;
+ function checkResolvedBlockScopedVariable(result: ts.Symbol, errorLocation: Node): void {
+ Debug.assert(!!(result.flags & SymbolFlags.BlockScopedVariable || result.flags & SymbolFlags.Class || result.flags & SymbolFlags.Enum));
+ if (result.flags & (SymbolFlags.Function | SymbolFlags.FunctionScopedVariable | SymbolFlags.Assignment) && result.flags & SymbolFlags.Class) {
+ // constructor functions aren't block scoped
+ return;
}
+ // Block-scoped variables cannot be used before their definition
+ const declaration = result.declarations?.find(d => isBlockOrCatchScoped(d) || isClassLike(d) || (d.kind === SyntaxKind.EnumDeclaration));
+ if (declaration === undefined)
+ return Debug.fail("checkResolvedBlockScopedVariable could not find block-scoped declaration");
- function checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
- if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Type)) {
- const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
- if (symbol) {
- error(
- errorLocation,
- Diagnostics.Cannot_use_namespace_0_as_a_value,
- unescapeLeadingUnderscores(name));
- return true;
- }
+ if (!(declaration.flags & NodeFlags.Ambient) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) {
+ let diagnosticMessage;
+ const declarationName = declarationNameToString(getNameOfDeclaration(declaration));
+ if (result.flags & SymbolFlags.BlockScopedVariable) {
+ diagnosticMessage = error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationName);
}
- else if (meaning & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Value)) {
- const symbol = resolveSymbol(resolveName(errorLocation, name, (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
- if (symbol) {
- error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, unescapeLeadingUnderscores(name));
- return true;
- }
+ else if (result.flags & SymbolFlags.Class) {
+ diagnosticMessage = error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationName);
}
- return false;
- }
-
- function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void {
- Debug.assert(!!(result.flags & SymbolFlags.BlockScopedVariable || result.flags & SymbolFlags.Class || result.flags & SymbolFlags.Enum));
- if (result.flags & (SymbolFlags.Function | SymbolFlags.FunctionScopedVariable | SymbolFlags.Assignment) && result.flags & SymbolFlags.Class) {
- // constructor functions aren't block scoped
- return;
+ else if (result.flags & SymbolFlags.RegularEnum) {
+ diagnosticMessage = error(errorLocation, Diagnostics.Enum_0_used_before_its_declaration, declarationName);
}
- // Block-scoped variables cannot be used before their definition
- const declaration = result.declarations?.find(
- d => isBlockOrCatchScoped(d) || isClassLike(d) || (d.kind === SyntaxKind.EnumDeclaration));
-
- if (declaration === undefined) return Debug.fail("checkResolvedBlockScopedVariable could not find block-scoped declaration");
-
- if (!(declaration.flags & NodeFlags.Ambient) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) {
- let diagnosticMessage;
- const declarationName = declarationNameToString(getNameOfDeclaration(declaration));
- if (result.flags & SymbolFlags.BlockScopedVariable) {
- diagnosticMessage = error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationName);
- }
- else if (result.flags & SymbolFlags.Class) {
- diagnosticMessage = error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationName);
- }
- else if (result.flags & SymbolFlags.RegularEnum) {
+ else {
+ Debug.assert(!!(result.flags & SymbolFlags.ConstEnum));
+ if (shouldPreserveConstEnums(compilerOptions)) {
diagnosticMessage = error(errorLocation, Diagnostics.Enum_0_used_before_its_declaration, declarationName);
}
- else {
- Debug.assert(!!(result.flags & SymbolFlags.ConstEnum));
- if (shouldPreserveConstEnums(compilerOptions)) {
- diagnosticMessage = error(errorLocation, Diagnostics.Enum_0_used_before_its_declaration, declarationName);
- }
- }
+ }
- if (diagnosticMessage) {
- addRelatedInfo(diagnosticMessage,
- createDiagnosticForNode(declaration, Diagnostics._0_is_declared_here, declarationName)
- );
- }
+ if (diagnosticMessage) {
+ addRelatedInfo(diagnosticMessage, createDiagnosticForNode(declaration, Diagnostics._0_is_declared_here, declarationName));
}
}
+ }
- /* Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached.
- * If at any point current node is equal to 'parent' node - return true.
- * If current node is an IIFE, continue walking up.
- * Return false if 'stopAt' node is reached or isFunctionLike(current) === true.
- */
- function isSameScopeDescendentOf(initial: Node, parent: Node | undefined, stopAt: Node): boolean {
- return !!parent && !!findAncestor(initial, n => n === parent
- || (n === stopAt || isFunctionLike(n) && !getImmediatelyInvokedFunctionExpression(n) ? "quit" : false));
- }
+ /* Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached.
+ * If at any point current node is equal to 'parent' node - return true.
+ * If current node is an IIFE, continue walking up.
+ * Return false if 'stopAt' node is reached or isFunctionLike(current) === true.
+ */
+ function isSameScopeDescendentOf(initial: Node, parent: Node | undefined, stopAt: Node): boolean {
+ return !!parent && !!findAncestor(initial, n => n === parent
+ || (n === stopAt || isFunctionLike(n) && !getImmediatelyInvokedFunctionExpression(n) ? "quit" : false));
+ }
- function getAnyImportSyntax(node: Node): AnyImportSyntax | undefined {
- switch (node.kind) {
- case SyntaxKind.ImportEqualsDeclaration:
- return node as ImportEqualsDeclaration;
- case SyntaxKind.ImportClause:
- return (node as ImportClause).parent;
- case SyntaxKind.NamespaceImport:
- return (node as NamespaceImport).parent.parent;
- case SyntaxKind.ImportSpecifier:
- return (node as ImportSpecifier).parent.parent.parent;
- default:
- return undefined;
- }
+ function getAnyImportSyntax(node: Node): AnyImportSyntax | undefined {
+ switch (node.kind) {
+ case SyntaxKind.ImportEqualsDeclaration:
+ return node as ImportEqualsDeclaration;
+ case SyntaxKind.ImportClause:
+ return (node as ImportClause).parent;
+ case SyntaxKind.NamespaceImport:
+ return (node as NamespaceImport).parent.parent;
+ case SyntaxKind.ImportSpecifier:
+ return (node as ImportSpecifier).parent.parent.parent;
+ default:
+ return undefined;
}
+ }
- function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration | undefined {
- return symbol.declarations && findLast(symbol.declarations, isAliasSymbolDeclaration);
- }
+ function getDeclarationOfAliasSymbol(symbol: ts.Symbol): Declaration | undefined {
+ return symbol.declarations && findLast(symbol.declarations, isAliasSymbolDeclaration);
+ }
- /**
- * An alias symbol is created by one of the following declarations:
- * import = ...
- * import from ...
- * import * as