diff --git a/.eslintrc.json b/.eslintrc.json
index 35901b12013ad..f78f3250020cf 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -87,7 +87,7 @@
"local/simple-indent": "error",
"local/debug-assert": "error",
"local/no-keywords": "error",
- "local/one-namespace-per-file": "error",
+ "local/jsdoc-format": "error",
// eslint-plugin-import
"import/no-extraneous-dependencies": ["error", { "optionalDependencies": false }],
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000000000..d6f923aed1844
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,8 @@
+# Generated module conversion step - inlineImports
+15139837058ce2af370bb4e8f4b18ccc75498881
+# Generated module conversion step - stripNamespaces
+459fb4c4db144fe25db9f99a85679da5c65d3fc9
+# Generated module conversion step - explicitify
+e3488bb30d460cedc2739ed0cc2b9fb4bb186825
+# Generated module conversion step - unindent
+85bdc31dba8f2b5a457f3cc87daf9d266b2d6481
diff --git a/.vscode/settings.template.json b/.vscode/settings.template.json
index ccfcfc2631b00..43d69a60b609c 100644
--- a/.vscode/settings.template.json
+++ b/.vscode/settings.template.json
@@ -6,4 +6,10 @@
// To use the locally built compiler, after 'npm run build':
// "typescript.tsdk": "built/local"
+
+ // To ignore commits listed in .git-blame-ignore-revs in GitLens:
+ // "gitlens.advanced.blame.customArguments": [
+ // "--ignore-revs-file",
+ // ".git-blame-ignore-revs"
+ // ]
}
diff --git a/scripts/eslint/rules/jsdoc-format.cjs b/scripts/eslint/rules/jsdoc-format.cjs
new file mode 100644
index 0000000000000..86783b3547a76
--- /dev/null
+++ b/scripts/eslint/rules/jsdoc-format.cjs
@@ -0,0 +1,110 @@
+const { TSESTree } = require("@typescript-eslint/utils");
+const { createRule } = require("./utils.cjs");
+
+module.exports = createRule({
+ name: "jsdoc-format",
+ meta: {
+ docs: {
+ description: ``,
+ recommended: "error",
+ },
+ messages: {
+ internalCommentInNonJSDocError: `@internal should not appear in non-JSDoc comment for declaration.`,
+ internalCommentNotLastError: `@internal should only appear in final JSDoc comment for declaration.`,
+ multipleJSDocError: `Declaration has multiple JSDoc comments.`,
+ internalCommentOnParameterProperty: `@internal cannot appear on a JSDoc comment; use a declared property and an assignment in the constructor instead.`,
+ },
+ schema: [],
+ type: "problem",
+ },
+ defaultOptions: [],
+
+ create(context) {
+ const sourceCode = context.getSourceCode();
+ const atInternal = "@internal";
+ const jsdocStart = "/**";
+
+ /** @type {(c: TSESTree.Comment, indexInComment: number) => TSESTree.SourceLocation} */
+ const getAtInternalLoc = (c, indexInComment) => {
+ const line = c.loc.start.line;
+ return {
+ start: {
+ line,
+ column: c.loc.start.column + indexInComment,
+ },
+ end: {
+ line,
+ column: c.loc.start.column + indexInComment + atInternal.length,
+ },
+ };
+ };
+
+ /** @type {(c: TSESTree.Comment) => TSESTree.SourceLocation} */
+ const getJSDocStartLoc = (c) => {
+ return {
+ start: c.loc.start,
+ end: {
+ line: c.loc.start.line,
+ column: c.loc.start.column + jsdocStart.length,
+ },
+ };
+ };
+
+ /** @type {(node: TSESTree.Node) => void} */
+ const checkJSDocFormat = (node) => {
+ const blockComments = sourceCode.getCommentsBefore(node).filter(c => c.type === "Block");
+ if (blockComments.length === 0) {
+ return;
+ }
+
+ const last = blockComments.length - 1;
+ let seenJSDoc = false;
+ for (let i = 0; i < blockComments.length; i++) {
+ const c = blockComments[i];
+ const rawComment = sourceCode.getText(c);
+
+ const isJSDoc = rawComment.startsWith(jsdocStart);
+ if (isJSDoc && seenJSDoc) {
+ context.report({ messageId: "multipleJSDocError", node: c, loc: getJSDocStartLoc(c) });
+ }
+ seenJSDoc = seenJSDoc || isJSDoc;
+
+ const indexInComment = rawComment.indexOf(atInternal);
+ if (indexInComment === -1) {
+ continue;
+ }
+
+ if (!isJSDoc) {
+ context.report({ messageId: "internalCommentInNonJSDocError", node: c, loc: getAtInternalLoc(c, indexInComment) });
+ }
+ else if (i !== last) {
+ context.report({ messageId: "internalCommentNotLastError", node: c, loc: getAtInternalLoc(c, indexInComment) });
+ }
+ else if (node.type === "TSParameterProperty") {
+ context.report({ messageId: "internalCommentOnParameterProperty", node: c, loc: getAtInternalLoc(c, indexInComment) });
+ }
+ }
+ };
+
+ return {
+ ClassDeclaration: checkJSDocFormat,
+ FunctionDeclaration: checkJSDocFormat,
+ TSEnumDeclaration: checkJSDocFormat,
+ TSModuleDeclaration: checkJSDocFormat,
+ VariableDeclaration: checkJSDocFormat,
+ TSInterfaceDeclaration: checkJSDocFormat,
+ TSTypeAliasDeclaration: checkJSDocFormat,
+ TSCallSignatureDeclaration: checkJSDocFormat,
+ ExportAllDeclaration: checkJSDocFormat,
+ ExportNamedDeclaration: checkJSDocFormat,
+ TSImportEqualsDeclaration: checkJSDocFormat,
+ TSNamespaceExportDeclaration: checkJSDocFormat,
+ TSConstructSignatureDeclaration: checkJSDocFormat,
+ ExportDefaultDeclaration: checkJSDocFormat,
+ TSPropertySignature: checkJSDocFormat,
+ TSIndexSignature: checkJSDocFormat,
+ TSMethodSignature: checkJSDocFormat,
+ TSParameterProperty: checkJSDocFormat,
+ };
+ },
+});
diff --git a/scripts/eslint/rules/one-namespace-per-file.cjs b/scripts/eslint/rules/one-namespace-per-file.cjs
deleted file mode 100644
index 2b8772005a9c7..0000000000000
--- a/scripts/eslint/rules/one-namespace-per-file.cjs
+++ /dev/null
@@ -1,45 +0,0 @@
-const { AST_NODE_TYPES, TSESTree } = require("@typescript-eslint/utils");
-const { createRule } = require("./utils.cjs");
-
-module.exports = createRule({
- name: "one-namespace-per-file",
- meta: {
- docs: {
- description: `Limits each file to having at most one top-level namespace declaration`,
- recommended: "error",
- },
- messages: {
- excessNamespaceError: `All but one of these namespaces should be moved into separate files.`,
- },
- schema: [],
- type: "problem",
- },
- defaultOptions: [],
-
- create(context) {
- /** @type {(node: TSESTree.Node) => node is TSESTree.TSModuleDeclaration} */
- const isNamespaceDeclaration = (node) => node.type === AST_NODE_TYPES.TSModuleDeclaration;
-
- /** @type {(node: TSESTree.Program) => void} */
- const checkSourceFile = (node) => {
- if (context.getFilename().endsWith(".d.ts")) {
- return;
- }
- const members = node.body;
- const namespaces = members.filter(isNamespaceDeclaration);
- if (namespaces.length <= 1) {
- return;
- }
-
- namespaces.forEach(n => {
- context.report({
- messageId: "excessNamespaceError", node: n
- });
- });
- };
-
- return {
- Program: checkSourceFile,
- };
- },
-});
diff --git a/scripts/processDiagnosticMessages.mjs b/scripts/processDiagnosticMessages.mjs
index cddd618d03abe..9b46eb6630ab4 100644
--- a/scripts/processDiagnosticMessages.mjs
+++ b/scripts/processDiagnosticMessages.mjs
@@ -44,10 +44,7 @@ function main() {
}
}
- const outputFilesDir = path.dirname(inputFilePath);
- const thisFilePathRel = path.relative(process.cwd(), outputFilesDir);
-
- const infoFileOutput = buildInfoFileOutput(diagnosticMessages, `./${path.basename(inputFilePath)}`, thisFilePathRel);
+ const infoFileOutput = buildInfoFileOutput(diagnosticMessages, inputFilePath);
checkForUniqueCodes(diagnosticMessages);
writeFile("diagnosticInformationMap.generated.ts", infoFileOutput);
@@ -72,31 +69,34 @@ function checkForUniqueCodes(diagnosticTable) {
/**
* @param {InputDiagnosticMessageTable} messageTable
* @param {string} inputFilePathRel
- * @param {string} thisFilePathRel
* @returns {string}
*/
-function buildInfoFileOutput(messageTable, inputFilePathRel, thisFilePathRel) {
- let result =
- "// \r\n" +
- "// generated from '" + inputFilePathRel + "' in '" + 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, inputFilePathRel) {
+ const result = [
+ "// ",
+ `// generated from '${inputFilePathRel}'`,
+ "",
+ "import { DiagnosticCategory, DiagnosticMessage } from \"./types\";",
+ "",
+ "function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean, reportsDeprecated?: {}): DiagnosticMessage {",
+ " return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated };",
+ "}",
+ "",
+ "/** @internal */",
+ "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");
}
/**
diff --git a/src/compiler/_namespaces/ts.moduleSpecifiers.ts b/src/compiler/_namespaces/ts.moduleSpecifiers.ts
new file mode 100644
index 0000000000000..47a204d4a72b9
--- /dev/null
+++ b/src/compiler/_namespaces/ts.moduleSpecifiers.ts
@@ -0,0 +1,3 @@
+/* Generated file to emulate the ts.moduleSpecifiers namespace. */
+
+export * from "../moduleSpecifiers";
diff --git a/src/compiler/_namespaces/ts.performance.ts b/src/compiler/_namespaces/ts.performance.ts
new file mode 100644
index 0000000000000..707f8f85ef697
--- /dev/null
+++ b/src/compiler/_namespaces/ts.performance.ts
@@ -0,0 +1,3 @@
+/* Generated file to emulate the ts.performance namespace. */
+
+export * from "../performance";
diff --git a/src/compiler/_namespaces/ts.ts b/src/compiler/_namespaces/ts.ts
new file mode 100644
index 0000000000000..b31b19cae7cb5
--- /dev/null
+++ b/src/compiler/_namespaces/ts.ts
@@ -0,0 +1,74 @@
+/* Generated file to emulate the ts namespace. */
+
+export * from "../corePublic";
+export * from "../core";
+export * from "../debug";
+export * from "../semver";
+export * from "../performanceCore";
+export * from "../perfLogger";
+export * from "../tracing";
+export * from "../types";
+export * from "../sys";
+export * from "../path";
+export * from "../diagnosticInformationMap.generated";
+export * from "../scanner";
+export * from "../utilitiesPublic";
+export * from "../utilities";
+export * from "../factory/baseNodeFactory";
+export * from "../factory/parenthesizerRules";
+export * from "../factory/nodeConverters";
+export * from "../factory/nodeFactory";
+export * from "../factory/emitNode";
+export * from "../factory/emitHelpers";
+export * from "../factory/nodeTests";
+export * from "../factory/utilities";
+export * from "../factory/utilitiesPublic";
+export * from "../parser";
+export * from "../commandLineParser";
+export * from "../moduleNameResolver";
+export * from "../binder";
+export * from "../symbolWalker";
+export * from "../checker";
+export * from "../visitorPublic";
+export * from "../sourcemap";
+export * from "../transformers/utilities";
+export * from "../transformers/destructuring";
+export * from "../transformers/taggedTemplate";
+export * from "../transformers/ts";
+export * from "../transformers/classFields";
+export * from "../transformers/typeSerializer";
+export * from "../transformers/legacyDecorators";
+export * from "../transformers/es2017";
+export * from "../transformers/es2018";
+export * from "../transformers/es2019";
+export * from "../transformers/es2020";
+export * from "../transformers/es2021";
+export * from "../transformers/esnext";
+export * from "../transformers/jsx";
+export * from "../transformers/es2016";
+export * from "../transformers/es2015";
+export * from "../transformers/es5";
+export * from "../transformers/generators";
+export * from "../transformers/module/module";
+export * from "../transformers/module/system";
+export * from "../transformers/module/esnextAnd2015";
+export * from "../transformers/module/node";
+export * from "../transformers/declarations/diagnostics";
+export * from "../transformers/declarations";
+export * from "../transformer";
+export * from "../emitter";
+export * from "../watchUtilities";
+export * from "../program";
+export * from "../builderStatePublic";
+export * from "../builderState";
+export * from "../builder";
+export * from "../builderPublic";
+export * from "../resolutionCache";
+export * from "../watch";
+export * from "../watchPublic";
+export * from "../tsbuild";
+export * from "../tsbuildPublic";
+import * as moduleSpecifiers from "./ts.moduleSpecifiers";
+export { moduleSpecifiers };
+import * as performance from "./ts.performance";
+export { performance };
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 4c3eac092b45a..1f758b6787a9f 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -1,3536 +1,3598 @@
+import * as ts from "./_namespaces/ts";
+import {
+ __String, AccessExpression, addRelatedInfo, append, appendIfUnique, ArrayBindingElement, ArrayLiteralExpression,
+ ArrowFunction, AssignmentDeclarationKind, BinaryExpression, BinaryOperatorToken, BindableAccessExpression,
+ BindableObjectDefinePropertyCall, BindablePropertyAssignmentExpression, BindableStaticAccessExpression,
+ BindableStaticNameExpression, BindableStaticPropertyAssignmentExpression, BindingElement, Block,
+ BreakOrContinueStatement, CallChain, CallExpression, CaseBlock, CaseClause, cast, CatchClause, ClassLikeDeclaration,
+ ClassStaticBlockDeclaration, CompilerOptions, concatenate, ConditionalExpression, ConditionalTypeNode, contains,
+ createBinaryExpressionTrampoline, createDiagnosticForNodeInSourceFile, createFileDiagnostic, createQueue,
+ createSymbolTable, Debug, Declaration, declarationNameToString, DeleteExpression, DestructuringAssignment,
+ DiagnosticCategory, DiagnosticMessage, DiagnosticRelatedInformation, Diagnostics, DiagnosticWithLocation,
+ DoStatement, DynamicNamedDeclaration, ElementAccessChain, ElementAccessExpression, EntityNameExpression,
+ EnumDeclaration, escapeLeadingUnderscores, ESMap, every, ExportAssignment, exportAssignmentIsAlias,
+ ExportDeclaration, ExportSpecifier, Expression, ExpressionStatement, findAncestor, FlowFlags, FlowLabel, FlowNode,
+ FlowReduceLabel, forEach, forEachChild, ForInOrOfStatement, ForStatement, FunctionDeclaration, FunctionExpression,
+ FunctionLikeDeclaration, GetAccessorDeclaration, getAssignedExpandoInitializer, getAssignmentDeclarationKind,
+ getAssignmentDeclarationPropertyAccessKind, getCombinedModifierFlags, getCombinedNodeFlags, getContainingClass,
+ getEffectiveContainerForJSDocTemplateTag, getElementOrPropertyAccessName, getEmitScriptTarget,
+ getEnclosingBlockScopeContainer, getErrorSpanForNode, getEscapedTextOfIdentifierOrLiteral, getExpandoInitializer,
+ getHostSignatureFromJSDoc, getImmediatelyInvokedFunctionExpression, getJSDocTypeTag, getLeftmostAccessExpression,
+ getNameOfDeclaration, getNameOrArgument, getNodeId, getRangesWhere, getRightMostAssignedExpression,
+ getSourceFileOfNode, getSourceTextOfNodeFromSourceFile, getSpanOfTokenAtPosition, getStrictOptionValue,
+ getSymbolNameForPrivateIdentifier, getTextOfIdentifierOrLiteral, getThisContainer, getTokenPosOfNode,
+ hasDynamicName, hasJSDocNodes, hasSyntacticModifier, Identifier, idText, IfStatement, ImportClause,
+ InternalSymbolName, isAliasableExpression, isAmbientModule, isAssignmentExpression, isAssignmentOperator,
+ isAssignmentTarget, isAsyncFunction, isAutoAccessorPropertyDeclaration, isBinaryExpression,
+ isBindableObjectDefinePropertyCall, isBindableStaticAccessExpression, isBindableStaticNameExpression,
+ isBindingPattern, isBlock, isBlockOrCatchScoped, isCallExpression, isClassStaticBlockDeclaration,
+ isConditionalTypeNode, isDeclaration, isDeclarationStatement, isDestructuringAssignment, isDottedName,
+ isElementAccessExpression, isEmptyObjectLiteral, isEntityNameExpression, isEnumConst, isEnumDeclaration,
+ isExportAssignment, isExportDeclaration, isExportsIdentifier, isExportSpecifier, isExpression,
+ isExpressionOfOptionalChainRoot, isExternalModule, isExternalOrCommonJsModule, isForInOrOfStatement,
+ isFunctionDeclaration, isFunctionLike, isFunctionLikeDeclaration, isFunctionLikeOrClassStaticBlockDeclaration,
+ isFunctionSymbol, isGlobalScopeAugmentation, isIdentifier, isIdentifierName, isInJSFile, isInTopLevelContext,
+ isJSDocConstructSignature, isJSDocEnumTag, isJSDocTemplateTag, isJSDocTypeAlias, isJsonSourceFile,
+ isLeftHandSideExpression, isLogicalOrCoalescingAssignmentOperator, isModuleAugmentationExternal, isModuleBlock,
+ isModuleDeclaration, isModuleExportsAccessExpression, isNamedDeclaration, isNamespaceExport, isNonNullExpression,
+ isNullishCoalesce, isObjectLiteralExpression, isObjectLiteralMethod,
+ isObjectLiteralOrClassExpressionMethodOrAccessor, isOmittedExpression, isOptionalChain, isOptionalChainRoot,
+ isOutermostOptionalChain, isParameterDeclaration, isParameterPropertyDeclaration, isParenthesizedExpression,
+ isPartOfTypeQuery, isPrefixUnaryExpression, isPrivateIdentifier, isPrologueDirective,
+ isPropertyAccessEntityNameExpression, isPropertyAccessExpression, isPropertyNameLiteral, isPrototypeAccess,
+ isPushOrUnshiftIdentifier, isRequireCall, isShorthandPropertyAssignment, isSignedNumericLiteral, isSourceFile,
+ isSpecialPropertyDeclaration, isStatement, isStatementButNotDeclaration, isStatic, isString, isStringLiteralLike,
+ isStringOrNumericLiteralLike, isThisInitializedDeclaration, isTypeAliasDeclaration, isTypeOfExpression,
+ isVariableDeclaration, isVariableDeclarationInitializedToBareOrAccessedRequire, isVariableStatement,
+ JSDocCallbackTag, JSDocClassTag, JSDocEnumTag, JSDocFunctionType, JSDocParameterTag, JSDocPropertyLikeTag,
+ JSDocSignature, JSDocTypedefTag, JSDocTypeLiteral, JsxAttribute, JsxAttributes, LabeledStatement, length,
+ LiteralLikeElementAccessExpression, Map, MappedTypeNode, MethodDeclaration, ModifierFlags, ModuleBlock,
+ ModuleDeclaration, Mutable, NamespaceExportDeclaration, Node, NodeArray, NodeFlags, nodeHasName, nodeIsMissing,
+ nodeIsPresent, NonNullChain, NonNullExpression, NumericLiteral, objectAllocator, ObjectLiteralExpression,
+ OptionalChain, ParameterDeclaration, ParenthesizedExpression, Pattern, PatternAmbientModule, perfLogger,
+ PostfixUnaryExpression, PrefixUnaryExpression, PrivateIdentifier, PropertyAccessChain, PropertyAccessExpression,
+ PropertyDeclaration, PropertySignature, removeFileExtension, ReturnStatement, ScriptTarget, Set,
+ SetAccessorDeclaration, setParent, setParentRecursive, setValueDeclaration, ShorthandPropertyAssignment,
+ shouldPreserveConstEnums, SignatureDeclaration, skipParentheses, sliceAfter, some, SourceFile, SpreadElement,
+ Statement, StringLiteral, SwitchStatement, Symbol, SymbolFlags, symbolName, SymbolTable, SyntaxKind, TextRange,
+ ThrowStatement, TokenFlags, tokenToString, tracing, TracingNode, tryCast, tryParsePattern, TryStatement,
+ TypeLiteralNode, TypeOfExpression, TypeParameterDeclaration, unescapeLeadingUnderscores, unreachableCodeIsError,
+ unusedLabelIsError, VariableDeclaration, WhileStatement, WithStatement,
+} from "./_namespaces/ts";
+
+/** @internal */
+export const enum ModuleInstanceState {
+ NonInstantiated = 0,
+ Instantiated = 1,
+ ConstEnumOnly = 2
+}
-/* @internal */
-namespace ts {
- export const enum ModuleInstanceState {
- NonInstantiated = 0,
- Instantiated = 1,
- ConstEnumOnly = 2
- }
-
- interface ActiveLabel {
- next: ActiveLabel | undefined;
- name: __String;
- breakTarget: FlowLabel;
- continueTarget: FlowLabel | undefined;
- referenced: boolean;
- }
+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;
+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;
+}
- 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:
+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;
- }
+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,
+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,
+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) {
- performance.mark("beforeBind");
- perfLogger.logStartBindFile("" + file.fileName);
- binder(file, options);
- perfLogger.logStopBindFile();
- performance.mark("afterBind");
- performance.measure("Bind", "beforeBind", "afterBind");
- }
-
- 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) {
- tracing?.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true);
- bind(file);
- tracing?.pop();
- 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;
- }
+const binder = createBinder();
- return bindSourceFile;
+/** @internal */
+export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
+ ts.performance.mark("beforeBind");
+ perfLogger.logStartBindFile("" + file.fileName);
+ binder(file, options);
+ perfLogger.logStopBindFile();
+ ts.performance.mark("afterBind");
+ ts.performance.measure("Bind", "beforeBind", "afterBind");
+}
- 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 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) {
+ tracing?.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true);
+ bind(file);
+ tracing?.pop();
+ 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 createSymbol(flags: SymbolFlags, name: __String): Symbol {
- symbolCount++;
- return new Symbol(flags, name);
+ 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): Symbol {
+ symbolCount++;
+ return new Symbol(flags, name);
+ }
- node.symbol = symbol;
- symbol.declarations = appendIfUnique(symbol.declarations, node);
+ function addDeclarationToSymbol(symbol: 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: ${Debug.formatSyntaxKind(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: ${Debug.formatSyntaxKind(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: Symbol | undefined, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags, isReplaceableByMethod?: boolean, isComputedName?: boolean): 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: 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;
+ }
- addDeclarationToSymbol(symbol, node, includes);
- if (symbol.parent) {
- Debug.assert(symbol.parent === parent, "Existing symbol parent should match new one");
+ 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 {
- 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 isImmediatelyInvoked =
- (containerFlags & ContainerFlags.IsFunctionExpression &&
- !hasSyntacticModifier(node, ModifierFlags.Async) &&
- !(node as FunctionLikeDeclaration).asteriskToken &&
- !!getImmediatelyInvokedFunctionExpression(node)) ||
- node.kind === SyntaxKind.ClassStaticBlockDeclaration;
- // 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 (!isImmediatelyInvoked) {
- 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 = isImmediatelyInvoked || node.kind === SyntaxKind.Constructor || (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 (!isImmediatelyInvoked) {
- 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 isImmediatelyInvoked =
+ (containerFlags & ContainerFlags.IsFunctionExpression &&
+ !hasSyntacticModifier(node, ModifierFlags.Async) &&
+ !(node as FunctionLikeDeclaration).asteriskToken &&
+ !!getImmediatelyInvokedFunctionExpression(node)) ||
+ node.kind === SyntaxKind.ClassStaticBlockDeclaration;
+ // 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 (!isImmediatelyInvoked) {
+ 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 = isImmediatelyInvoked || node.kind === SyntaxKind.Constructor || (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 (!isImmediatelyInvoked) {
+ 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.Parameter:
- bindParameterFlow(node as ParameterDeclaration);
- 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.Parameter:
+ bindParameterFlow(node as ParameterDeclaration);
+ 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) || isEntityNameExpression(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) || isEntityNameExpression(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;
}
+ 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;
+ 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;
+ }
- 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 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 createBranchLabel(): FlowLabel {
+ return initFlowNode({ flags: FlowFlags.BranchLabel, antecedents: undefined });
+ }
+
+ function createLoopLabel(): FlowLabel {
+ return initFlowNode({ flags: FlowFlags.LoopLabel, antecedents: undefined });
+ }
+
+ 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 addAntecedent(label: FlowLabel, antecedent: FlowNode): void {
+ if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) {
+ (label.antecedents || (label.antecedents = [])).push(antecedent);
+ setFlowNodeReferenced(antecedent);
}
+ }
- function createLoopLabel(): FlowLabel {
- return initFlowNode({ flags: FlowFlags.LoopLabel, antecedents: undefined });
+ 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 createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
+ setFlowNodeReferenced(antecedent);
+ return initFlowNode({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd });
+ }
- function createReduceLabel(target: FlowLabel, antecedents: FlowNode[], antecedent: FlowNode): FlowReduceLabel {
- return initFlowNode({ flags: FlowFlags.ReduceLabel, target, antecedents, antecedent });
+ 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 createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode {
+ setFlowNodeReferenced(antecedent);
+ return initFlowNode({ flags: FlowFlags.Call, antecedent, node });
+ }
- 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 finishFlowLabel(flow: FlowLabel): FlowNode {
+ const antecedents = flow.antecedents;
+ if (!antecedents) {
+ return unreachableFlow;
}
+ if (antecedents.length === 1) {
+ return antecedents[0];
+ }
+ return flow;
+ }
- 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 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 createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression | undefined): FlowNode {
- if (antecedent.flags & FlowFlags.Unreachable) {
- return antecedent;
- }
- if (!expression) {
- return flags & FlowFlags.TrueCondition ? antecedent : unreachableFlow;
+ function isLogicalExpression(node: Node) {
+ while (true) {
+ if (node.kind === SyntaxKind.ParenthesizedExpression) {
+ node = (node as ParenthesizedExpression).expression;
}
- if ((expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition ||
- expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) &&
- !isExpressionOfOptionalChainRoot(expression) && !isNullishCoalesce(expression.parent)) {
- return unreachableFlow;
+ else if (node.kind === SyntaxKind.PrefixUnaryExpression && (node as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken) {
+ node = (node as PrefixUnaryExpression).operand;
}
- if (!isNarrowingExpression(expression)) {
- return antecedent;
+ 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);
}
- setFlowNodeReferenced(antecedent);
- return initFlowNode({ flags, antecedent, node: expression });
}
+ }
- 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) &&
+ !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 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 finishFlowLabel(flow: FlowLabel): FlowNode {
- const antecedents = flow.antecedents;
- if (!antecedents) {
- return unreachableFlow;
- }
- if (antecedents.length === 1) {
- return antecedents[0];
- }
- return flow;
+ 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 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 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 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 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 isLogicalAssignmentExpression(node: Node) {
- node = skipParentheses(node);
- return isBinaryExpression(node) && isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind);
- }
+ 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 isTopLevelLogicalExpression(node: Node): boolean {
- while (isParenthesizedExpression(node.parent) ||
- isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.ExclamationToken) {
- node = node.parent;
- }
- return !isStatementCondition(node) &&
- !isLogicalExpression(node.parent) &&
- !(isOptionalChain(node.parent) && node.parent.expression === node);
- }
+ 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 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 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 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 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 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 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);
- }
+ 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;
}
}
-
- 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);
+ 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));
+ }
+ 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 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 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 bindExpressionStatement(node: ExpressionStatement): void {
+ bind(node.expression);
+ maybeBindExpressionFlowIfCall(node.expression);
+ }
- function maybeBindExpressionFlowIfCall(node: Expression) {
- // A top level or 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 maybeBindExpressionFlowIfCall(node: Expression) {
+ // A top level or 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 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 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 bindDestructuringTargetFlow(node: Expression) {
- if (node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
- bindAssignmentTargetFlow((node as BinaryExpression).left);
- }
- else {
- bindAssignmentTargetFlow(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);
+ }
+ }
- 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);
- }
+ 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 (p.kind === SyntaxKind.ShorthandPropertyAssignment) {
- bindAssignmentTargetFlow(p.name);
- }
- else if (p.kind === SyntaxKind.SpreadAssignment) {
- bindAssignmentTargetFlow(p.expression);
- }
+ }
+ 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);
}
}
}
+ }
- 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) {
- const maybeBound = maybeBind(left);
- if (node.operatorToken.kind === SyntaxKind.CommaToken) {
- maybeBindExpressionFlowIfCall(left);
- }
- return maybeBound;
+ function onLeft(left: Expression, state: WorkArea, node: BinaryExpression) {
+ if (!state.skip) {
+ const maybeBound = maybeBind(left);
+ if (node.operatorToken.kind === SyntaxKind.CommaToken) {
+ maybeBindExpressionFlowIfCall(left);
}
+ return maybeBound;
}
+ }
- function onOperator(operatorToken: BinaryOperatorToken, state: WorkArea, _node: BinaryExpression) {
- if (!state.skip) {
- bind(operatorToken);
- }
+ function onOperator(operatorToken: BinaryOperatorToken, state: WorkArea, _node: BinaryExpression) {
+ if (!state.skip) {
+ bind(operatorToken);
}
+ }
- function onRight(right: Expression, state: WorkArea, node: BinaryExpression) {
- if (!state.skip) {
- const maybeBound = maybeBind(right);
- if (node.operatorToken.kind === SyntaxKind.CommaToken) {
- maybeBindExpressionFlowIfCall(right);
- }
- return maybeBound;
+ function onRight(right: Expression, state: WorkArea, node: BinaryExpression) {
+ if (!state.skip) {
+ const maybeBound = maybeBind(right);
+ if (node.operatorToken.kind === SyntaxKind.CommaToken) {
+ maybeBindExpressionFlowIfCall(right);
}
+ return maybeBound;
}
+ }
- 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) {
+ bindEachChild(node);
+ if (node.initializer || isForInOrOfStatement(node.parent.parent)) {
+ bindInitializedVariableFlow(node);
}
+ }
+
+ function bindBindingElementFlow(node: BindingElement) {
+ // 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?`
+ bind(node.dotDotDotToken);
+ bind(node.propertyName);
+ bindInitializer(node.initializer);
+ bind(node.name);
+ }
+
+ function bindParameterFlow(node: ParameterDeclaration) {
+ bindEach(node.modifiers);
+ bind(node.dotDotDotToken);
+ bind(node.questionToken);
+ bind(node.type);
+ bindInitializer(node.initializer);
+ bind(node.name);
+ }
- function bindBindingElementFlow(node: BindingElement) {
- // 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?`
- bind(node.dotDotDotToken);
- bind(node.propertyName);
- bindInitializer(node.initializer);
- bind(node.name);
+ // a BindingElement/Parameter does not have side effects if initializers are not evaluated and used. (see GH#49759)
+ function bindInitializer(node: Expression | undefined) {
+ if (!node) {
+ return;
}
+ const entryFlow = currentFlow;
+ bind(node);
+ if (entryFlow === unreachableFlow || entryFlow === currentFlow) {
+ return;
+ }
+ const exitFlow = createBranchLabel();
+ addAntecedent(exitFlow, entryFlow);
+ addAntecedent(exitFlow, currentFlow);
+ currentFlow = finishFlowLabel(exitFlow);
+ }
- function bindParameterFlow(node: ParameterDeclaration) {
- bindEach(node.modifiers);
- bind(node.dotDotDotToken);
- bind(node.questionToken);
- bind(node.type);
- bindInitializer(node.initializer);
- bind(node.name);
+ 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);
+ }
+ }
- // a BindingElement/Parameter does not have side effects if initializers are not evaluated and used. (see GH#49759)
- function bindInitializer(node: Expression | undefined) {
- if (!node) {
- return;
- }
- const entryFlow = currentFlow;
- bind(node);
- if (entryFlow === unreachableFlow || entryFlow === currentFlow) {
- return;
- }
- const exitFlow = createBranchLabel();
- addAntecedent(exitFlow, entryFlow);
- addAntecedent(exitFlow, currentFlow);
- currentFlow = finishFlowLabel(exitFlow);
+ function bindJSDocClassTag(node: JSDocClassTag) {
+ bindEachChild(node);
+ const host = getHostSignatureFromJSDoc(node);
+ if (host && host.kind !== SyntaxKind.MethodDeclaration) {
+ addDeclarationToSymbol(host.symbol, host, SymbolFlags.Class);
}
+ }
- 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 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 bindJSDocClassTag(node: JSDocClassTag) {
- bindEachChild(node);
- const host = getHostSignatureFromJSDoc(node);
- if (host && host.kind !== SyntaxKind.MethodDeclaration) {
- addDeclarationToSymbol(host.symbol, host, SymbolFlags.Class);
- }
+ 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 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 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 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 bindOptionalChainFlow(node: OptionalChain) {
+ if (isTopLevelLogicalExpression(node)) {
+ const postExpressionLabel = createBranchLabel();
+ bindOptionalChain(node, postExpressionLabel, postExpressionLabel);
+ currentFlow = finishFlowLabel(postExpressionLabel);
+ }
+ else {
+ bindOptionalChain(node, currentTrueTarget!, currentFalseTarget!);
}
+ }
- 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 bindNonNullExpressionFlow(node: NonNullExpression | NonNullChain) {
+ if (isOptionalChain(node)) {
+ bindOptionalChainFlow(node);
+ }
+ else {
+ bindEachChild(node);
}
+ }
- function bindOptionalChainFlow(node: OptionalChain) {
- if (isTopLevelLogicalExpression(node)) {
- const postExpressionLabel = createBranchLabel();
- bindOptionalChain(node, postExpressionLabel, postExpressionLabel);
- currentFlow = finishFlowLabel(postExpressionLabel);
- }
- else {
- bindOptionalChain(node, currentTrueTarget!, currentFalseTarget!);
- }
+ function bindAccessExpressionFlow(node: AccessExpression | PropertyAccessChain | ElementAccessChain) {
+ if (isOptionalChain(node)) {
+ bindOptionalChainFlow(node);
}
+ else {
+ bindEachChild(node);
+ }
+ }
- function bindNonNullExpressionFlow(node: NonNullExpression | NonNullChain) {
- 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 {
bindEachChild(node);
+ if (node.expression.kind === SyntaxKind.SuperKeyword) {
+ currentFlow = createFlowCall(currentFlow, node);
+ }
}
}
-
- function bindAccessExpressionFlow(node: AccessExpression | PropertyAccessChain | ElementAccessChain) {
- if (isOptionalChain(node)) {
- bindOptionalChainFlow(node);
- }
- else {
- bindEachChild(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 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 {
- 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);
- }
- }
+ 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.ModuleDeclaration:
+ case SyntaxKind.TypeAliasDeclaration:
+ case SyntaxKind.MappedType:
+ case SyntaxKind.IndexSignature:
+ 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.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;
}
- 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;
+ return ContainerFlags.None;
+ }
- case SyntaxKind.InterfaceDeclaration:
- return ContainerFlags.IsContainer | ContainerFlags.IsInterface;
+ function addToContainerChain(next: Node) {
+ if (lastContainer) {
+ lastContainer.nextContainer = next;
+ }
- case SyntaxKind.ModuleDeclaration:
- case SyntaxKind.TypeAliasDeclaration:
- case SyntaxKind.MappedType:
- case SyntaxKind.IndexSignature:
- return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
+ lastContainer = next;
+ }
- case SyntaxKind.SourceFile:
- return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals;
+ 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.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.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;
- }
+ 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 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);
- }
- }
+ 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);
+ }
- const symbol = declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes)!;
- file.patternAmbientModules = append(file.patternAmbientModules, pattern && !isString(pattern) ? { pattern, symbol } : undefined);
- }
+ 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;
+ }
- const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type);
- addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral);
- typeLiteralSymbol.members = createSymbolTable();
- typeLiteralSymbol.members.set(symbol.escapedName, symbol);
- }
+ 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);
+ }
- function bindObjectLiteralExpression(node: ObjectLiteralExpression) {
- return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.Object);
- }
+ function bindObjectLiteralExpression(node: ObjectLiteralExpression) {
+ return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.Object);
+ }
- function bindJsxAttributes(node: JsxAttributes) {
- return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.JSXAttributes);
- }
+ function bindJsxAttributes(node: JsxAttributes) {
+ return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.JSXAttributes);
+ }
- function bindJsxAttribute(node: JsxAttribute, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
- return declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
+ 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 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:
+ 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 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;
+ 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;
+ }
}
- }
-
- function delayedBindJSDocTypedefTag() {
- if (!delayedTypeAliases) {
- return;
+ else if (isJSDocEnumTag(typeAlias) || !typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) {
+ parent = typeAlias.parent;
+ bindBlockScopedDeclaration(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
}
- 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 (isJSDocEnumTag(typeAlias) || !typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) {
- parent = typeAlias.parent;
- bindBlockScopedDeclaration(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
- }
- else {
- bind(typeAlias.fullName);
- }
+ else {
+ bind(typeAlias.fullName);
}
- container = saveContainer;
- lastContainer = saveLastContainer;
- blockScopeContainer = saveBlockScopeContainer;
- parent = saveParent;
- currentFlow = saveCurrentFlow;
}
+ container = saveContainer;
+ lastContainer = saveLastContainer;
+ blockScopeContainer = saveBlockScopeContainer;
+ parent = saveParent;
+ currentFlow = saveCurrentFlow;
+ }
- // 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) {
+ // 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,
- 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)));
- }
+ Diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module,
+ declarationNameToString(node)));
}
- else if (node.originalKeywordKind === SyntaxKind.YieldKeyword && node.flags & NodeFlags.YieldContext) {
+ 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)));
}
}
- }
-
- 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;
+ 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)));
}
+ }
+ }
- if (file.externalModuleIndicator) {
- return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_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;
+ }
- return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode;
+ if (file.externalModuleIndicator) {
+ return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_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)));
- }
+ 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)));
}
}
+ }
- 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);
- if (tracing) (node as TracingNode).tracingPath = file.path;
- const saveInStrictMode = inStrictMode;
+ function bind(node: Node | undefined): void {
+ if (!node) {
+ return;
+ }
+ setParent(node, parent);
+ if (tracing) (node as TracingNode).tracingPath = file.path;
+ 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) {
- const isAutoAccessor = isAutoAccessorPropertyDeclaration(node);
- const includes = isAutoAccessor ? SymbolFlags.Accessor : SymbolFlags.Property;
- const excludes = isAutoAccessor ? SymbolFlags.AccessorExcludes : SymbolFlags.PropertyExcludes;
- return bindPropertyOrMethodOrAccessor(node, includes | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), excludes);
- }
+ function bindPropertyWorker(node: PropertyDeclaration | PropertySignature) {
+ const isAutoAccessor = isAutoAccessorPropertyDeclaration(node);
+ const includes = isAutoAccessor ? SymbolFlags.Accessor : SymbolFlags.Property;
+ const excludes = isAutoAccessor ? SymbolFlags.AccessorExcludes : SymbolFlags.PropertyExcludes;
+ return bindPropertyOrMethodOrAccessor(node, includes | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), excludes);
+ }
- 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)!);
+ }
+ 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 bindNamespaceExportDeclaration(node: NamespaceExportDeclaration) {
- if (some(node.modifiers)) {
- 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);
- }
+ function bindNamespaceExportDeclaration(node: NamespaceExportDeclaration) {
+ if (some(node.modifiers)) {
+ 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);
+ }
+ }
- 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 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 bindImportClause(node: ImportClause) {
- if (node.name) {
- declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
- }
+ function bindImportClause(node: ImportClause) {
+ if (node.name) {
+ declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
}
+ }
- function setCommonJsModuleIndicator(node: Node) {
- if (file.externalModuleIndicator && file.externalModuleIndicator !== true) {
- return false;
- }
- if (!file.commonJsModuleIndicator) {
- file.commonJsModuleIndicator = node;
- if (!file.externalModuleIndicator) {
- bindSourceFileAsExternalModule();
- }
+ function setCommonJsModuleIndicator(node: Node) {
+ if (file.externalModuleIndicator && file.externalModuleIndicator !== true) {
+ return false;
+ }
+ if (!file.commonJsModuleIndicator) {
+ file.commonJsModuleIndicator = node;
+ if (!file.externalModuleIndicator) {
+ bindSourceFileAsExternalModule();
}
- return true;
}
+ return true;
+ }
- 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;
- });
+ function bindObjectDefinePropertyExport(node: BindableObjectDefinePropertyCall) {
+ if (!setCommonJsModuleIndicator(node)) {
+ return;
+ }
+ const symbol = forEachIdentifierInEntityName(node.arguments[0], /*parent*/ undefined, (id, symbol) => {
if (symbol) {
- const flags = SymbolFlags.Property | SymbolFlags.ExportValue;
- declareSymbol(symbol.exports!, symbol, node, 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 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 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) {
- 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 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);
}
+ }
- 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;
- }
-
- // '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!;
- 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
+ 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);
+ }
+ }
+
+ 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)) {
- break;
- }
- else if ((thisContainer as SourceFile).commonJsModuleIndicator) {
- declareSymbol(thisContainer.symbol.exports!, thisContainer.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None);
+ bindDynamicallyNamedThisPropertyAssignment(node, constructorSymbol, constructorSymbol.members);
}
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: 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: Symbol | undefined) {
+ if (symbol) {
+ (symbol.assignmentDeclarationMembers || (symbol.assignmentDeclarationMembers = new 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: 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: 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: 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): 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: 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 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)));
- }
- 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);
- }
-
- function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) {
- if (inStrictMode) {
- checkStrictModeEvalOrArguments(node, node.name);
- }
-
- if (!isBindingPattern(node.name)) {
- const possibleVariableDecl = node.kind === SyntaxKind.VariableDeclaration ? node : node.parent.parent;
- if (isInJSFile(node) &&
- isVariableDeclarationInitializedToBareOrAccessedRequire(possibleVariableDecl) &&
- !getJSDocTypeTag(node) &&
- !(getCombinedModifierFlags(node) & ModifierFlags.Export)
- ) {
- 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);
- }
+ // 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)));
}
+ symbol.exports!.set(prototypeSymbol.escapedName, prototypeSymbol);
+ prototypeSymbol.parent = symbol;
+ }
- 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);
- }
+ function bindEnumDeclaration(node: EnumDeclaration) {
+ return isEnumConst(node)
+ ? bindBlockScopedDeclaration(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes)
+ : bindBlockScopedDeclaration(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes);
+ }
+
+ function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) {
+ if (inStrictMode) {
+ checkStrictModeEvalOrArguments(node, node.name);
+ }
- if (isBindingPattern(node.name)) {
- bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, "__" + (node as ParameterDeclaration).parent.parameters.indexOf(node as ParameterDeclaration) as __String);
+ if (!isBindingPattern(node.name)) {
+ const possibleVariableDecl = node.kind === SyntaxKind.VariableDeclaration ? node : node.parent.parent;
+ if (isInJSFile(node) &&
+ isVariableDeclarationInitializedToBareOrAccessedRequire(possibleVariableDecl) &&
+ !getJSDocTypeTag(node) &&
+ !(getCombinedModifierFlags(node) & ModifierFlags.Export)
+ ) {
+ declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
}
- else {
+ 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);
}
-
- // 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);
}
- return true;
+ else {
+ bindAnonymousDeclaration(node, SymbolFlags.TypeParameter, getDeclarationName(node)!); // TODO: GH#18217
+ }
+ }
+ 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;
+ }
+}
+
+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`
+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 = createQueue();
- q.enqueue(node);
- while (!q.isEmpty() && i < 100) {
- i++;
- node = q.dequeue();
- 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.enqueue(init);
- if (isAssignmentExpression(init, /*excludeCompoundAssignment*/ true)) {
- q.enqueue(init.left);
- q.enqueue(init.right);
- }
+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 = createQueue();
+ q.enqueue(node);
+ while (!q.isEmpty() && i < 100) {
+ i++;
+ node = q.dequeue();
+ 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.enqueue(init);
+ if (isAssignmentExpression(init, /*excludeCompoundAssignment*/ true)) {
+ q.enqueue(init.left);
+ q.enqueue(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);
+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 5a599b5840885..dfc492c78086c 100644
--- a/src/compiler/builder.ts
+++ b/src/compiler/builder.ts
@@ -1,738 +1,731 @@
-/*@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;
- }
+import * as ts from "./_namespaces/ts";
+import {
+ addRange, AffectedFileResult, arrayFrom, arrayToMap, BuilderProgram, BuilderProgramHost, BuilderState, BuildInfo,
+ BundleBuildInfo, CancellationToken, CommandLineOption, compareStringsCaseSensitive, compareValues, CompilerHost,
+ CompilerOptions, compilerOptionsAffectDeclarationPath, compilerOptionsAffectEmit,
+ compilerOptionsAffectSemanticDiagnostics, CompilerOptionsValue, concatenate, convertToOptionsWithAbsolutePaths,
+ createBuildInfo, createGetCanonicalFileName, createProgram, CustomTransformers, Debug, Diagnostic,
+ DiagnosticCategory, DiagnosticMessageChain, DiagnosticRelatedInformation, DiagnosticWithLocation,
+ EmitAndSemanticDiagnosticsBuilderProgram, EmitOnly, EmitResult, emitSkippedWithNoDiagnostics, emptyArray,
+ ensurePathIsNonModuleName, ESMap, filterSemanticDiagnostics, forEach, forEachEntry, forEachKey, generateDjb2Hash,
+ GetCanonicalFileName, getDirectoryPath, getEmitDeclarations, getNormalizedAbsolutePath, getOptionsNameMap,
+ getOwnKeys, getRelativePathFromDirectory, getTsBuildInfoEmitOutputFilePath, handleNoEmitOptions, isArray,
+ isDeclarationFileName, isJsonSourceFile, isNumber, isString, map, Map, mapDefined, maybeBind, noop, notImplemented,
+ outFile, Path, Program, ProjectReference, ReadBuildProgramHost, ReadonlyCollection, ReadonlyESMap, ReadonlySet,
+ returnFalse, returnUndefined, SemanticDiagnosticsBuilderProgram, Set, skipTypeChecking, some, SourceFile,
+ sourceFileMayBeEmitted, SourceMapEmitResult, toPath, tryAddToSet, WriteFileCallback, WriteFileCallbackData,
+} from "./_namespaces/ts";
+
+/** @internal */
+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;
- }
+/** @internal */
+export interface ReusableDiagnosticRelatedInformation {
+ category: DiagnosticCategory;
+ code: number;
+ file: string | undefined;
+ start: number | undefined;
+ length: number | undefined;
+ messageText: string | ReusableDiagnosticMessageChain;
+}
- export type ReusableDiagnosticMessageChain = DiagnosticMessageChain;
-
- /** Signature (Hash of d.ts emitted), is string if it was emitted using same d.ts.map option as what compilerOptions indicate, otherwise tuple of string */
- export type EmitSignature = string | [signature: string];
-
- export interface ReusableBuilderProgramState 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;
- /**
- * program corresponding to this state
- */
- program?: Program | undefined;
- /**
- * compilerOptions for the program
- */
- compilerOptions: CompilerOptions;
- /**
- * Files pending to be emitted
- */
- affectedFilesPendingEmit?: ReadonlyESMap;
- /**
- * emitKind pending for a program with --out
- */
- programEmitPending?: BuilderFileEmit;
- /*
- * true if semantic diagnostics are ReusableDiagnostic instead of Diagnostic
- */
- hasReusableDiagnostic?: true;
- /**
- * Hash of d.ts emitted for the file, use to track when emit of d.ts changes
- */
- emitSignatures?: ESMap;
- /**
- * Hash of d.ts emit with --out
- */
- outSignature?: EmitSignature;
- /**
- * Name of the file whose dts was the latest to change
- */
- latestChangedDtsFile: string | undefined;
- /**
- * Bundle information either from oldState or current one so it can be used to complete the information in buildInfo when emitting only js or dts files
- */
- bundle?: BundleBuildInfo;
- }
+/** @internal */
+export type ReusableDiagnosticMessageChain = DiagnosticMessageChain;
- export const enum BuilderFileEmit {
- None = 0,
- Js = 1 << 0, // emit js file
- JsMap = 1 << 1, // emit js.map file
- JsInlineMap = 1 << 2, // emit inline source map in js file
- Dts = 1 << 3, // emit d.ts file
- DtsMap = 1 << 4, // emit d.ts.map file
-
- AllJs = Js | JsMap | JsInlineMap,
- AllDts = Dts | DtsMap,
- All = AllJs | AllDts,
- }
+/** @internal */
+/** Signature (Hash of d.ts emitted), is string if it was emitted using same d.ts.map option as what compilerOptions indicate, otherwise tuple of string */
+export type EmitSignature = string | [signature: string];
+/** @internal */
+export interface ReusableBuilderProgramState extends BuilderState {
/**
- * 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, ReusableBuilderProgramState {
- /**
- * 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;
- /**
- * 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;
- /**
- * Records if change in dts emit was detected
- */
- hasChangedEmitSignature?: boolean;
- /**
- * Files pending to be emitted
- */
- affectedFilesPendingEmit?: ESMap;
- /**
- * true if build info is emitted
- */
- buildInfoEmitPending: boolean;
- /**
- * Already seen emitted files
- */
- seenEmittedFiles: ESMap | undefined;
- /** Stores list of files that change signature during emit - test only */
- filesChangingSignature?: Set;
- }
-
- export type SavedBuildProgramEmitState = Pick & { changedFilesSet: BuilderProgramState["changedFilesSet"] | undefined };
-
- /** Get flags determining what all needs to be emitted */
- export function getBuilderFileEmit(options: CompilerOptions) {
- let result = BuilderFileEmit.Js;
- if (options.sourceMap) result = result | BuilderFileEmit.JsMap;
- if (options.inlineSourceMap) result = result | BuilderFileEmit.JsInlineMap;
- if (getEmitDeclarations(options)) result = result | BuilderFileEmit.Dts;
- if (options.declarationMap) result = result | BuilderFileEmit.DtsMap;
- if (options.emitDeclarationOnly) result = result & BuilderFileEmit.AllDts;
- return result;
- }
-
- /** Determing what all is pending to be emitted based on previous options or previous file emit flags */
- export function getPendingEmitKind(optionsOrEmitKind: CompilerOptions | BuilderFileEmit, oldOptionsOrEmitKind: CompilerOptions | BuilderFileEmit | undefined): BuilderFileEmit {
- const oldEmitKind = oldOptionsOrEmitKind && (isNumber(oldOptionsOrEmitKind) ? oldOptionsOrEmitKind : getBuilderFileEmit(oldOptionsOrEmitKind));
- const emitKind = isNumber(optionsOrEmitKind) ? optionsOrEmitKind : getBuilderFileEmit(optionsOrEmitKind);
- if (oldEmitKind === emitKind) return BuilderFileEmit.None;
- if (!oldEmitKind || !emitKind) return emitKind;
- const diff = oldEmitKind ^ emitKind;
- let result = BuilderFileEmit.None;
- // If there is diff in Js emit, pending emit is js emit flags
- if (diff & BuilderFileEmit.AllJs) result = emitKind & BuilderFileEmit.AllJs;
- // If there is diff in Dts emit, pending emit is dts emit flags
- if (diff & BuilderFileEmit.AllDts) result = result | (emitKind & BuilderFileEmit.AllDts);
- return result;
- }
+ semanticDiagnosticsPerFile?: ESMap | undefined;
+ /**
+ * The map has key by source file's path that has been changed
+ */
+ changedFilesSet?: Set;
+ /**
+ * program corresponding to this state
+ */
+ program?: Program | undefined;
+ /**
+ * compilerOptions for the program
+ */
+ compilerOptions: CompilerOptions;
+ /**
+ * Files pending to be emitted
+ */
+ affectedFilesPendingEmit?: ReadonlyESMap;
+ /**
+ * emitKind pending for a program with --out
+ */
+ programEmitPending?: BuilderFileEmit;
+ /*
+ * true if semantic diagnostics are ReusableDiagnostic instead of Diagnostic
+ */
+ hasReusableDiagnostic?: true;
+ /**
+ * Hash of d.ts emitted for the file, use to track when emit of d.ts changes
+ */
+ emitSignatures?: ESMap;
+ /**
+ * Hash of d.ts emit with --out
+ */
+ outSignature?: EmitSignature;
+ /**
+ * Name of the file whose dts was the latest to change
+ */
+ latestChangedDtsFile: string | undefined;
+ /**
+ * Bundle information either from oldState or current one so it can be used to complete the information in buildInfo when emitting only js or dts files
+ */
+ bundle?: BundleBuildInfo;
+}
- 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 {
+ None = 0,
+ Js = 1 << 0, // emit js file
+ JsMap = 1 << 1, // emit js.map file
+ JsInlineMap = 1 << 2, // emit inline source map in js file
+ Dts = 1 << 3, // emit d.ts file
+ DtsMap = 1 << 4, // emit d.ts.map file
+
+ AllJs = Js | JsMap | JsInlineMap,
+ AllDts = Dts | DtsMap,
+ All = AllJs | AllDts,
+}
+/** @internal */
+/**
+ * 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.
+export interface BuilderProgramState extends BuilderState, ReusableBuilderProgramState {
+ /**
+ * 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;
+ /**
+ * 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;
+ /**
+ * Records if change in dts emit was detected
+ */
+ hasChangedEmitSignature?: boolean;
/**
- * Create the state so that we can iterate on changedFiles/affected files
+ * Files pending to be emitted
*/
- 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;
- const outFilePath = outFile(compilerOptions);
- // With --out or --outFile, any change affects all semantic diagnostics so no need to cache them
- if (!outFilePath) {
- state.semanticDiagnosticsPerFile = new Map();
+ affectedFilesPendingEmit?: ESMap;
+ /**
+ * true if build info is emitted
+ */
+ buildInfoEmitPending: boolean;
+ /**
+ * Already seen emitted files
+ */
+ seenEmittedFiles: ESMap | undefined;
+ /** Stores list of files that change signature during emit - test only */
+ filesChangingSignature?: Set;
+}
+
+/** @internal */
+export type SavedBuildProgramEmitState = Pick & { changedFilesSet: BuilderProgramState["changedFilesSet"] | undefined };
+
+/** @internal */
+/** Get flags determining what all needs to be emitted */
+export function getBuilderFileEmit(options: CompilerOptions) {
+ let result = BuilderFileEmit.Js;
+ if (options.sourceMap) result = result | BuilderFileEmit.JsMap;
+ if (options.inlineSourceMap) result = result | BuilderFileEmit.JsInlineMap;
+ if (getEmitDeclarations(options)) result = result | BuilderFileEmit.Dts;
+ if (options.declarationMap) result = result | BuilderFileEmit.DtsMap;
+ if (options.emitDeclarationOnly) result = result & BuilderFileEmit.AllDts;
+ return result;
+}
+
+/** @internal */
+/** Determing what all is pending to be emitted based on previous options or previous file emit flags */
+export function getPendingEmitKind(optionsOrEmitKind: CompilerOptions | BuilderFileEmit, oldOptionsOrEmitKind: CompilerOptions | BuilderFileEmit | undefined): BuilderFileEmit {
+ const oldEmitKind = oldOptionsOrEmitKind && (isNumber(oldOptionsOrEmitKind) ? oldOptionsOrEmitKind : getBuilderFileEmit(oldOptionsOrEmitKind));
+ const emitKind = isNumber(optionsOrEmitKind) ? optionsOrEmitKind : getBuilderFileEmit(optionsOrEmitKind);
+ if (oldEmitKind === emitKind) return BuilderFileEmit.None;
+ if (!oldEmitKind || !emitKind) return emitKind;
+ const diff = oldEmitKind ^ emitKind;
+ let result = BuilderFileEmit.None;
+ // If there is diff in Js emit, pending emit is js emit flags
+ if (diff & BuilderFileEmit.AllJs) result = emitKind & BuilderFileEmit.AllJs;
+ // If there is diff in Dts emit, pending emit is dts emit flags
+ if (diff & BuilderFileEmit.AllDts) result = result | (emitKind & BuilderFileEmit.AllDts);
+ return result;
+}
+
+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));
+}
+
+/**
+ * Create the state so that we can iterate on changedFiles/affected files
+ */
+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;
+ const outFilePath = outFile(compilerOptions);
+ // With --out or --outFile, any change affects all semantic diagnostics so no need to cache them
+ if (!outFilePath) {
+ state.semanticDiagnosticsPerFile = new Map();
+ }
+ else if (compilerOptions.composite && oldState?.outSignature && outFilePath === outFile(oldState?.compilerOptions)) {
+ state.outSignature = oldState.outSignature && getEmitSignatureFromOldSignature(compilerOptions, oldState.compilerOptions, oldState.outSignature);
+ }
+ state.changedFilesSet = new Set();
+ state.latestChangedDtsFile = compilerOptions.composite ? oldState?.latestChangedDtsFile : undefined;
+
+ const useOldState = BuilderState.canReuseOldState(state.referencedMap, oldState);
+ const oldCompilerOptions = useOldState ? oldState!.compilerOptions : undefined;
+ const canCopySemanticDiagnostics = useOldState && oldState!.semanticDiagnosticsPerFile && !!state.semanticDiagnosticsPerFile &&
+ !compilerOptionsAffectSemanticDiagnostics(compilerOptions, oldCompilerOptions!);
+ // We can only reuse emit signatures (i.e. .d.ts signatures) if the .d.ts file is unchanged,
+ // which will eg be depedent on change in options like declarationDir and outDir options are unchanged.
+ // We need to look in oldState.compilerOptions, rather than oldCompilerOptions (i.e.we need to disregard useOldState) because
+ // oldCompilerOptions can be undefined if there was change in say module from None to some other option
+ // which would make useOldState as false since we can now use reference maps that are needed to track what to emit, what to check etc
+ // but that option change does not affect d.ts file name so emitSignatures should still be reused.
+ const canCopyEmitSignatures = compilerOptions.composite &&
+ oldState?.emitSignatures &&
+ !outFilePath &&
+ !compilerOptionsAffectDeclarationPath(compilerOptions, oldState.compilerOptions);
+ if (useOldState) {
+ // Copy old state's changed files set
+ oldState!.changedFilesSet?.forEach(value => state.changedFilesSet.add(value));
+ if (!outFilePath && oldState!.affectedFilesPendingEmit?.size) {
+ state.affectedFilesPendingEmit = new Map(oldState!.affectedFilesPendingEmit);
+ state.seenAffectedFiles = new Set();
}
- else if (compilerOptions.composite && oldState?.outSignature && outFilePath === outFile(oldState?.compilerOptions)) {
- state.outSignature = oldState.outSignature && getEmitSignatureFromOldSignature(compilerOptions, oldState.compilerOptions, oldState.outSignature);
+ state.programEmitPending = oldState!.programEmitPending;
+ }
+ else {
+ // We arent using old state, so atleast emit buildInfo with current information
+ state.buildInfoEmitPending = true;
+ }
+
+ // 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 ||
+ // Implied formats dont match
+ oldInfo.impliedFormat !== info.impliedFormat ||
+ // 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
+ addFileToChangeSet(state, sourceFilePath);
}
- state.changedFilesSet = new Set();
- state.latestChangedDtsFile = compilerOptions.composite ? oldState?.latestChangedDtsFile : undefined;
-
- const useOldState = BuilderState.canReuseOldState(state.referencedMap, oldState);
- const oldCompilerOptions = useOldState ? oldState!.compilerOptions : undefined;
- const canCopySemanticDiagnostics = useOldState && oldState!.semanticDiagnosticsPerFile && !!state.semanticDiagnosticsPerFile &&
- !compilerOptionsAffectSemanticDiagnostics(compilerOptions, oldCompilerOptions!);
- // We can only reuse emit signatures (i.e. .d.ts signatures) if the .d.ts file is unchanged,
- // which will eg be depedent on change in options like declarationDir and outDir options are unchanged.
- // We need to look in oldState.compilerOptions, rather than oldCompilerOptions (i.e.we need to disregard useOldState) because
- // oldCompilerOptions can be undefined if there was change in say module from None to some other option
- // which would make useOldState as false since we can now use reference maps that are needed to track what to emit, what to check etc
- // but that option change does not affect d.ts file name so emitSignatures should still be reused.
- const canCopyEmitSignatures = compilerOptions.composite &&
- oldState?.emitSignatures &&
- !outFilePath &&
- !compilerOptionsAffectDeclarationPath(compilerOptions, oldState.compilerOptions);
- if (useOldState) {
- // Copy old state's changed files set
- oldState!.changedFilesSet?.forEach(value => state.changedFilesSet.add(value));
- if (!outFilePath && oldState!.affectedFilesPendingEmit?.size) {
- state.affectedFilesPendingEmit = new Map(oldState!.affectedFilesPendingEmit);
- state.seenAffectedFiles = new Set();
+ 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);
}
- state.programEmitPending = oldState!.programEmitPending;
}
- else {
- // We arent using old state, so atleast emit buildInfo with current information
- state.buildInfoEmitPending = true;
+ if (canCopyEmitSignatures) {
+ const oldEmitSignature = oldState.emitSignatures.get(sourceFilePath);
+ if (oldEmitSignature) {
+ (state.emitSignatures ??= new Map()).set(sourceFilePath, getEmitSignatureFromOldSignature(compilerOptions, oldState.compilerOptions, oldEmitSignature));
+ }
}
+ });
- // 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 ||
- // Implied formats dont match
- oldInfo.impliedFormat !== info.impliedFormat ||
- // 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
- addFileToChangeSet(state, 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();
+ // If the global file is removed, add all files as changed
+ if (useOldState && forEachEntry(oldState!.fileInfos, (info, sourceFilePath) => (outFilePath || info.affectsGlobalScope) && !state.fileInfos.has(sourceFilePath))) {
+ BuilderState.getAllFilesExcludingDefaultLibraryFile(state, newProgram, /*firstSourceFile*/ undefined)
+ .forEach(file => addFileToChangeSet(state, file.resolvedPath));
+ }
+ else if (oldCompilerOptions) {
+ // If options affect emit, then we need to do complete emit per compiler options
+ // otherwise only the js or dts that needs to emitted because its different from previously emitted options
+ const pendingEmitKind = compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions) ?
+ getBuilderFileEmit(compilerOptions) :
+ getPendingEmitKind(compilerOptions, oldCompilerOptions);
+ if (pendingEmitKind !== BuilderFileEmit.None) {
+ if (!outFilePath) {
+ // Add all files to affectedFilesPendingEmit since emit changed
+ newProgram.getSourceFiles().forEach(f => {
+ // Add to affectedFilesPending emit only if not changed since any changed file will do full emit
+ if (!state.changedFilesSet.has(f.resolvedPath)) {
+ addToAffectedFilesPendingEmit(
+ state,
+ f.resolvedPath,
+ pendingEmitKind
+ );
}
- state.semanticDiagnosticsFromOldState.add(sourceFilePath);
- }
- }
- if (canCopyEmitSignatures) {
- const oldEmitSignature = oldState.emitSignatures.get(sourceFilePath);
- if (oldEmitSignature) {
- (state.emitSignatures ??= new Map()).set(sourceFilePath, getEmitSignatureFromOldSignature(compilerOptions, oldState.compilerOptions, oldEmitSignature));
- }
+ });
+ Debug.assert(!state.seenAffectedFiles || !state.seenAffectedFiles.size);
+ state.seenAffectedFiles = state.seenAffectedFiles || new Set();
+ state.buildInfoEmitPending = true;
}
- });
-
- // If the global file is removed, add all files as changed
- if (useOldState && forEachEntry(oldState!.fileInfos, (info, sourceFilePath) => (outFilePath || info.affectsGlobalScope) && !state.fileInfos.has(sourceFilePath))) {
- BuilderState.getAllFilesExcludingDefaultLibraryFile(state, newProgram, /*firstSourceFile*/ undefined)
- .forEach(file => addFileToChangeSet(state, file.resolvedPath));
- }
- else if (oldCompilerOptions) {
- // If options affect emit, then we need to do complete emit per compiler options
- // otherwise only the js or dts that needs to emitted because its different from previously emitted options
- const pendingEmitKind = compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions) ?
- getBuilderFileEmit(compilerOptions) :
- getPendingEmitKind(compilerOptions, oldCompilerOptions);
- if (pendingEmitKind !== BuilderFileEmit.None) {
- if (!outFilePath) {
- // Add all files to affectedFilesPendingEmit since emit changed
- newProgram.getSourceFiles().forEach(f => {
- // Add to affectedFilesPending emit only if not changed since any changed file will do full emit
- if (!state.changedFilesSet.has(f.resolvedPath)) {
- addToAffectedFilesPendingEmit(
- state,
- f.resolvedPath,
- pendingEmitKind
- );
- }
- });
- Debug.assert(!state.seenAffectedFiles || !state.seenAffectedFiles.size);
- state.seenAffectedFiles = state.seenAffectedFiles || new Set();
- state.buildInfoEmitPending = true;
- }
- else {
- state.programEmitPending = state.programEmitPending ?
- state.programEmitPending | pendingEmitKind :
- pendingEmitKind;
- }
+ else {
+ state.programEmitPending = state.programEmitPending ?
+ state.programEmitPending | pendingEmitKind :
+ pendingEmitKind;
}
}
- if (outFilePath && !state.changedFilesSet.size) {
- // Copy the bundle information from old state so we can patch it later if we are doing partial emit
- if (useOldState) state.bundle = oldState!.bundle;
- // If this program has prepend references, always emit since we wont know if files on disk are correct unless we check file hash for correctness
- if (some(newProgram.getProjectReferences(), ref => !!ref.prepend)) state.programEmitPending = getBuilderFileEmit(compilerOptions);
- }
- return state;
}
-
- function addFileToChangeSet(state: BuilderProgramState, path: Path) {
- state.changedFilesSet.add(path);
- state.buildInfoEmitPending = true;
- // Setting this to undefined as changed files means full emit so no need to track emit explicitly
- state.programEmitPending = undefined;
+ if (outFilePath && !state.changedFilesSet.size) {
+ // Copy the bundle information from old state so we can patch it later if we are doing partial emit
+ if (useOldState) state.bundle = oldState!.bundle;
+ // If this program has prepend references, always emit since we wont know if files on disk are correct unless we check file hash for correctness
+ if (some(newProgram.getProjectReferences(), ref => !!ref.prepend)) state.programEmitPending = getBuilderFileEmit(compilerOptions);
}
+ return state;
+}
- /**
- * Covert to Emit signature based on oldOptions and EmitSignature format
- * If d.ts map options differ then swap the format, otherwise use as is
- */
- function getEmitSignatureFromOldSignature(options: CompilerOptions, oldOptions: CompilerOptions, oldEmitSignature: EmitSignature): EmitSignature {
- return !!options.declarationMap === !!oldOptions.declarationMap ?
- // Use same format of signature
- oldEmitSignature :
- // Convert to different format
- isString(oldEmitSignature) ? [oldEmitSignature] : oldEmitSignature[0];
- }
+function addFileToChangeSet(state: BuilderProgramState, path: Path) {
+ state.changedFilesSet.add(path);
+ state.buildInfoEmitPending = true;
+ // Setting this to undefined as changed files means full emit so no need to track emit explicitly
+ state.programEmitPending = undefined;
+}
- 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;
- });
+/**
+ * Covert to Emit signature based on oldOptions and EmitSignature format
+ * If d.ts map options differ then swap the format, otherwise use as is
+ */
+function getEmitSignatureFromOldSignature(options: CompilerOptions, oldOptions: CompilerOptions, oldEmitSignature: EmitSignature): EmitSignature {
+ return !!options.declarationMap === !!oldOptions.declarationMap ?
+ // Use same format of signature
+ oldEmitSignature :
+ // Convert to different format
+ isString(oldEmitSignature) ? [oldEmitSignature] : oldEmitSignature[0];
+}
- function toPath(path: string) {
- return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
- }
- }
+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;
+ });
- function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program, toPath: (path: string) => Path): DiagnosticRelatedInformation {
- const { file } = diagnostic;
- return {
- ...diagnostic,
- file: file ? newProgram.getSourceFileByPath(toPath(file)) : undefined
- };
+ function toPath(path: string) {
+ return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
}
+}
- /**
- * Releases program and other related not needed properties
- */
- function releaseCache(state: BuilderProgramState) {
- BuilderState.releaseCache(state);
- state.program = undefined;
- }
+function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program, toPath: (path: string) => Path): DiagnosticRelatedInformation {
+ const { file } = diagnostic;
+ return {
+ ...diagnostic,
+ file: file ? newProgram.getSourceFileByPath(toPath(file)) : undefined
+ };
+}
- function backupBuilderProgramEmitState(state: Readonly): SavedBuildProgramEmitState {
- const outFilePath = outFile(state.compilerOptions);
- // Only in --out changeFileSet is kept around till emit
- Debug.assert(!state.changedFilesSet.size || outFilePath);
- return {
- affectedFilesPendingEmit: state.affectedFilesPendingEmit && new Map(state.affectedFilesPendingEmit),
- seenEmittedFiles: state.seenEmittedFiles && new Map(state.seenEmittedFiles),
- programEmitPending: state.programEmitPending,
- emitSignatures: state.emitSignatures && new Map(state.emitSignatures),
- outSignature: state.outSignature,
- latestChangedDtsFile: state.latestChangedDtsFile,
- hasChangedEmitSignature: state.hasChangedEmitSignature,
- changedFilesSet: outFilePath ? new Set(state.changedFilesSet) : undefined,
- };
- }
+/**
+ * Releases program and other related not needed properties
+ */
+function releaseCache(state: BuilderProgramState) {
+ BuilderState.releaseCache(state);
+ state.program = undefined;
+}
- function restoreBuilderProgramEmitState(state: BuilderProgramState, savedEmitState: SavedBuildProgramEmitState) {
- state.affectedFilesPendingEmit = savedEmitState.affectedFilesPendingEmit;
- state.seenEmittedFiles = savedEmitState.seenEmittedFiles;
- state.programEmitPending = savedEmitState.programEmitPending;
- state.emitSignatures = savedEmitState.emitSignatures;
- state.outSignature = savedEmitState.outSignature;
- state.latestChangedDtsFile = savedEmitState.latestChangedDtsFile;
- state.hasChangedEmitSignature = savedEmitState.hasChangedEmitSignature;
- if (savedEmitState.changedFilesSet) state.changedFilesSet = savedEmitState.changedFilesSet;
- }
+function backupBuilderProgramEmitState(state: Readonly): SavedBuildProgramEmitState {
+ const outFilePath = outFile(state.compilerOptions);
+ // Only in --out changeFileSet is kept around till emit
+ Debug.assert(!state.changedFilesSet.size || outFilePath);
+ return {
+ affectedFilesPendingEmit: state.affectedFilesPendingEmit && new Map(state.affectedFilesPendingEmit),
+ seenEmittedFiles: state.seenEmittedFiles && new Map(state.seenEmittedFiles),
+ programEmitPending: state.programEmitPending,
+ emitSignatures: state.emitSignatures && new Map(state.emitSignatures),
+ outSignature: state.outSignature,
+ latestChangedDtsFile: state.latestChangedDtsFile,
+ hasChangedEmitSignature: state.hasChangedEmitSignature,
+ changedFilesSet: outFilePath ? new Set(state.changedFilesSet) : undefined,
+ };
+}
- /**
- * 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));
- }
+function restoreBuilderProgramEmitState(state: BuilderProgramState, savedEmitState: SavedBuildProgramEmitState) {
+ state.affectedFilesPendingEmit = savedEmitState.affectedFilesPendingEmit;
+ state.seenEmittedFiles = savedEmitState.seenEmittedFiles;
+ state.programEmitPending = savedEmitState.programEmitPending;
+ state.emitSignatures = savedEmitState.emitSignatures;
+ state.outSignature = savedEmitState.outSignature;
+ state.latestChangedDtsFile = savedEmitState.latestChangedDtsFile;
+ state.hasChangedEmitSignature = savedEmitState.hasChangedEmitSignature;
+ if (savedEmitState.changedFilesSet) state.changedFilesSet = savedEmitState.changedFilesSet;
+}
- /**
- * 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,
- getCanonicalFileName: GetCanonicalFileName,
- host: BuilderProgramHost
- ): 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;
- addToAffectedFilesPendingEmit(state, affectedFile.resolvedPath, getBuilderFileEmit(state.compilerOptions));
- handleDtsMayChangeOfAffectedFile(
- state,
- affectedFile,
- cancellationToken,
- computeHash,
- getCanonicalFileName,
- host
- );
- return affectedFile;
- }
- affectedFilesIndex++;
- }
+/**
+ * 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));
+}
- // Remove the changed file from the change set
- state.changedFilesSet.delete(state.currentChangedFilePath!);
- state.currentChangedFilePath = undefined;
- // Commit the changes in file signature
- state.oldSignatures?.clear();
- state.oldExportedModulesMap?.clear();
- state.affectedFiles = 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,
+ getCanonicalFileName: GetCanonicalFileName,
+ host: BuilderProgramHost
+): 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;
+ addToAffectedFilesPendingEmit(state, affectedFile.resolvedPath, getBuilderFileEmit(state.compilerOptions));
+ handleDtsMayChangeOfAffectedFile(
+ state,
+ affectedFile,
+ cancellationToken,
+ computeHash,
+ getCanonicalFileName,
+ host
+ );
+ return affectedFile;
+ }
+ affectedFilesIndex++;
}
- // Get next changed file
- const nextKey = state.changedFilesSet.keys().next();
- if (nextKey.done) {
- // Done
- return undefined;
- }
+ // Remove the changed file from the change set
+ state.changedFilesSet.delete(state.currentChangedFilePath!);
+ state.currentChangedFilePath = undefined;
+ // Commit the changes in file signature
+ state.oldSignatures?.clear();
+ state.oldExportedModulesMap?.clear();
+ state.affectedFiles = undefined;
+ }
- // 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;
- }
+ // Get next changed file
+ const nextKey = state.changedFilesSet.keys().next();
+ if (nextKey.done) {
+ // Done
+ return undefined;
+ }
- // Get next batch of affected files
- state.affectedFiles = BuilderState.getFilesAffectedByWithOldState(
- state,
- program,
- nextKey.value,
- cancellationToken,
- computeHash,
- getCanonicalFileName,
- );
- state.currentChangedFilePath = nextKey.value;
- state.affectedFilesIndex = 0;
- if (!state.seenAffectedFiles) state.seenAffectedFiles = new Set();
+ // 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;
}
- }
- function clearAffectedFilesPendingEmit(state: BuilderProgramState, emitOnlyDtsFiles: boolean | undefined) {
- if (!state.affectedFilesPendingEmit?.size) return;
- if (!emitOnlyDtsFiles) return state.affectedFilesPendingEmit = undefined;
- state.affectedFilesPendingEmit.forEach((emitKind, path) => {
- // Mark the files as pending only if they are pending on js files, remove the dts emit pending flag
- const pending = emitKind & BuilderFileEmit.AllJs;
- if (!pending) state.affectedFilesPendingEmit!.delete(path);
- else state.affectedFilesPendingEmit!.set(path, pending);
- });
+ // Get next batch of affected files
+ state.affectedFiles = BuilderState.getFilesAffectedByWithOldState(
+ state,
+ program,
+ nextKey.value,
+ cancellationToken,
+ computeHash,
+ getCanonicalFileName,
+ );
+ state.currentChangedFilePath = nextKey.value;
+ state.affectedFilesIndex = 0;
+ if (!state.seenAffectedFiles) state.seenAffectedFiles = new Set();
}
+}
- /**
- * Returns next file to be emitted from files that retrieved semantic diagnostics but did not emit yet
- */
- function getNextAffectedFilePendingEmit(state: BuilderProgramState, emitOnlyDtsFiles: boolean | undefined) {
- if (!state.affectedFilesPendingEmit?.size) return undefined;
- return forEachEntry(state.affectedFilesPendingEmit, (emitKind, path) => {
- const affectedFile = state.program!.getSourceFileByPath(path);
- if (!affectedFile || !sourceFileMayBeEmitted(affectedFile, state.program!)) {
- state.affectedFilesPendingEmit!.delete(path);
- return undefined;
- }
- const seenKind = state.seenEmittedFiles?.get(affectedFile.resolvedPath);
- let pendingKind = getPendingEmitKind(emitKind, seenKind);
- if (emitOnlyDtsFiles) pendingKind = pendingKind & BuilderFileEmit.AllDts;
- if (pendingKind) return { affectedFile, emitKind: pendingKind };
- });
- }
+function clearAffectedFilesPendingEmit(state: BuilderProgramState, emitOnlyDtsFiles: boolean | undefined) {
+ if (!state.affectedFilesPendingEmit?.size) return;
+ if (!emitOnlyDtsFiles) return state.affectedFilesPendingEmit = undefined;
+ state.affectedFilesPendingEmit.forEach((emitKind, path) => {
+ // Mark the files as pending only if they are pending on js files, remove the dts emit pending flag
+ const pending = emitKind & BuilderFileEmit.AllJs;
+ if (!pending) state.affectedFilesPendingEmit!.delete(path);
+ else state.affectedFilesPendingEmit!.set(path, pending);
+ });
+}
- function removeDiagnosticsOfLibraryFiles(state: BuilderProgramState) {
- 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)
- );
+/**
+ * Returns next file to be emitted from files that retrieved semantic diagnostics but did not emit yet
+ */
+function getNextAffectedFilePendingEmit(state: BuilderProgramState, emitOnlyDtsFiles: boolean | undefined) {
+ if (!state.affectedFilesPendingEmit?.size) return undefined;
+ return forEachEntry(state.affectedFilesPendingEmit, (emitKind, path) => {
+ const affectedFile = state.program!.getSourceFileByPath(path);
+ if (!affectedFile || !sourceFileMayBeEmitted(affectedFile, state.program!)) {
+ state.affectedFilesPendingEmit!.delete(path);
+ return undefined;
}
+ const seenKind = state.seenEmittedFiles?.get(affectedFile.resolvedPath);
+ let pendingKind = getPendingEmitKind(emitKind, seenKind);
+ if (emitOnlyDtsFiles) pendingKind = pendingKind & BuilderFileEmit.AllDts;
+ if (pendingKind) return { affectedFile, emitKind: pendingKind };
+ });
+}
+
+function removeDiagnosticsOfLibraryFiles(state: BuilderProgramState) {
+ 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)
+ );
}
+}
- /**
- * 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,
- getCanonicalFileName: GetCanonicalFileName,
- host: BuilderProgramHost,
- ) {
- removeSemanticDiagnosticsOf(state, affectedFile.resolvedPath);
-
- // If affected files is everything except default library, then nothing more to do
- if (state.allFilesExcludingDefaultLibraryFile === state.affectedFiles) {
- removeDiagnosticsOfLibraryFiles(state);
- // 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,
- cancellationToken,
- computeHash,
- getCanonicalFileName,
- );
- return;
- }
- if (state.compilerOptions.assumeChangesOnlyAffectDirectDependencies) return;
- handleDtsMayChangeOfReferencingExportOfAffectedFile(
+/**
+ * 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,
+ getCanonicalFileName: GetCanonicalFileName,
+ host: BuilderProgramHost,
+) {
+ removeSemanticDiagnosticsOf(state, affectedFile.resolvedPath);
+
+ // If affected files is everything except default library, then nothing more to do
+ if (state.allFilesExcludingDefaultLibraryFile === state.affectedFiles) {
+ removeDiagnosticsOfLibraryFiles(state);
+ // 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,
cancellationToken,
computeHash,
getCanonicalFileName,
- host,
);
+ return;
}
+ if (state.compilerOptions.assumeChangesOnlyAffectDirectDependencies) return;
+ handleDtsMayChangeOfReferencingExportOfAffectedFile(
+ state,
+ affectedFile,
+ cancellationToken,
+ computeHash,
+ getCanonicalFileName,
+ host,
+ );
+}
- /**
- * 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,
- getCanonicalFileName: GetCanonicalFileName,
- host: BuilderProgramHost
- ): 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,
- cancellationToken,
- computeHash,
- getCanonicalFileName,
- !host.disableUseFileVersionAsSignature
- );
- // If not dts emit, nothing more to do
- if (getEmitDeclarations(state.compilerOptions)) {
- addToAffectedFilesPendingEmit(state, path, state.compilerOptions.declarationMap ? BuilderFileEmit.AllDts : BuilderFileEmit.Dts);
- }
+/**
+ * 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,
+ getCanonicalFileName: GetCanonicalFileName,
+ host: BuilderProgramHost
+): 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,
+ cancellationToken,
+ computeHash,
+ getCanonicalFileName,
+ !host.disableUseFileVersionAsSignature
+ );
+ // If not dts emit, nothing more to do
+ if (getEmitDeclarations(state.compilerOptions)) {
+ addToAffectedFilesPendingEmit(state, path, state.compilerOptions.declarationMap ? BuilderFileEmit.AllDts : BuilderFileEmit.Dts);
}
}
}
+}
- /**
- * 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;
- }
- state.semanticDiagnosticsFromOldState.delete(path);
- state.semanticDiagnosticsPerFile!.delete(path);
- return !state.semanticDiagnosticsFromOldState.size;
+/**
+ * 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;
}
+ state.semanticDiagnosticsFromOldState.delete(path);
+ state.semanticDiagnosticsPerFile!.delete(path);
+ return !state.semanticDiagnosticsFromOldState.size;
+}
- function isChangedSignature(state: BuilderProgramState, path: Path) {
- const oldSignature = Debug.checkDefined(state.oldSignatures).get(path) || undefined;
- const newSignature = Debug.checkDefined(state.fileInfos.get(path)).signature;
- return newSignature !== oldSignature;
- }
+function isChangedSignature(state: BuilderProgramState, path: Path) {
+ const oldSignature = Debug.checkDefined(state.oldSignatures).get(path) || undefined;
+ const newSignature = Debug.checkDefined(state.fileInfos.get(path)).signature;
+ return newSignature !== oldSignature;
+}
- function handleDtsMayChangeOfGlobalScope(
- state: BuilderProgramState,
- filePath: Path,
- cancellationToken: CancellationToken | undefined,
- computeHash: BuilderState.ComputeHash,
- getCanonicalFileName: GetCanonicalFileName,
- host: BuilderProgramHost,
- ): boolean {
- if (!state.fileInfos.get(filePath)?.affectsGlobalScope) return false;
- // Every file needs to be handled
- BuilderState.getAllFilesExcludingDefaultLibraryFile(state, state.program!, /*firstSourceFile*/ undefined)
- .forEach(file => handleDtsMayChangeOf(
- state,
- file.resolvedPath,
- cancellationToken,
- computeHash,
- getCanonicalFileName,
- host,
- ));
- removeDiagnosticsOfLibraryFiles(state);
- return true;
- }
+function handleDtsMayChangeOfGlobalScope(
+ state: BuilderProgramState,
+ filePath: Path,
+ cancellationToken: CancellationToken | undefined,
+ computeHash: BuilderState.ComputeHash,
+ getCanonicalFileName: GetCanonicalFileName,
+ host: BuilderProgramHost,
+): boolean {
+ if (!state.fileInfos.get(filePath)?.affectsGlobalScope) return false;
+ // Every file needs to be handled
+ BuilderState.getAllFilesExcludingDefaultLibraryFile(state, state.program!, /*firstSourceFile*/ undefined)
+ .forEach(file => handleDtsMayChangeOf(
+ state,
+ file.resolvedPath,
+ cancellationToken,
+ computeHash,
+ getCanonicalFileName,
+ host,
+ ));
+ removeDiagnosticsOfLibraryFiles(state);
+ return true;
+}
- /**
- * Iterate on referencing modules that export entities from affected file and delete diagnostics and add pending emit
- */
- function handleDtsMayChangeOfReferencingExportOfAffectedFile(
- state: BuilderProgramState,
- affectedFile: SourceFile,
- cancellationToken: CancellationToken | undefined,
- computeHash: BuilderState.ComputeHash,
- getCanonicalFileName: GetCanonicalFileName,
- host: BuilderProgramHost
- ) {
- // 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 (!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);
- if (handleDtsMayChangeOfGlobalScope(state, currentPath, cancellationToken, computeHash, getCanonicalFileName, host)) return;
- handleDtsMayChangeOf(state, currentPath, cancellationToken, computeHash, getCanonicalFileName, host);
- if (isChangedSignature(state, currentPath)) {
- const currentSourceFile = Debug.checkDefined(state.program).getSourceFileByPath(currentPath)!;
- queue.push(...BuilderState.getReferencedByPaths(state, currentSourceFile.resolvedPath));
- }
+/**
+ * Iterate on referencing modules that export entities from affected file and delete diagnostics and add pending emit
+ */
+function handleDtsMayChangeOfReferencingExportOfAffectedFile(
+ state: BuilderProgramState,
+ affectedFile: SourceFile,
+ cancellationToken: CancellationToken | undefined,
+ computeHash: BuilderState.ComputeHash,
+ getCanonicalFileName: GetCanonicalFileName,
+ host: BuilderProgramHost
+) {
+ // 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 (!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);
+ if (handleDtsMayChangeOfGlobalScope(state, currentPath, cancellationToken, computeHash, getCanonicalFileName, host)) return;
+ handleDtsMayChangeOf(state, currentPath, cancellationToken, computeHash, getCanonicalFileName, host);
+ if (isChangedSignature(state, currentPath)) {
+ const currentSourceFile = Debug.checkDefined(state.program).getSourceFileByPath(currentPath)!;
+ queue.push(...BuilderState.getReferencedByPaths(state, currentSourceFile.resolvedPath));
}
}
}
-
- 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.exportedModulesMap.getKeys(affectedFile.resolvedPath)?.forEach(exportedFromPath => {
- if (handleDtsMayChangeOfGlobalScope(state, exportedFromPath, cancellationToken, computeHash, getCanonicalFileName, host)) return true;
- const references = state.referencedMap!.getKeys(exportedFromPath);
- return references && forEachKey(references, filePath =>
- handleDtsMayChangeOfFileAndExportsOfFile(
- state,
- filePath,
- seenFileAndExportsOfFile,
- cancellationToken,
- computeHash,
- getCanonicalFileName,
- host,
- )
- );
- });
}
- /**
- * handle dts and semantic diagnostics on file and iterate on anything that exports this file
- * return true when all work is done and we can exit handling dts emit and semantic diagnostics
- */
- function handleDtsMayChangeOfFileAndExportsOfFile(
- state: BuilderProgramState,
- filePath: Path,
- seenFileAndExportsOfFile: Set,
- cancellationToken: CancellationToken | undefined,
- computeHash: BuilderState.ComputeHash,
- getCanonicalFileName: GetCanonicalFileName,
- host: BuilderProgramHost,
- ): boolean | undefined {
- if (!tryAddToSet(seenFileAndExportsOfFile, filePath)) return undefined;
-
- if (handleDtsMayChangeOfGlobalScope(state, filePath, cancellationToken, computeHash, getCanonicalFileName, host)) return true;
- handleDtsMayChangeOf(state, filePath, cancellationToken, computeHash, getCanonicalFileName, host);
-
- // If exported modules has path, all files referencing file exported from are affected
- state.exportedModulesMap!.getKeys(filePath)?.forEach(exportedFromPath =>
+ 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.exportedModulesMap.getKeys(affectedFile.resolvedPath)?.forEach(exportedFromPath => {
+ if (handleDtsMayChangeOfGlobalScope(state, exportedFromPath, cancellationToken, computeHash, getCanonicalFileName, host)) return true;
+ const references = state.referencedMap!.getKeys(exportedFromPath);
+ return references && forEachKey(references, filePath =>
handleDtsMayChangeOfFileAndExportsOfFile(
state,
- exportedFromPath,
+ filePath,
seenFileAndExportsOfFile,
cancellationToken,
computeHash,
@@ -740,969 +733,1029 @@ namespace ts {
host,
)
);
+ });
+}
- // 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
- handleDtsMayChangeOf( // Dont add to seen since this is not yet done with the export removal
- state,
- referencingFilePath,
- cancellationToken,
- computeHash,
- getCanonicalFileName,
- host,
- )
- );
- return undefined;
- }
+/**
+ * handle dts and semantic diagnostics on file and iterate on anything that exports this file
+ * return true when all work is done and we can exit handling dts emit and semantic diagnostics
+ */
+function handleDtsMayChangeOfFileAndExportsOfFile(
+ state: BuilderProgramState,
+ filePath: Path,
+ seenFileAndExportsOfFile: Set,
+ cancellationToken: CancellationToken | undefined,
+ computeHash: BuilderState.ComputeHash,
+ getCanonicalFileName: GetCanonicalFileName,
+ host: BuilderProgramHost,
+): boolean | undefined {
+ if (!tryAddToSet(seenFileAndExportsOfFile, filePath)) return undefined;
+
+ if (handleDtsMayChangeOfGlobalScope(state, filePath, cancellationToken, computeHash, getCanonicalFileName, host)) return true;
+ handleDtsMayChangeOf(state, filePath, cancellationToken, computeHash, getCanonicalFileName, host);
+
+ // If exported modules has path, all files referencing file exported from are affected
+ state.exportedModulesMap!.getKeys(filePath)?.forEach(exportedFromPath =>
+ handleDtsMayChangeOfFileAndExportsOfFile(
+ state,
+ exportedFromPath,
+ seenFileAndExportsOfFile,
+ cancellationToken,
+ computeHash,
+ getCanonicalFileName,
+ host,
+ )
+ );
- /**
- * 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)
- );
- }
+ // 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
+ handleDtsMayChangeOf( // Dont add to seen since this is not yet done with the export removal
+ state,
+ referencingFilePath,
+ cancellationToken,
+ computeHash,
+ getCanonicalFileName,
+ host,
+ )
+ );
+ return undefined;
+}
- /**
- * 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
+ */
+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
+ */
+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[]];
- /**
- * fileId if pending emit is same as what compilerOptions suggest
- * [fileId] if pending emit is only dts file emit
- * [fileId, emitKind] if any other type emit is pending
- */
- export type ProgramBuilderInfoFilePendingEmit = ProgramBuildInfoFileId | [fileId: ProgramBuildInfoFileId] | [fileId: ProgramBuildInfoFileId, emitKind: BuilderFileEmit];
- export type ProgramBuildInfoReferencedMap = [fileId: ProgramBuildInfoFileId, fileIdListId: ProgramBuildInfoFileIdListId][];
- export type ProgramMultiFileEmitBuildInfoBuilderStateFileInfo = 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;
- };
- /**
- * [fileId, signature] if different from file's signature
- * fileId if file wasnt emitted
- */
- export type ProgramBuildInfoEmitSignature = ProgramBuildInfoFileId | [fileId: ProgramBuildInfoFileId, signature: EmitSignature | []];
- /**
- * ProgramMultiFileEmitBuildInfoFileInfo is string if FileInfo.version === FileInfo.signature && !FileInfo.affectsGlobalScope otherwise encoded FileInfo
- */
- export type ProgramMultiFileEmitBuildInfoFileInfo = string | ProgramMultiFileEmitBuildInfoBuilderStateFileInfo;
- export interface ProgramMultiFileEmitBuildInfo {
- fileNames: readonly string[];
- fileInfos: readonly ProgramMultiFileEmitBuildInfoFileInfo[];
- options: CompilerOptions | undefined;
- fileIdsList: readonly (readonly ProgramBuildInfoFileId[])[] | undefined;
- referencedMap: ProgramBuildInfoReferencedMap | undefined;
- exportedModulesMap: ProgramBuildInfoReferencedMap | undefined;
- semanticDiagnosticsPerFile: ProgramBuildInfoDiagnostic[] | undefined;
- affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] | undefined;
- changeFileSet: readonly ProgramBuildInfoFileId[] | undefined;
- emitSignatures: readonly ProgramBuildInfoEmitSignature[] | undefined;
- // Because this is only output file in the program, we dont need fileId to deduplicate name
- latestChangedDtsFile?: string | undefined;
+ // 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 */
+/**
+ * fileId if pending emit is same as what compilerOptions suggest
+ * [fileId] if pending emit is only dts file emit
+ * [fileId, emitKind] if any other type emit is pending
+ */
+export type ProgramBuilderInfoFilePendingEmit = ProgramBuildInfoFileId | [fileId: ProgramBuildInfoFileId] | [fileId: ProgramBuildInfoFileId, emitKind: BuilderFileEmit];
+/** @internal */
+export type ProgramBuildInfoReferencedMap = [fileId: ProgramBuildInfoFileId, fileIdListId: ProgramBuildInfoFileIdListId][];
+/** @internal */
+export type ProgramMultiFileEmitBuildInfoBuilderStateFileInfo = Omit & {
/**
- * ProgramBundleEmitBuildInfoFileInfo is string if !FileInfo.impliedFormat otherwise encoded FileInfo
- */
- export type ProgramBundleEmitBuildInfoFileInfo = string | BuilderState.FileInfo;
- /**
- * false if it is the emit corresponding to compilerOptions
- * value otherwise
+ * Signature is
+ * - undefined if FileInfo.version === FileInfo.signature
+ * - false if FileInfo has signature as undefined (not calculated)
+ * - string actual signature
*/
- export type ProgramBuildInfoBundlePendingEmit = BuilderFileEmit | false;
- export interface ProgramBundleEmitBuildInfo {
- fileNames: readonly string[];
- fileInfos: readonly ProgramBundleEmitBuildInfoFileInfo[];
- options: CompilerOptions | undefined;
- outSignature: EmitSignature | undefined;
- latestChangedDtsFile: string | undefined;
- pendingEmit: ProgramBuildInfoBundlePendingEmit | undefined;
- }
-
- export type ProgramBuildInfo = ProgramMultiFileEmitBuildInfo | ProgramBundleEmitBuildInfo;
+ signature: string | false | undefined;
+};
+/** @internal */
+/**
+ * [fileId, signature] if different from file's signature
+ * fileId if file wasnt emitted
+ */
+export type ProgramBuildInfoEmitSignature = ProgramBuildInfoFileId | [fileId: ProgramBuildInfoFileId, signature: EmitSignature | []];
+/** @internal */
+/**
+ * ProgramMultiFileEmitBuildInfoFileInfo is string if FileInfo.version === FileInfo.signature && !FileInfo.affectsGlobalScope otherwise encoded FileInfo
+ */
+export type ProgramMultiFileEmitBuildInfoFileInfo = string | ProgramMultiFileEmitBuildInfoBuilderStateFileInfo;
+/** @internal */
+export interface ProgramMultiFileEmitBuildInfo {
+ fileNames: readonly string[];
+ fileInfos: readonly ProgramMultiFileEmitBuildInfoFileInfo[];
+ options: CompilerOptions | undefined;
+ fileIdsList: readonly (readonly ProgramBuildInfoFileId[])[] | undefined;
+ referencedMap: ProgramBuildInfoReferencedMap | undefined;
+ exportedModulesMap: ProgramBuildInfoReferencedMap | undefined;
+ semanticDiagnosticsPerFile: ProgramBuildInfoDiagnostic[] | undefined;
+ affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] | undefined;
+ changeFileSet: readonly ProgramBuildInfoFileId[] | undefined;
+ emitSignatures: readonly ProgramBuildInfoEmitSignature[] | undefined;
+ // Because this is only output file in the program, we dont need fileId to deduplicate name
+ latestChangedDtsFile?: string | undefined;
+}
+/** @internal */
+/**
+ * ProgramBundleEmitBuildInfoFileInfo is string if !FileInfo.impliedFormat otherwise encoded FileInfo
+ */
+export type ProgramBundleEmitBuildInfoFileInfo = string | BuilderState.FileInfo;
+/** @internal */
+/**
+ * false if it is the emit corresponding to compilerOptions
+ * value otherwise
+ */
+export type ProgramBuildInfoBundlePendingEmit = BuilderFileEmit | false;
+/** @internal */
+export interface ProgramBundleEmitBuildInfo {
+ fileNames: readonly string[];
+ fileInfos: readonly ProgramBundleEmitBuildInfoFileInfo[];
+ options: CompilerOptions | undefined;
+ outSignature: EmitSignature | undefined;
+ latestChangedDtsFile: string | undefined;
+ pendingEmit: ProgramBuildInfoBundlePendingEmit | undefined;
+}
- export function isProgramBundleEmitBuildInfo(info: ProgramBuildInfo): info is ProgramBundleEmitBuildInfo {
- return !!outFile(info.options || {});
- }
+/** @internal */
+export type ProgramBuildInfo = ProgramMultiFileEmitBuildInfo | ProgramBundleEmitBuildInfo;
- /**
- * Gets the program information to be emitted in buildInfo so that we can use it to create new program
- */
- function getBuildInfo(state: BuilderProgramState, getCanonicalFileName: GetCanonicalFileName, bundle: BundleBuildInfo | undefined): BuildInfo {
- const currentDirectory = Debug.checkDefined(state.program).getCurrentDirectory();
- const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(state.compilerOptions)!, currentDirectory));
- // Convert the file name to Path here if we set the fileName instead to optimize multiple d.ts file emits and having to compute Canonical path
- const latestChangedDtsFile = state.latestChangedDtsFile ? relativeToBuildInfoEnsuringAbsolutePath(state.latestChangedDtsFile) : undefined;
- const fileNames: string[] = [];
- const fileNameToFileId = new Map();
- if (outFile(state.compilerOptions)) {
- // Copy all fileInfo, version and impliedFormat
- // Affects global scope and signature doesnt matter because with --out they arent calculated or needed to determine upto date ness
- const fileInfos = arrayFrom(state.fileInfos.entries(), ([key, value]): ProgramBundleEmitBuildInfoFileInfo => {
- // Ensure fileId
- toFileId(key);
- return value.impliedFormat ?
- { version: value.version, impliedFormat: value.impliedFormat, signature: undefined, affectsGlobalScope: undefined } :
- value.version;
- });
- const program: ProgramBundleEmitBuildInfo = {
- fileNames,
- fileInfos,
- options: convertToProgramBuildInfoCompilerOptions(state.compilerOptions),
- outSignature: state.outSignature,
- latestChangedDtsFile,
- pendingEmit: !state.programEmitPending ?
- undefined : // Pending is undefined or None is encoded as undefined
- state.programEmitPending === getBuilderFileEmit(state.compilerOptions) ?
- false : // Pending emit is same as deteremined by compilerOptions
- state.programEmitPending, // Actual value
- };
- // Complete the bundle information if we are doing partial emit (only js or only dts)
- const { js, dts, commonSourceDirectory, sourceFiles } = bundle!;
- state.bundle = bundle = {
- commonSourceDirectory,
- sourceFiles,
- js: js || (!state.compilerOptions.emitDeclarationOnly ? state.bundle?.js : undefined),
- dts: dts || (getEmitDeclarations(state.compilerOptions) ? state.bundle?.dts : undefined),
- };
- return createBuildInfo(program, bundle);
- }
+/** @internal */
+export function isProgramBundleEmitBuildInfo(info: ProgramBuildInfo): info is ProgramBundleEmitBuildInfo {
+ return !!outFile(info.options || {});
+}
- let fileIdsList: (readonly ProgramBuildInfoFileId[])[] | undefined;
- let fileNamesToFileIdListId: ESMap | undefined;
- let emitSignatures: ProgramBuildInfoEmitSignature[] | undefined;
- const fileInfos = arrayFrom(state.fileInfos.entries(), ([key, value]): ProgramMultiFileEmitBuildInfoFileInfo => {
+/**
+ * Gets the program information to be emitted in buildInfo so that we can use it to create new program
+ */
+function getBuildInfo(state: BuilderProgramState, getCanonicalFileName: GetCanonicalFileName, bundle: BundleBuildInfo | undefined): BuildInfo {
+ const currentDirectory = Debug.checkDefined(state.program).getCurrentDirectory();
+ const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(state.compilerOptions)!, currentDirectory));
+ // Convert the file name to Path here if we set the fileName instead to optimize multiple d.ts file emits and having to compute Canonical path
+ const latestChangedDtsFile = state.latestChangedDtsFile ? relativeToBuildInfoEnsuringAbsolutePath(state.latestChangedDtsFile) : undefined;
+ const fileNames: string[] = [];
+ const fileNameToFileId = new Map();
+ if (outFile(state.compilerOptions)) {
+ // Copy all fileInfo, version and impliedFormat
+ // Affects global scope and signature doesnt matter because with --out they arent calculated or needed to determine upto date ness
+ const fileInfos = arrayFrom(state.fileInfos.entries(), ([key, value]): ProgramBundleEmitBuildInfoFileInfo => {
// Ensure fileId
- const fileId = toFileId(key);
- Debug.assert(fileNames[fileId - 1] === relativeToBuildInfo(key));
- const oldSignature = state.oldSignatures?.get(key);
- const actualSignature = oldSignature !== undefined ? oldSignature || undefined : value.signature;
- if (state.compilerOptions.composite) {
- const file = state.program!.getSourceFileByPath(key)!;
- if (!isJsonSourceFile(file) && sourceFileMayBeEmitted(file, state.program!)) {
- const emitSignature = state.emitSignatures?.get(key);
- if (emitSignature !== actualSignature) {
- (emitSignatures ||= []).push(emitSignature === undefined ?
- fileId : // There is no emit, encode as false
- // fileId, signature: emptyArray if signature only differs in dtsMap option than our own compilerOptions otherwise EmitSignature
- [fileId, !isString(emitSignature) && emitSignature[0] === actualSignature ? emptyArray as [] : emitSignature]);
- }
- }
- }
- return value.version === actualSignature ?
- value.affectsGlobalScope || value.impliedFormat ?
- // If file version is same as signature, dont serialize signature
- { version: value.version, signature: undefined, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat } :
- // If file info only contains version and signature and both are same we can just write string
- value.version :
- actualSignature !== undefined ? // If signature is not same as version, encode signature in the fileInfo
- oldSignature === undefined ?
- // If we havent computed signature, use fileInfo as is
- value :
- // Serialize fileInfo with new updated signature
- { version: value.version, signature: actualSignature, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat } :
- // Signature of the FileInfo is undefined, serialize it as false
- { version: value.version, signature: false, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat };
+ toFileId(key);
+ return value.impliedFormat ?
+ { version: value.version, impliedFormat: value.impliedFormat, signature: undefined, affectsGlobalScope: undefined } :
+ value.version;
});
-
- 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 => {
- const oldValue = state.oldExportedModulesMap?.get(key);
- // Not in temporary cache, use existing value
- if (oldValue === undefined) return [toFileId(key), toFileIdListId(state.exportedModulesMap!.getValues(key)!)];
- if (oldValue) return [toFileId(key), toFileIdListId(oldValue)];
- return undefined;
- });
- }
-
- 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),
- convertToReusableDiagnostics(value, relativeToBuildInfo)
- ] :
- toFileId(key)
- );
- }
- }
-
- let affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] | undefined;
- if (state.affectedFilesPendingEmit?.size) {
- const fullEmitForOptions = getBuilderFileEmit(state.compilerOptions);
- const seenFiles = new Set();
- for (const path of arrayFrom(state.affectedFilesPendingEmit.keys()).sort(compareStringsCaseSensitive)) {
- if (tryAddToSet(seenFiles, path)) {
- const file = state.program!.getSourceFileByPath(path)!;
- if (!sourceFileMayBeEmitted(file, state.program!)) continue;
- const fileId = toFileId(path), pendingEmit = state.affectedFilesPendingEmit.get(path)!;
- (affectedFilesPendingEmit ||= []).push(
- pendingEmit === fullEmitForOptions ?
- fileId : // Pending full emit per options
- pendingEmit === BuilderFileEmit.Dts ?
- [fileId] : // Pending on Dts only
- [fileId, pendingEmit] // Anything else
- );
- }
- }
- }
-
- let changeFileSet: ProgramBuildInfoFileId[] | undefined;
- if (state.changedFilesSet.size) {
- for (const path of arrayFrom(state.changedFilesSet.keys()).sort(compareStringsCaseSensitive)) {
- (changeFileSet ||= []).push(toFileId(path));
- }
- }
-
- const program: ProgramMultiFileEmitBuildInfo = {
+ const program: ProgramBundleEmitBuildInfo = {
fileNames,
fileInfos,
options: convertToProgramBuildInfoCompilerOptions(state.compilerOptions),
- fileIdsList,
- referencedMap,
- exportedModulesMap,
- semanticDiagnosticsPerFile,
- affectedFilesPendingEmit,
- changeFileSet,
- emitSignatures,
+ outSignature: state.outSignature,
latestChangedDtsFile,
+ pendingEmit: !state.programEmitPending ?
+ undefined : // Pending is undefined or None is encoded as undefined
+ state.programEmitPending === getBuilderFileEmit(state.compilerOptions) ?
+ false : // Pending emit is same as deteremined by compilerOptions
+ state.programEmitPending, // Actual value
+ };
+ // Complete the bundle information if we are doing partial emit (only js or only dts)
+ const { js, dts, commonSourceDirectory, sourceFiles } = bundle!;
+ state.bundle = bundle = {
+ commonSourceDirectory,
+ sourceFiles,
+ js: js || (!state.compilerOptions.emitDeclarationOnly ? state.bundle?.js : undefined),
+ dts: dts || (getEmitDeclarations(state.compilerOptions) ? state.bundle?.dts : undefined),
};
return createBuildInfo(program, bundle);
+ }
- function relativeToBuildInfoEnsuringAbsolutePath(path: string) {
- return relativeToBuildInfo(getNormalizedAbsolutePath(path, currentDirectory));
- }
-
- 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);
+ let fileIdsList: (readonly ProgramBuildInfoFileId[])[] | undefined;
+ let fileNamesToFileIdListId: ESMap | undefined;
+ let emitSignatures: ProgramBuildInfoEmitSignature[] | undefined;
+ const fileInfos = arrayFrom(state.fileInfos.entries(), ([key, value]): ProgramMultiFileEmitBuildInfoFileInfo => {
+ // Ensure fileId
+ const fileId = toFileId(key);
+ Debug.assert(fileNames[fileId - 1] === relativeToBuildInfo(key));
+ const oldSignature = state.oldSignatures?.get(key);
+ const actualSignature = oldSignature !== undefined ? oldSignature || undefined : value.signature;
+ if (state.compilerOptions.composite) {
+ const file = state.program!.getSourceFileByPath(key)!;
+ if (!isJsonSourceFile(file) && sourceFileMayBeEmitted(file, state.program!)) {
+ const emitSignature = state.emitSignatures?.get(key);
+ if (emitSignature !== actualSignature) {
+ (emitSignatures ||= []).push(emitSignature === undefined ?
+ fileId : // There is no emit, encode as false
+ // fileId, signature: emptyArray if signature only differs in dtsMap option than our own compilerOptions otherwise EmitSignature
+ [fileId, !isString(emitSignature) && emitSignature[0] === actualSignature ? emptyArray as [] : emitSignature]);
+ }
}
- return fileId;
}
+ return value.version === actualSignature ?
+ value.affectsGlobalScope || value.impliedFormat ?
+ // If file version is same as signature, dont serialize signature
+ { version: value.version, signature: undefined, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat } :
+ // If file info only contains version and signature and both are same we can just write string
+ value.version :
+ actualSignature !== undefined ? // If signature is not same as version, encode signature in the fileInfo
+ oldSignature === undefined ?
+ // If we havent computed signature, use fileInfo as is
+ value :
+ // Serialize fileInfo with new updated signature
+ { version: value.version, signature: actualSignature, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat } :
+ // Signature of the FileInfo is undefined, serialize it as false
+ { 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)!)
+ ]);
+ }
- 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;
+ let exportedModulesMap: ProgramBuildInfoReferencedMap | undefined;
+ if (state.exportedModulesMap) {
+ exportedModulesMap = mapDefined(arrayFrom(state.exportedModulesMap.keys()).sort(compareStringsCaseSensitive), key => {
+ const oldValue = state.oldExportedModulesMap?.get(key);
+ // Not in temporary cache, use existing value
+ if (oldValue === undefined) return [toFileId(key), toFileIdListId(state.exportedModulesMap!.getValues(key)!)];
+ if (oldValue) return [toFileId(key), toFileIdListId(oldValue)];
+ return undefined;
+ });
+ }
+
+ 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),
+ convertToReusableDiagnostics(value, relativeToBuildInfo)
+ ] :
+ toFileId(key)
+ );
}
+ }
- /**
- * @param optionKey key of CommandLineOption to use to determine if the option should be serialized in tsbuildinfo
- */
- function convertToProgramBuildInfoCompilerOptions(options: CompilerOptions) {
- let result: CompilerOptions | undefined;
- const { optionsNameMap } = getOptionsNameMap();
- for (const name of getOwnKeys(options).sort(compareStringsCaseSensitive)) {
- const optionInfo = optionsNameMap.get(name.toLowerCase());
- if (optionInfo?.affectsBuildInfo) {
- (result ||= {})[name] = convertToReusableCompilerOptionValue(
- optionInfo,
- options[name] as CompilerOptionsValue,
- relativeToBuildInfoEnsuringAbsolutePath
- );
- }
+ let affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] | undefined;
+ if (state.affectedFilesPendingEmit?.size) {
+ const fullEmitForOptions = getBuilderFileEmit(state.compilerOptions);
+ const seenFiles = new Set();
+ for (const path of arrayFrom(state.affectedFilesPendingEmit.keys()).sort(compareStringsCaseSensitive)) {
+ if (tryAddToSet(seenFiles, path)) {
+ const file = state.program!.getSourceFileByPath(path)!;
+ if (!sourceFileMayBeEmitted(file, state.program!)) continue;
+ const fileId = toFileId(path), pendingEmit = state.affectedFilesPendingEmit.get(path)!;
+ (affectedFilesPendingEmit ||= []).push(
+ pendingEmit === fullEmitForOptions ?
+ fileId : // Pending full emit per options
+ pendingEmit === BuilderFileEmit.Dts ?
+ [fileId] : // Pending on Dts only
+ [fileId, pendingEmit] // Anything else
+ );
}
- 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);
- }
+ let changeFileSet: ProgramBuildInfoFileId[] | undefined;
+ if (state.changedFilesSet.size) {
+ for (const path of arrayFrom(state.changedFilesSet.keys()).sort(compareStringsCaseSensitive)) {
+ (changeFileSet ||= []).push(toFileId(path));
}
- 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;
- });
+ const program: ProgramMultiFileEmitBuildInfo = {
+ fileNames,
+ fileInfos,
+ options: convertToProgramBuildInfoCompilerOptions(state.compilerOptions),
+ fileIdsList,
+ referencedMap,
+ exportedModulesMap,
+ semanticDiagnosticsPerFile,
+ affectedFilesPendingEmit,
+ changeFileSet,
+ emitSignatures,
+ latestChangedDtsFile,
+ };
+ return createBuildInfo(program, bundle);
+
+ function relativeToBuildInfoEnsuringAbsolutePath(path: string) {
+ return relativeToBuildInfo(getNormalizedAbsolutePath(path, currentDirectory));
}
- function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation, relativeToBuildInfo: (path: string) => string): ReusableDiagnosticRelatedInformation {
- const { file } = diagnostic;
- return {
- ...diagnostic,
- file: file ? relativeToBuildInfo(file.resolvedPath) : undefined
- };
+ function relativeToBuildInfo(path: string) {
+ return ensurePathIsNonModuleName(getRelativePathFromDirectory(buildInfoDirectory, path, getCanonicalFileName));
}
- export enum BuilderProgramKind {
- SemanticDiagnosticsBuilderProgram,
- EmitAndSemanticDiagnosticsBuilderProgram
+ 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;
}
- export interface BuilderCreationParameters {
- newProgram: Program;
- host: BuilderProgramHost;
- oldProgram: BuilderProgram | undefined;
- configFileParsingDiagnostics: readonly Diagnostic[];
+ 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;
}
- 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();
+ /**
+ * @param optionKey key of CommandLineOption to use to determine if the option should be serialized in tsbuildinfo
+ */
+ function convertToProgramBuildInfoCompilerOptions(options: CompilerOptions) {
+ let result: CompilerOptions | undefined;
+ const { optionsNameMap } = getOptionsNameMap();
+ for (const name of getOwnKeys(options).sort(compareStringsCaseSensitive)) {
+ const optionInfo = optionsNameMap.get(name.toLowerCase());
+ if (optionInfo?.affectsBuildInfo) {
+ (result ||= {})[name] = convertToReusableCompilerOptionValue(
+ optionInfo,
+ options[name] as CompilerOptionsValue,
+ relativeToBuildInfoEnsuringAbsolutePath
+ );
+ }
}
- 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;
+ 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 {
- newProgram = newProgramOrRootNames;
- host = hostOrOptions as BuilderProgramHost;
- oldProgram = oldProgramOrHost as BuilderProgram;
- configFileParsingDiagnostics = configFileParsingDiagnosticsOrOldProgram as readonly Diagnostic[];
+ else if (option.isFilePath) {
+ return relativeToBuildInfo(value as string);
}
- return { host, newProgram, oldProgram, configFileParsingDiagnostics: configFileParsingDiagnostics || emptyArray };
}
+ 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;
+ });
+}
+
+function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation, relativeToBuildInfo: (path: string) => string): ReusableDiagnosticRelatedInformation {
+ const { file } = diagnostic;
+ return {
+ ...diagnostic,
+ file: file ? relativeToBuildInfo(file.resolvedPath) : undefined
+ };
+}
+
+/** @internal */
+export enum BuilderProgramKind {
+ SemanticDiagnosticsBuilderProgram,
+ EmitAndSemanticDiagnosticsBuilderProgram
+}
+
+/** @internal */
+export interface BuilderCreationParameters {
+ newProgram: Program;
+ host: BuilderProgramHost;
+ oldProgram: BuilderProgram | undefined;
+ configFileParsingDiagnostics: readonly Diagnostic[];
+}
- function getTextHandlingSourceMapForSignature(text: string, data: WriteFileCallbackData | undefined) {
- return data?.sourceMapUrlPos !== undefined ? text.substring(0, data.sourceMapUrlPos) : text;
+/** @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 computeSignatureWithDiagnostics(
- sourceFile: SourceFile,
- text: string,
- computeHash: BuilderState.ComputeHash | undefined,
- getCanonicalFileName: GetCanonicalFileName,
- data: WriteFileCallbackData | undefined
- ) {
- text = getTextHandlingSourceMapForSignature(text, data);
- let sourceFileDirectory: string | undefined;
- if (data?.diagnostics?.length) {
- text += data.diagnostics.map(diagnostic =>
- `${locationInfo(diagnostic)}${DiagnosticCategory[diagnostic.category]}${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText)}`
- ).join("\n");
- }
- return (computeHash ?? generateDjb2Hash)(text);
-
- function flattenDiagnosticMessageText(diagnostic: string | DiagnosticMessageChain | undefined): string {
- return isString(diagnostic) ?
- diagnostic :
- diagnostic === undefined ?
- "" :
- !diagnostic.next ?
- diagnostic.messageText :
- diagnostic.messageText + diagnostic.next.map(flattenDiagnosticMessageText).join("\n");
- }
+function getTextHandlingSourceMapForSignature(text: string, data: WriteFileCallbackData | undefined) {
+ return data?.sourceMapUrlPos !== undefined ? text.substring(0, data.sourceMapUrlPos) : text;
+}
- function locationInfo(diagnostic: DiagnosticWithLocation) {
- if (diagnostic.file.resolvedPath === sourceFile.resolvedPath) return `(${diagnostic.start},${diagnostic.length})`;
- if (sourceFileDirectory === undefined) sourceFileDirectory = getDirectoryPath(sourceFile.resolvedPath);
- return `${ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceFileDirectory, diagnostic.file.resolvedPath, getCanonicalFileName))}(${diagnostic.start},${diagnostic.length})`;
- }
+/** @internal */
+export function computeSignatureWithDiagnostics(
+ sourceFile: SourceFile,
+ text: string,
+ computeHash: BuilderState.ComputeHash | undefined,
+ getCanonicalFileName: GetCanonicalFileName,
+ data: WriteFileCallbackData | undefined
+) {
+ text = getTextHandlingSourceMapForSignature(text, data);
+ let sourceFileDirectory: string | undefined;
+ if (data?.diagnostics?.length) {
+ text += data.diagnostics.map(diagnostic =>
+ `${locationInfo(diagnostic)}${DiagnosticCategory[diagnostic.category]}${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText)}`
+ ).join("\n");
+ }
+ return (computeHash ?? generateDjb2Hash)(text);
+
+ function flattenDiagnosticMessageText(diagnostic: string | DiagnosticMessageChain | undefined): string {
+ return isString(diagnostic) ?
+ diagnostic :
+ diagnostic === undefined ?
+ "" :
+ !diagnostic.next ?
+ diagnostic.messageText :
+ diagnostic.messageText + diagnostic.next.map(flattenDiagnosticMessageText).join("\n");
}
- export function computeSignature(text: string, computeHash: BuilderState.ComputeHash | undefined, data?: WriteFileCallbackData) {
- return (computeHash ?? generateDjb2Hash)(getTextHandlingSourceMapForSignature(text, data));
+ function locationInfo(diagnostic: DiagnosticWithLocation) {
+ if (diagnostic.file.resolvedPath === sourceFile.resolvedPath) return `(${diagnostic.start},${diagnostic.length})`;
+ if (sourceFileDirectory === undefined) sourceFileDirectory = getDirectoryPath(sourceFile.resolvedPath);
+ return `${ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceFileDirectory, diagnostic.file.resolvedPath, getCanonicalFileName))}(${diagnostic.start},${diagnostic.length})`;
}
+}
- 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;
- }
+/** @internal */
+export function computeSignature(text: string, computeHash: BuilderState.ComputeHash | undefined, data?: WriteFileCallbackData) {
+ return (computeHash ?? generateDjb2Hash)(getTextHandlingSourceMapForSignature(text, data));
+}
- /**
- * Create the canonical file name for identity
- */
- const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
- /**
- * Computing hash to for signature verification
- */
- const computeHash = maybeBind(host, host.createHash);
- const state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState, host.disableUseFileVersionAsSignature);
- newProgram.getBuildInfo = bundle => getBuildInfo(state, getCanonicalFileName, bundle);
-
- // 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.saveEmitState = () => backupBuilderProgramEmitState(state);
- builderProgram.restoreEmitState = (saved) => restoreBuilderProgramEmitState(state, saved);
- builderProgram.hasChangedEmitSignature = () => !!state.hasChangedEmitSignature;
- builderProgram.getAllDependencies = sourceFile => BuilderState.getAllDependencies(state, Debug.checkDefined(state.program), sourceFile);
- builderProgram.getSemanticDiagnostics = getSemanticDiagnostics;
- builderProgram.emit = emit;
- builderProgram.releaseProgram = () => releaseCache(state);
-
- 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);
+ const state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState, host.disableUseFileVersionAsSignature);
+ newProgram.getBuildInfo = bundle => getBuildInfo(state, getCanonicalFileName, bundle);
+
+ // 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.saveEmitState = () => backupBuilderProgramEmitState(state);
+ builderProgram.restoreEmitState = (saved) => restoreBuilderProgramEmitState(state, saved);
+ builderProgram.hasChangedEmitSignature = () => !!state.hasChangedEmitSignature;
+ builderProgram.getAllDependencies = sourceFile => BuilderState.getAllDependencies(state, Debug.checkDefined(state.program), sourceFile);
+ builderProgram.getSemanticDiagnostics = getSemanticDiagnostics;
+ builderProgram.emit = emit;
+ builderProgram.releaseProgram = () => releaseCache(state);
+
+ 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, getCanonicalFileName, host);
- const programEmitKind = getBuilderFileEmit(state.compilerOptions);
- let emitKind: BuilderFileEmit = emitOnlyDtsFiles ?
- programEmitKind & BuilderFileEmit.AllDts : programEmitKind;
- if (!affected) {
- if (!outFile(state.compilerOptions)) {
- const pendingAffectedFile = getNextAffectedFilePendingEmit(state, emitOnlyDtsFiles);
- if (!pendingAffectedFile) {
- // Emit buildinfo if pending
- if (!state.buildInfoEmitPending) return undefined;
- const affected = state.program!;
- const result = affected.emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken);
- state.buildInfoEmitPending = false;
- return { result, affected };
- }
- // Emit pending affected file
- ({ affectedFile: affected, emitKind } = pendingAffectedFile);
- }
- else {
- // Emit program if it was pending emit
- if (!state.programEmitPending) return undefined;
- emitKind = state.programEmitPending;
- if (emitOnlyDtsFiles) emitKind = emitKind & BuilderFileEmit.AllDts;
- if (!emitKind) return undefined;
- affected = state.program!;
+ /**
+ * 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, getCanonicalFileName, host);
+ const programEmitKind = getBuilderFileEmit(state.compilerOptions);
+ let emitKind: BuilderFileEmit = emitOnlyDtsFiles ?
+ programEmitKind & BuilderFileEmit.AllDts : programEmitKind;
+ if (!affected) {
+ if (!outFile(state.compilerOptions)) {
+ const pendingAffectedFile = getNextAffectedFilePendingEmit(state, emitOnlyDtsFiles);
+ if (!pendingAffectedFile) {
+ // Emit buildinfo if pending
+ if (!state.buildInfoEmitPending) return undefined;
+ const affected = state.program!;
+ const result = affected.emitBuildInfo(writeFile || maybeBind(host, host.writeFile), cancellationToken);
+ state.buildInfoEmitPending = false;
+ return { result, affected };
}
- }
- // Determine if we can do partial emit
- let emitOnly: EmitOnly | undefined;
- if (emitKind & BuilderFileEmit.AllJs) emitOnly = EmitOnly.Js;
- if (emitKind & BuilderFileEmit.AllDts) emitOnly = emitOnly === undefined ? EmitOnly.Dts : undefined;
- if (affected === state.program) {
- // Set up programEmit before calling emit so that its set in buildInfo
- state.programEmitPending = state.changedFilesSet.size ?
- getPendingEmitKind(programEmitKind, emitKind) :
- state.programEmitPending ?
- getPendingEmitKind(state.programEmitPending, emitKind) :
- undefined;
- }
- // Actual emit
- const result = state.program!.emit(
- affected === state.program ? undefined : affected as SourceFile,
- getWriteFileCallback(writeFile, customTransformers),
- cancellationToken,
- emitOnly,
- customTransformers
- );
- if (affected !== state.program) {
- // update affected files
- const affectedSourceFile = affected as SourceFile;
- state.seenAffectedFiles!.add(affectedSourceFile.resolvedPath);
- if (state.affectedFilesIndex !== undefined) state.affectedFilesIndex++;
- // Change in changeSet/affectedFilesPendingEmit, buildInfo needs to be emitted
- state.buildInfoEmitPending = true;
- // Update the pendingEmit for the file
- const existing = state.seenEmittedFiles?.get(affectedSourceFile.resolvedPath) || BuilderFileEmit.None;
- (state.seenEmittedFiles ??= new Map()).set(affectedSourceFile.resolvedPath, emitKind | existing);
- const existingPending = state.affectedFilesPendingEmit?.get(affectedSourceFile.resolvedPath) || programEmitKind;
- const pendingKind = getPendingEmitKind(existingPending, emitKind | existing);
- if (pendingKind) (state.affectedFilesPendingEmit ??= new Map()).set(affectedSourceFile.resolvedPath, pendingKind);
- else state.affectedFilesPendingEmit?.delete(affectedSourceFile.resolvedPath);
+ // Emit pending affected file
+ ({ affectedFile: affected, emitKind } = pendingAffectedFile);
}
else {
- // In program clear our changed files since any emit handles all changes
- state.changedFilesSet.clear();
+ // Emit program if it was pending emit
+ if (!state.programEmitPending) return undefined;
+ emitKind = state.programEmitPending;
+ if (emitOnlyDtsFiles) emitKind = emitKind & BuilderFileEmit.AllDts;
+ if (!emitKind) return undefined;
+ affected = state.program!;
}
- return { result, affected };
}
+ // Determine if we can do partial emit
+ let emitOnly: EmitOnly | undefined;
+ if (emitKind & BuilderFileEmit.AllJs) emitOnly = EmitOnly.Js;
+ if (emitKind & BuilderFileEmit.AllDts) emitOnly = emitOnly === undefined ? EmitOnly.Dts : undefined;
+ if (affected === state.program) {
+ // Set up programEmit before calling emit so that its set in buildInfo
+ state.programEmitPending = state.changedFilesSet.size ?
+ getPendingEmitKind(programEmitKind, emitKind) :
+ state.programEmitPending ?
+ getPendingEmitKind(state.programEmitPending, emitKind) :
+ undefined;
+ }
+ // Actual emit
+ const result = state.program!.emit(
+ affected === state.program ? undefined : affected as SourceFile,
+ getWriteFileCallback(writeFile, customTransformers),
+ cancellationToken,
+ emitOnly,
+ customTransformers
+ );
+ if (affected !== state.program) {
+ // update affected files
+ const affectedSourceFile = affected as SourceFile;
+ state.seenAffectedFiles!.add(affectedSourceFile.resolvedPath);
+ if (state.affectedFilesIndex !== undefined) state.affectedFilesIndex++;
+ // Change in changeSet/affectedFilesPendingEmit, buildInfo needs to be emitted
+ state.buildInfoEmitPending = true;
+ // Update the pendingEmit for the file
+ const existing = state.seenEmittedFiles?.get(affectedSourceFile.resolvedPath) || BuilderFileEmit.None;
+ (state.seenEmittedFiles ??= new Map()).set(affectedSourceFile.resolvedPath, emitKind | existing);
+ const existingPending = state.affectedFilesPendingEmit?.get(affectedSourceFile.resolvedPath) || programEmitKind;
+ const pendingKind = getPendingEmitKind(existingPending, emitKind | existing);
+ if (pendingKind) (state.affectedFilesPendingEmit ??= new Map()).set(affectedSourceFile.resolvedPath, pendingKind);
+ else state.affectedFilesPendingEmit?.delete(affectedSourceFile.resolvedPath);
+ }
+ else {
+ // In program clear our changed files since any emit handles all changes
+ state.changedFilesSet.clear();
+ }
+ return { result, affected };
+ }
- function getWriteFileCallback(writeFile: WriteFileCallback | undefined, customTransformers: CustomTransformers | undefined): WriteFileCallback | undefined {
- if (!getEmitDeclarations(state.compilerOptions)) return writeFile || maybeBind(host, host.writeFile);
- return (fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
- if (isDeclarationFileName(fileName)) {
- if (!outFile(state.compilerOptions)) {
- Debug.assert(sourceFiles?.length === 1);
- let emitSignature;
- if (!customTransformers) {
- const file = sourceFiles[0];
- const info = state.fileInfos.get(file.resolvedPath)!;
- if (info.signature === file.version) {
- const signature = computeSignatureWithDiagnostics(
- file,
- text,
- computeHash,
- getCanonicalFileName,
- data,
- );
- // With d.ts diagnostics they are also part of the signature so emitSignature will be different from it since its just hash of d.ts
- if (!data?.diagnostics?.length) emitSignature = signature;
- if (signature !== file.version) { // Update it
- if (host.storeFilesChangingSignatureDuringEmit) (state.filesChangingSignature ??= new Set()).add(file.resolvedPath);
- if (state.exportedModulesMap) BuilderState.updateExportedModules(state, file, file.exportedModulesFromDeclarationEmit);
- if (state.affectedFiles) {
- // Keep old signature so we know what to undo if cancellation happens
- const existing = state.oldSignatures?.get(file.resolvedPath);
- if (existing === undefined) (state.oldSignatures ??= new Map()).set(file.resolvedPath, info.signature || false);
- info.signature = signature;
- }
- else {
- // These are directly commited
- info.signature = signature;
- state.oldExportedModulesMap?.clear();
- }
+ function getWriteFileCallback(writeFile: WriteFileCallback | undefined, customTransformers: CustomTransformers | undefined): WriteFileCallback | undefined {
+ if (!getEmitDeclarations(state.compilerOptions)) return writeFile || maybeBind(host, host.writeFile);
+ return (fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
+ if (isDeclarationFileName(fileName)) {
+ if (!outFile(state.compilerOptions)) {
+ Debug.assert(sourceFiles?.length === 1);
+ let emitSignature;
+ if (!customTransformers) {
+ const file = sourceFiles[0];
+ const info = state.fileInfos.get(file.resolvedPath)!;
+ if (info.signature === file.version) {
+ const signature = computeSignatureWithDiagnostics(
+ file,
+ text,
+ computeHash,
+ getCanonicalFileName,
+ data,
+ );
+ // With d.ts diagnostics they are also part of the signature so emitSignature will be different from it since its just hash of d.ts
+ if (!data?.diagnostics?.length) emitSignature = signature;
+ if (signature !== file.version) { // Update it
+ if (host.storeFilesChangingSignatureDuringEmit) (state.filesChangingSignature ??= new Set()).add(file.resolvedPath);
+ if (state.exportedModulesMap) BuilderState.updateExportedModules(state, file, file.exportedModulesFromDeclarationEmit);
+ if (state.affectedFiles) {
+ // Keep old signature so we know what to undo if cancellation happens
+ const existing = state.oldSignatures?.get(file.resolvedPath);
+ if (existing === undefined) (state.oldSignatures ??= new Map()).set(file.resolvedPath, info.signature || false);
+ info.signature = signature;
+ }
+ else {
+ // These are directly commited
+ info.signature = signature;
+ state.oldExportedModulesMap?.clear();
}
}
}
-
- // Store d.ts emit hash so later can be compared to check if d.ts has changed.
- // Currently we do this only for composite projects since these are the only projects that can be referenced by other projects
- // and would need their d.ts change time in --build mode
- if (state.compilerOptions.composite) {
- const filePath = sourceFiles[0].resolvedPath;
- emitSignature = handleNewSignature(state.emitSignatures?.get(filePath), emitSignature);
- if (!emitSignature) return;
- (state.emitSignatures ??= new Map()).set(filePath, emitSignature);
- }
}
- else if (state.compilerOptions.composite) {
- const newSignature = handleNewSignature(state.outSignature, /*newSignature*/ undefined);
- if (!newSignature) return;
- state.outSignature = newSignature;
+
+ // Store d.ts emit hash so later can be compared to check if d.ts has changed.
+ // Currently we do this only for composite projects since these are the only projects that can be referenced by other projects
+ // and would need their d.ts change time in --build mode
+ if (state.compilerOptions.composite) {
+ const filePath = sourceFiles[0].resolvedPath;
+ emitSignature = handleNewSignature(state.emitSignatures?.get(filePath), emitSignature);
+ if (!emitSignature) return;
+ (state.emitSignatures ??= new Map()).set(filePath, emitSignature);
}
}
- if (writeFile) writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
- else if (host.writeFile) host.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
- else state.program!.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
-
- /**
- * Compare to existing computed signature and store it or handle the changes in d.ts map option from before
- * returning undefined means that, we dont need to emit this d.ts file since its contents didnt change
- */
- function handleNewSignature(oldSignatureFormat: EmitSignature | undefined, newSignature: string | undefined) {
- const oldSignature = !oldSignatureFormat || isString(oldSignatureFormat) ? oldSignatureFormat : oldSignatureFormat[0];
- newSignature ??= computeSignature(text, computeHash, data);
- // Dont write dts files if they didn't change
- if (newSignature === oldSignature) {
- // If the signature was encoded as string the dts map options match so nothing to do
- if (oldSignatureFormat === oldSignature) return undefined;
- // Mark as differsOnlyInMap so that --build can reverse the timestamp so that
- // the downstream projects dont detect this as change in d.ts file
- else if (data) data.differsOnlyInMap = true;
- else data = { differsOnlyInMap: true };
- }
- else {
- state.hasChangedEmitSignature = true;
- state.latestChangedDtsFile = fileName;
- }
- return newSignature;
+ else if (state.compilerOptions.composite) {
+ const newSignature = handleNewSignature(state.outSignature, /*newSignature*/ undefined);
+ if (!newSignature) return;
+ state.outSignature = newSignature;
}
- };
- }
-
- /**
- * 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 {
- if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
- assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile);
}
- const result = handleNoEmitOptions(builderProgram, targetSourceFile, writeFile, cancellationToken);
- if (result) return result;
-
- // Emit only affected files if using builder for emit
- if (!targetSourceFile) {
- if (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
- };
+ if (writeFile) writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
+ else if (host.writeFile) host.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
+ else state.program!.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
+
+ /**
+ * Compare to existing computed signature and store it or handle the changes in d.ts map option from before
+ * returning undefined means that, we dont need to emit this d.ts file since its contents didnt change
+ */
+ function handleNewSignature(oldSignatureFormat: EmitSignature | undefined, newSignature: string | undefined) {
+ const oldSignature = !oldSignatureFormat || isString(oldSignatureFormat) ? oldSignatureFormat : oldSignatureFormat[0];
+ newSignature ??= computeSignature(text, computeHash, data);
+ // Dont write dts files if they didn't change
+ if (newSignature === oldSignature) {
+ // If the signature was encoded as string the dts map options match so nothing to do
+ if (oldSignatureFormat === oldSignature) return undefined;
+ // Mark as differsOnlyInMap so that --build can reverse the timestamp so that
+ // the downstream projects dont detect this as change in d.ts file
+ else if (data) data.differsOnlyInMap = true;
+ else data = { differsOnlyInMap: true };
}
- // In non Emit builder, clear affected files pending emit
else {
- clearAffectedFilesPendingEmit(state, emitOnlyDtsFiles);
+ state.hasChangedEmitSignature = true;
+ state.latestChangedDtsFile = fileName;
}
+ return newSignature;
}
- return Debug.checkDefined(state.program).emit(
- targetSourceFile,
- getWriteFileCallback(writeFile, customTransformers),
- 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 {
+ if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) {
+ assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile);
}
+ const result = handleNoEmitOptions(builderProgram, targetSourceFile, writeFile, cancellationToken);
+ if (result) return result;
- /**
- * 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, getCanonicalFileName, host);
- let result;
- if (!affected) return undefined; // Done
- else if (affected !== state.program) {
- // Get diagnostics for the affected file if its not ignored
- const affectedSourceFile = affected as SourceFile;
- if (!ignoreSourceFile || !ignoreSourceFile(affectedSourceFile)) {
- result = getSemanticDiagnosticsOfFile(state, affectedSourceFile, cancellationToken);
- }
- state.seenAffectedFiles!.add(affectedSourceFile.resolvedPath);
- state.affectedFilesIndex!++;
- // Change in changeSet, buildInfo needs to be emitted
- state.buildInfoEmitPending = true;
- if (!result) continue;
- }
- else {
- // When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified)
- result = state.program.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken);
- state.changedFilesSet.clear();
- state.programEmitPending = getBuilderFileEmit(state.compilerOptions);
+ // Emit only affected files if using builder for emit
+ if (!targetSourceFile) {
+ if (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 { result, affected };
+ return {
+ emitSkipped,
+ diagnostics: diagnostics || emptyArray,
+ emittedFiles,
+ sourceMaps
+ };
+ }
+ // In non Emit builder, clear affected files pending emit
+ else {
+ clearAffectedFilesPendingEmit(state, emitOnlyDtsFiles);
}
}
+ return Debug.checkDefined(state.program).emit(
+ targetSourceFile,
+ getWriteFileCallback(writeFile, customTransformers),
+ 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, getCanonicalFileName, host);
+ let result;
+ if (!affected) return undefined; // Done
+ else if (affected !== state.program) {
+ // Get diagnostics for the affected file if its not ignored
+ const affectedSourceFile = affected as SourceFile;
+ if (!ignoreSourceFile || !ignoreSourceFile(affectedSourceFile)) {
+ result = getSemanticDiagnosticsOfFile(state, affectedSourceFile, cancellationToken);
+ }
+ state.seenAffectedFiles!.add(affectedSourceFile.resolvedPath);
+ state.affectedFilesIndex!++;
+ // Change in changeSet, buildInfo needs to be emitted
+ state.buildInfoEmitPending = true;
+ if (!result) continue;
}
-
- if (sourceFile) {
- return getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken);
+ else {
+ // When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified)
+ result = state.program.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken);
+ state.changedFilesSet.clear();
+ state.programEmitPending = getBuilderFileEmit(state.compilerOptions);
}
+ return { result, 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)) {
- }
+ /**
+ * 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);
+ }
- let diagnostics: Diagnostic[] | undefined;
- for (const sourceFile of Debug.checkDefined(state.program).getSourceFiles()) {
- diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken));
- }
- return diagnostics || emptyArray;
+ if (sourceFile) {
+ return getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken);
}
- }
- function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilePendingEmit: Path, kind: BuilderFileEmit) {
- const existingKind = state.affectedFilesPendingEmit?.get(affectedFilePendingEmit) || BuilderFileEmit.None;
- (state.affectedFilesPendingEmit ??= new Map()).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)) {
+ }
- export function toBuilderStateFileInfoForMultiEmit(fileInfo: ProgramMultiFileEmitBuildInfoFileInfo): 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 };
+ 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 toBuilderFileEmit(value: ProgramBuilderInfoFilePendingEmit, fullEmitForOptions: BuilderFileEmit): BuilderFileEmit{
- return isNumber(value) ? fullEmitForOptions : value[1] || BuilderFileEmit.Dts;
- }
+function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilePendingEmit: Path, kind: BuilderFileEmit) {
+ const existingKind = state.affectedFilesPendingEmit?.get(affectedFilePendingEmit) || BuilderFileEmit.None;
+ (state.affectedFilesPendingEmit ??= new Map()).set(affectedFilePendingEmit, existingKind | kind);
+}
- export function toProgramEmitPending(value: ProgramBuildInfoBundlePendingEmit, options: CompilerOptions | undefined): BuilderFileEmit | undefined {
- return !value ? getBuilderFileEmit(options || {}) : value;
- }
+/** @internal */
+export function toBuilderStateFileInfoForMultiEmit(fileInfo: ProgramMultiFileEmitBuildInfoFileInfo): 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 };
+}
- export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo, buildInfoPath: string, host: ReadBuildProgramHost): EmitAndSemanticDiagnosticsBuilderProgram {
- const program = buildInfo.program!;
- const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
- const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
-
- let state: ReusableBuilderProgramState;
- const filePaths = program.fileNames?.map(toPath);
- let filePathsSetList: Set[] | undefined;
- const latestChangedDtsFile = program.latestChangedDtsFile ? toAbsolutePath(program.latestChangedDtsFile) : undefined;
- if (isProgramBundleEmitBuildInfo(program)) {
- const fileInfos = new Map();
- program.fileInfos.forEach((fileInfo, index) => {
- const path = toFilePath(index + 1 as ProgramBuildInfoFileId);
- fileInfos.set(path, isString(fileInfo) ? { version: fileInfo, signature: undefined, affectsGlobalScope: undefined, impliedFormat: undefined } : fileInfo);
- });
- state = {
- fileInfos,
- compilerOptions: program.options ? convertToOptionsWithAbsolutePaths(program.options, toAbsolutePath) : {},
- latestChangedDtsFile,
- outSignature: program.outSignature,
- programEmitPending: program.pendingEmit === undefined ? undefined : toProgramEmitPending(program.pendingEmit, program.options),
- bundle: buildInfo.bundle,
- };
- }
- else {
- filePathsSetList = program.fileIdsList?.map(fileIds => new Set(fileIds.map(toFilePath)));
- const fileInfos = new Map();
- const emitSignatures = program.options?.composite && !outFile(program.options) ? new Map() : undefined;
- program.fileInfos.forEach((fileInfo, index) => {
- const path = toFilePath(index + 1 as ProgramBuildInfoFileId);
- const stateFileInfo = toBuilderStateFileInfoForMultiEmit(fileInfo);
- fileInfos.set(path, stateFileInfo);
- if (emitSignatures && stateFileInfo.signature) emitSignatures.set(path, stateFileInfo.signature);
- });
- program.emitSignatures?.forEach(value => {
- if (isNumber(value)) emitSignatures!.delete(toFilePath(value));
- else {
- const key = toFilePath(value[0]);
- emitSignatures!.set(key, !isString(value[1]) && !value[1].length ?
- // File signature is emit signature but differs in map
- [emitSignatures!.get(key)! as string] :
- value[1]
- );
- }
- });
- const fullEmitForOptions = program.affectedFilesPendingEmit ? getBuilderFileEmit(program.options || {}) : undefined;
- state = {
- 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: program.affectedFilesPendingEmit && arrayToMap(program.affectedFilesPendingEmit, value => toFilePath(isNumber(value) ? value : value[0]), value => toBuilderFileEmit(value, fullEmitForOptions!)),
- changedFilesSet: new Set(map(program.changeFileSet, toFilePath)),
- latestChangedDtsFile,
- emitSignatures: emitSignatures?.size ? emitSignatures : undefined,
- };
- }
+/** @internal */
+export function toBuilderFileEmit(value: ProgramBuilderInfoFilePendingEmit, fullEmitForOptions: BuilderFileEmit): BuilderFileEmit{
+ return isNumber(value) ? fullEmitForOptions : value[1] || BuilderFileEmit.Dts;
+}
- return {
- getState: () => state,
- saveEmitState: noop as BuilderProgram["saveEmitState"],
- restoreEmitState: 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,
- hasChangedEmitSignature: returnFalse,
+/** @internal */
+export function toProgramEmitPending(value: ProgramBuildInfoBundlePendingEmit, options: CompilerOptions | undefined): BuilderFileEmit | undefined {
+ return !value ? getBuilderFileEmit(options || {}) : value;
+}
+
+/** @internal */
+export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo, buildInfoPath: string, host: ReadBuildProgramHost): EmitAndSemanticDiagnosticsBuilderProgram {
+ const program = buildInfo.program!;
+ const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
+ const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
+
+ let state: ReusableBuilderProgramState;
+ const filePaths = program.fileNames?.map(toPath);
+ let filePathsSetList: Set[] | undefined;
+ const latestChangedDtsFile = program.latestChangedDtsFile ? toAbsolutePath(program.latestChangedDtsFile) : undefined;
+ if (isProgramBundleEmitBuildInfo(program)) {
+ const fileInfos = new Map();
+ program.fileInfos.forEach((fileInfo, index) => {
+ const path = toFilePath(index + 1 as ProgramBuildInfoFileId);
+ fileInfos.set(path, isString(fileInfo) ? { version: fileInfo, signature: undefined, affectsGlobalScope: undefined, impliedFormat: undefined } : fileInfo);
+ });
+ state = {
+ fileInfos,
+ compilerOptions: program.options ? convertToOptionsWithAbsolutePaths(program.options, toAbsolutePath) : {},
+ latestChangedDtsFile,
+ outSignature: program.outSignature,
+ programEmitPending: program.pendingEmit === undefined ? undefined : toProgramEmitPending(program.pendingEmit, program.options),
+ bundle: buildInfo.bundle,
+ };
+ }
+ else {
+ filePathsSetList = program.fileIdsList?.map(fileIds => new Set(fileIds.map(toFilePath)));
+ const fileInfos = new Map();
+ const emitSignatures = program.options?.composite && !outFile(program.options) ? new Map() : undefined;
+ program.fileInfos.forEach((fileInfo, index) => {
+ const path = toFilePath(index + 1 as ProgramBuildInfoFileId);
+ const stateFileInfo = toBuilderStateFileInfoForMultiEmit(fileInfo);
+ fileInfos.set(path, stateFileInfo);
+ if (emitSignatures && stateFileInfo.signature) emitSignatures.set(path, stateFileInfo.signature);
+ });
+ program.emitSignatures?.forEach(value => {
+ if (isNumber(value)) emitSignatures!.delete(toFilePath(value));
+ else {
+ const key = toFilePath(value[0]);
+ emitSignatures!.set(key, !isString(value[1]) && !value[1].length ?
+ // File signature is emit signature but differs in map
+ [emitSignatures!.get(key)! as string] :
+ value[1]
+ );
+ }
+ });
+ const fullEmitForOptions = program.affectedFilesPendingEmit ? getBuilderFileEmit(program.options || {}) : undefined;
+ state = {
+ 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: program.affectedFilesPendingEmit && arrayToMap(program.affectedFilesPendingEmit, value => toFilePath(isNumber(value) ? value : value[0]), value => toBuilderFileEmit(value, fullEmitForOptions!)),
+ changedFilesSet: new Set(map(program.changeFileSet, toFilePath)),
+ latestChangedDtsFile,
+ emitSignatures: emitSignatures?.size ? emitSignatures : undefined,
};
+ }
- function toPath(path: string) {
- return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
- }
+ return {
+ getState: () => state,
+ saveEmitState: noop as BuilderProgram["saveEmitState"],
+ restoreEmitState: 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,
+ hasChangedEmitSignature: returnFalse,
+ };
- function toAbsolutePath(path: string) {
- return getNormalizedAbsolutePath(path, buildInfoDirectory);
- }
+ function toPath(path: string) {
+ return ts.toPath(path, buildInfoDirectory, getCanonicalFileName);
+ }
- function toFilePath(fileId: ProgramBuildInfoFileId) {
- return filePaths[fileId - 1];
- }
+ function toAbsolutePath(path: string) {
+ return getNormalizedAbsolutePath(path, buildInfoDirectory);
+ }
- function toFilePathsSet(fileIdsListId: ProgramBuildInfoFileIdListId) {
- return filePathsSetList![fileIdsListId - 1];
- }
+ function toFilePath(fileId: ProgramBuildInfoFileId) {
+ return filePaths[fileId - 1];
+ }
- function toManyToManyPathMap(referenceMap: ProgramBuildInfoReferencedMap | undefined): BuilderState.ManyToManyPathMap | undefined {
- if (!referenceMap) {
- return undefined;
- }
+ function toFilePathsSet(fileIdsListId: ProgramBuildInfoFileIdListId) {
+ return filePathsSetList![fileIdsListId - 1];
+ }
- const map = BuilderState.createManyToManyPathMap();
- referenceMap.forEach(([fileId, fileIdListId]) =>
- map.set(toFilePath(fileId), toFilePathsSet(fileIdListId))
- );
- return map;
+ function toManyToManyPathMap(referenceMap: ProgramBuildInfoReferencedMap | undefined): BuilderState.ManyToManyPathMap | undefined {
+ if (!referenceMap) {
+ return undefined;
}
- }
- export function getBuildInfoFileVersionMap(
- program: ProgramBuildInfo,
- buildInfoPath: string,
- host: Pick
- ): ESMap {
- const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
- const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
- const fileInfos = new Map();
- program.fileInfos.forEach((fileInfo, index) => {
- const path = toPath(program.fileNames[index], buildInfoDirectory, getCanonicalFileName);
- const version = isString(fileInfo) ? fileInfo : fileInfo.version;
- fileInfos.set(path, version);
- });
- return fileInfos;
+ const map = BuilderState.createManyToManyPathMap();
+ referenceMap.forEach(([fileId, fileIdListId]) =>
+ map.set(toFilePath(fileId), toFilePathsSet(fileIdListId))
+ );
+ return map;
}
+}
- export function createRedirectedBuilderProgram(getState: () => { program?: Program | undefined; compilerOptions: CompilerOptions; }, configFileParsingDiagnostics: readonly Diagnostic[]): BuilderProgram {
- return {
- getState: notImplemented,
- saveEmitState: noop as BuilderProgram["saveEmitState"],
- restoreEmitState: 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,
- };
+/** @internal */
+export function getBuildInfoFileVersionMap(
+ program: ProgramBuildInfo,
+ buildInfoPath: string,
+ host: Pick
+): ESMap {
+ const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory()));
+ const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
+ const fileInfos = new Map();
+ program.fileInfos.forEach((fileInfo, index) => {
+ const path = toPath(program.fileNames[index], buildInfoDirectory, getCanonicalFileName);
+ const version = isString(fileInfo) ? fileInfo : fileInfo.version;
+ fileInfos.set(path, version);
+ });
+ return fileInfos;
+}
- function getProgram() {
- return Debug.checkDefined(getState().program);
- }
+/** @internal */
+export function createRedirectedBuilderProgram(getState: () => { program?: Program | undefined; compilerOptions: CompilerOptions; }, configFileParsingDiagnostics: readonly Diagnostic[]): BuilderProgram {
+ return {
+ getState: notImplemented,
+ saveEmitState: noop as BuilderProgram["saveEmitState"],
+ restoreEmitState: 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 cc1a971753f3b..605d82018a3fd 100644
--- a/src/compiler/builderPublic.ts
+++ b/src/compiler/builderPublic.ts
@@ -1,181 +1,186 @@
-namespace ts {
- export type AffectedFileResult = { result: T; affected: SourceFile | Program; } | undefined;
+import {
+ BuilderProgramKind, CancellationToken, CompilerHost, CompilerOptions, createBuilderProgram,
+ createRedirectedBuilderProgram, CustomTransformers, Diagnostic, DiagnosticWithLocation, EmitResult,
+ getBuilderCreationParameters, Program, ProjectReference, ReusableBuilderProgramState, SavedBuildProgramEmitState,
+ SourceFile, WriteFileCallback,
+} from "./_namespaces/ts";
- 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;
- /**
- * Store the list of files that update signature during the emit
- */
- /*@internal*/
- storeFilesChangingSignatureDuringEmit?: boolean;
- /**
- * Gets the current time
- */
- /*@internal*/
- now?(): Date;
- }
+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*/
- saveEmitState(): SavedBuildProgramEmitState;
- /*@internal*/
- restoreEmitState(saved: SavedBuildProgramEmitState): void;
- /*@internal*/
- hasChangedEmitSignature?(): boolean;
- /**
- * 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;
+ /**
+ * 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;
/**
- * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files
+ * Store the list of files that update signature during the emit
*/
- 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;
- }
+ /*@internal*/
+ storeFilesChangingSignatureDuringEmit?: boolean;
+ /**
+ * Gets the current time
+ */
+ /*@internal*/
+ now?(): Date;
+}
+/**
+ * Builder to manage the program state changes
+ */
+export interface BuilderProgram {
+ /*@internal*/
+ getState(): ReusableBuilderProgramState;
+ /*@internal*/
+ saveEmitState(): SavedBuildProgramEmitState;
+ /*@internal*/
+ restoreEmitState(saved: SavedBuildProgramEmitState): void;
+ /*@internal*/
+ hasChangedEmitSignature?(): boolean;
+ /**
+ * 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[];
/**
- * 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 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[];
/**
- * 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
*/
- 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));
- }
+ 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;
+}
+/**
+ * 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 176ddcd810bda..6257d8a311b07 100644
--- a/src/compiler/builderState.ts
+++ b/src/compiler/builderState.ts
@@ -1,632 +1,641 @@
-/*@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 } = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, forceDtsEmit);
- return { outputFiles, emitSkipped, diagnostics };
-
- function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) {
- outputFiles.push({ name: fileName, writeByteOrderMark, text });
- }
+import {
+ arrayFrom, CancellationToken, computeSignatureWithDiagnostics, CustomTransformers, Debug, EmitOutput, emptyArray,
+ ESMap, ExportedModulesFromDeclarationEmit, GetCanonicalFileName, getDirectoryPath, getSourceFileOfNode,
+ isDeclarationFileName, isExternalOrCommonJsModule, isGlobalScopeAugmentation, isJsonSourceFile,
+ isModuleWithStringLiteralName, isStringLiteral, Iterator, Map, mapDefined, mapDefinedIterator, ModuleDeclaration,
+ ModuleKind, outFile, OutputFile, Path, Program, ReadonlySet, Set, some, SourceFile, StringLiteralLike, Symbol,
+ toPath, TypeChecker,
+} from "./_namespaces/ts";
+
+/** @internal */
+export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean,
+ cancellationToken?: CancellationToken, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitOutput {
+ const outputFiles: OutputFile[] = [];
+ const { emitSkipped, diagnostics } = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, forceDtsEmit);
+ return { outputFiles, emitSkipped, diagnostics };
+
+ function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) {
+ outputFiles.push({ name: fileName, writeByteOrderMark, text });
}
- 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;
-
- /**
- * 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;
- /**
- * Stores signatures before before the update till affected file is commited
- */
- oldSignatures?: ESMap;
- /**
- * Stores exportedModulesMap before the update till affected file is commited
- */
- oldExportedModulesMap?: ESMap | false>;
- /**
- * Cache of all files excluding default library file for the current program
- */
- allFilesExcludingDefaultLibraryFile?: readonly SourceFile[];
- /**
- * Cache of all the file names
- */
- allFileNames?: readonly string[];
+}
+/** @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;
+
+ /**
+ * 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;
+ /**
+ * Stores signatures before before the update till affected file is commited
+ */
+ oldSignatures?: ESMap;
+ /**
+ * Stores exportedModulesMap before the update till affected file is commited
+ */
+ oldExportedModulesMap?: ESMap | false>;
+ /**
+ * Cache of all files excluding default library file for the current program
+ */
+ allFilesExcludingDefaultLibraryFile?: readonly SourceFile[];
+ /**
+ * Cache of all the file names
+ */
+ allFileNames?: readonly string[];
+}
+/** @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: true | undefined;
+ impliedFormat: SourceFile["impliedNodeFormat"];
}
- 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: true | undefined;
- impliedFormat: SourceFile["impliedNodeFormat"];
- }
- export interface ReadonlyManyToManyPathMap {
- getKeys(v: Path): ReadonlySet | undefined;
- getValues(k: Path): ReadonlySet | undefined;
- keys(): Iterator;
- }
+ export interface ReadonlyManyToManyPathMap {
+ getKeys(v: Path): ReadonlySet | undefined;
+ getValues(k: Path): ReadonlySet | undefined;
+ keys(): Iterator;
+ }
- export interface ManyToManyPathMap extends ReadonlyManyToManyPathMap {
- deleteKey(k: Path): boolean;
- set(k: Path, v: ReadonlySet): void;
- }
+ export interface ManyToManyPathMap extends ReadonlyManyToManyPathMap {
+ deleteKey(k: Path): boolean;
+ set(k: Path, v: ReadonlySet): void;
+ }
- export function createManyToManyPathMap(): ManyToManyPathMap {
- function create(forward: ESMap>, reverse: ESMap>, deleted: Set | undefined): ManyToManyPathMap {
- const map: ManyToManyPathMap = {
- getKeys: v => reverse.get(v),
- getValues: k => forward.get(k),
- keys: () => forward.keys(),
+ export function createManyToManyPathMap(): ManyToManyPathMap {
+ function create(forward: ESMap>, reverse: ESMap>, deleted: Set | undefined): ManyToManyPathMap {
+ const map: ManyToManyPathMap = {
+ getKeys: v => reverse.get(v),
+ getValues: k => forward.get(k),
+ keys: () => forward.keys(),
- deleteKey: k => {
- (deleted ||= new Set()).add(k);
+ deleteKey: k => {
+ (deleted ||= new Set()).add(k);
- const set = forward.get(k);
- if (!set) {
- return false;
- }
+ const set = forward.get(k);
+ if (!set) {
+ return false;
+ }
- set.forEach(v => deleteFromMultimap(reverse, v, k));
- forward.delete(k);
- return true;
- },
- set: (k, vSet) => {
- deleted?.delete(k);
-
- const existingVSet = forward.get(k);
- forward.set(k, vSet);
-
- existingVSet?.forEach(v => {
- if (!vSet.has(v)) {
- deleteFromMultimap(reverse, v, k);
- }
- });
-
- vSet.forEach(v => {
- if (!existingVSet?.has(v)) {
- addToMultimap(reverse, v, k);
- }
- });
-
- return map;
- },
- };
-
- return map;
- }
+ set.forEach(v => deleteFromMultimap(reverse, v, k));
+ forward.delete(k);
+ return true;
+ },
+ set: (k, vSet) => {
+ deleted?.delete(k);
- return create(new Map>(), new Map>(), /*deleted*/ undefined);
- }
+ const existingVSet = forward.get(k);
+ forward.set(k, vSet);
- 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);
- }
+ existingVSet?.forEach(v => {
+ if (!vSet.has(v)) {
+ deleteFromMultimap(reverse, v, k);
+ }
+ });
- function deleteFromMultimap(map: ESMap>, k: K, v: V): boolean {
- const set = map.get(k);
+ vSet.forEach(v => {
+ if (!existingVSet?.has(v)) {
+ addToMultimap(reverse, v, k);
+ }
+ });
- if (set?.delete(v)) {
- if (!set.size) {
- map.delete(k);
- }
- return true;
- }
+ return map;
+ },
+ };
- return false;
+ return map;
}
- /**
- * Compute the hash to store the shape of the file
- */
- export type ComputeHash = ((data: string) => string) | undefined;
+ return create(new Map>(), new Map>(), /*deleted*/ undefined);
+ }
- function getReferencedFilesFromImportedModuleSymbol(symbol: Symbol): Path[] {
- return mapDefined(symbol.declarations, declaration => getSourceFileOfNode(declaration)?.resolvedPath);
+ 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);
+ }
- /**
- * 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 deleteFromMultimap(map: ESMap>, k: K, v: V): boolean {
+ const set = map.get(k);
- /**
- * 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);
+ if (set?.delete(v)) {
+ if (!set.size) {
+ map.delete(k);
+ }
+ return true;
}
- /**
- * 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);
- }
- }
+ return false;
+ }
- 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);
- }
- }
+ /**
+ * Compute the hash to store the shape of the file
+ */
+ export type ComputeHash = ((data: string) => string) | undefined;
- // Handle type reference directives
- if (sourceFile.resolvedTypeReferenceDirectiveNames) {
- sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => {
- if (!resolvedTypeReferenceDirective) {
- return;
- }
+ function getReferencedFilesFromImportedModuleSymbol(symbol: Symbol): Path[] {
+ return mapDefined(symbol.declarations, declaration => getSourceFileOfNode(declaration)?.resolvedPath);
+ }
- const fileName = resolvedTypeReferenceDirective.resolvedFileName!; // TODO: GH#18217
- const typeFilePath = getReferencedFileFromFileName(program, fileName, sourceFileDirectory, getCanonicalFileName);
- addReferencedFile(typeFilePath);
- });
- }
+ /**
+ * 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);
+ }
- // 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;
+ /**
+ * 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);
+ }
- // Add any file other than our own as reference
- addReferenceFromAmbientModule(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);
}
+ }
- // From ambient modules
- for (const ambientModule of program.getTypeChecker().getAmbientModules()) {
- if (ambientModule.declarations && ambientModule.declarations.length > 1) {
- addReferenceFromAmbientModule(ambientModule);
- }
+ 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);
}
+ }
- return referencedFiles;
-
- function addReferenceFromAmbientModule(symbol: Symbol) {
- if (!symbol.declarations) {
+ // 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);
+ });
+ }
+
+ // 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
- 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: BuilderState | 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 options = newProgram.getCompilerOptions();
- const isOutFile = outFile(options);
- const referencedMap = options.module !== ModuleKind.None && !isOutFile ?
- createManyToManyPathMap() : undefined;
- const exportedModulesMap = referencedMap ? createManyToManyPathMap() : undefined;
- 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 oldUncommittedSignature = useOldState ? oldState!.oldSignatures?.get(sourceFile.resolvedPath) : undefined;
- const signature = oldUncommittedSignature === undefined ?
- useOldState ? oldState!.fileInfos.get(sourceFile.resolvedPath)?.signature : undefined :
- oldUncommittedSignature || 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 oldUncommittedExportedModules = oldState!.oldExportedModulesMap?.get(sourceFile.resolvedPath);
- const exportedModules = oldUncommittedExportedModules === undefined ?
- oldState!.exportedModulesMap!.getValues(sourceFile.resolvedPath) :
- oldUncommittedExportedModules || undefined;
- 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,
- // No need to calculate affectsGlobalScope with --out since its not used at all
- affectsGlobalScope: !isOutFile ? isFileAffectingGlobalScope(sourceFile) || undefined : undefined,
- impliedFormat: sourceFile.impliedNodeFormat
- });
}
-
- return {
- fileInfos,
- referencedMap,
- exportedModulesMap,
- useFileVersionAsSignature: !disableUseFileVersionAsSignature && !useOldState
- };
}
- /**
- * Releases needed properties
- */
- export function releaseCache(state: BuilderState) {
- state.allFilesExcludingDefaultLibraryFile = undefined;
- state.allFileNames = undefined;
+ function addReferencedFile(referencedPath: Path) {
+ (referencedFiles || (referencedFiles = new Set())).add(referencedPath);
}
+ }
- /**
- * Gets the files affected by the path from the program
- */
- export function getFilesAffectedBy(
- state: BuilderState,
- programOfThisState: Program,
- path: Path,
- cancellationToken: CancellationToken | undefined,
- computeHash: ComputeHash,
- getCanonicalFileName: GetCanonicalFileName,
- ): readonly SourceFile[] {
- const result = getFilesAffectedByWithOldState(
- state,
- programOfThisState,
- path,
- cancellationToken,
- computeHash,
- getCanonicalFileName,
- );
- state.oldSignatures?.clear();
- state.oldExportedModulesMap?.clear();
- return result;
- }
+ /**
+ * Returns true if oldState is reusable, that is the emitKind = module/non module has not changed
+ */
+ export function canReuseOldState(newReferencedMap: ReadonlyManyToManyPathMap | undefined, oldState: BuilderState | undefined) {
+ return oldState && !oldState.referencedMap === !newReferencedMap;
+ }
- export function getFilesAffectedByWithOldState(
- state: BuilderState,
- programOfThisState: Program,
- path: Path,
- cancellationToken: CancellationToken | undefined,
- computeHash: ComputeHash,
- getCanonicalFileName: GetCanonicalFileName,
- ): readonly SourceFile[] {
- 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 Map();
+ const options = newProgram.getCompilerOptions();
+ const isOutFile = outFile(options);
+ const referencedMap = options.module !== ModuleKind.None && !isOutFile ?
+ createManyToManyPathMap() : undefined;
+ const exportedModulesMap = referencedMap ? createManyToManyPathMap() : undefined;
+ 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 oldUncommittedSignature = useOldState ? oldState!.oldSignatures?.get(sourceFile.resolvedPath) : undefined;
+ const signature = oldUncommittedSignature === undefined ?
+ useOldState ? oldState!.fileInfos.get(sourceFile.resolvedPath)?.signature : undefined :
+ oldUncommittedSignature || 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 oldUncommittedExportedModules = oldState!.oldExportedModulesMap?.get(sourceFile.resolvedPath);
+ const exportedModules = oldUncommittedExportedModules === undefined ?
+ oldState!.exportedModulesMap!.getValues(sourceFile.resolvedPath) :
+ oldUncommittedExportedModules || undefined;
+ if (exportedModules) {
+ exportedModulesMap!.set(sourceFile.resolvedPath, exportedModules);
+ }
+ }
}
+ fileInfos.set(sourceFile.resolvedPath, {
+ version,
+ signature,
+ // No need to calculate affectsGlobalScope with --out since its not used at all
+ affectsGlobalScope: !isOutFile ? isFileAffectingGlobalScope(sourceFile) || undefined : undefined,
+ impliedFormat: sourceFile.impliedNodeFormat
+ });
+ }
- if (!updateShapeSignature(state, programOfThisState, sourceFile, cancellationToken, computeHash, getCanonicalFileName)) {
- return [sourceFile];
- }
+ return {
+ fileInfos,
+ referencedMap,
+ exportedModulesMap,
+ useFileVersionAsSignature: !disableUseFileVersionAsSignature && !useOldState
+ };
+ }
- return (state.referencedMap ? getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit)(state, programOfThisState, sourceFile, cancellationToken, computeHash, getCanonicalFileName);
- }
+ /**
+ * Releases needed properties
+ */
+ export function releaseCache(state: BuilderState) {
+ state.allFilesExcludingDefaultLibraryFile = undefined;
+ state.allFileNames = undefined;
+ }
- export function updateSignatureOfFile(state: BuilderState, signature: string | undefined, path: Path) {
- state.fileInfos.get(path)!.signature = signature;
- (state.hasCalledUpdateShapeSignature ||= new Set()).add(path);
+ /**
+ * Gets the files affected by the path from the program
+ */
+ export function getFilesAffectedBy(
+ state: BuilderState,
+ programOfThisState: Program,
+ path: Path,
+ cancellationToken: CancellationToken | undefined,
+ computeHash: ComputeHash,
+ getCanonicalFileName: GetCanonicalFileName,
+ ): readonly SourceFile[] {
+ const result = getFilesAffectedByWithOldState(
+ state,
+ programOfThisState,
+ path,
+ cancellationToken,
+ computeHash,
+ getCanonicalFileName,
+ );
+ state.oldSignatures?.clear();
+ state.oldExportedModulesMap?.clear();
+ return result;
+ }
+
+ export function getFilesAffectedByWithOldState(
+ state: BuilderState,
+ programOfThisState: Program,
+ path: Path,
+ cancellationToken: CancellationToken | undefined,
+ computeHash: ComputeHash,
+ getCanonicalFileName: GetCanonicalFileName,
+ ): readonly SourceFile[] {
+ const sourceFile = programOfThisState.getSourceFileByPath(path);
+ if (!sourceFile) {
+ return emptyArray;
}
- /**
- * Returns if the shape of the signature has changed since last emit
- */
- export function updateShapeSignature(
- state: BuilderState,
- programOfThisState: Program,
- sourceFile: SourceFile,
- cancellationToken: CancellationToken | undefined,
- computeHash: ComputeHash,
- getCanonicalFileName: GetCanonicalFileName,
- useFileVersionAsSignature = state.useFileVersionAsSignature
- ) {
- // 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)) return false;
-
- const info = state.fileInfos.get(sourceFile.resolvedPath)!;
- const prevSignature = info.signature;
- let latestSignature: string | undefined;
- if (!sourceFile.isDeclarationFile && !useFileVersionAsSignature) {
- programOfThisState.emit(
- sourceFile,
- (fileName, text, _writeByteOrderMark, _onError, sourceFiles, data) => {
- Debug.assert(isDeclarationFileName(fileName), `File extension for signature expected to be dts: Got:: ${fileName}`);
- latestSignature = computeSignatureWithDiagnostics(
- sourceFile,
- text,
- computeHash,
- getCanonicalFileName,
- data,
- );
- if (latestSignature !== prevSignature) {
- updateExportedModules(state, sourceFile, sourceFiles![0].exportedModulesFromDeclarationEmit);
- }
- },
- cancellationToken,
- /*emitOnlyDtsFiles*/ true,
- /*customTransformers*/ undefined,
- /*forceDtsEmit*/ true
- );
- }
- // Default is to use file version as signature
- if (latestSignature === undefined) {
- latestSignature = sourceFile.version;
- if (state.exportedModulesMap && latestSignature !== prevSignature) {
- (state.oldExportedModulesMap ||= new Map()).set(sourceFile.resolvedPath, state.exportedModulesMap.getValues(sourceFile.resolvedPath) || false);
- // All the references in this file are exported
- const references = state.referencedMap ? state.referencedMap.getValues(sourceFile.resolvedPath) : undefined;
- if (references) {
- state.exportedModulesMap.set(sourceFile.resolvedPath, references);
- }
- else {
- state.exportedModulesMap.deleteKey(sourceFile.resolvedPath);
- }
- }
- }
- (state.oldSignatures ||= new Map()).set(sourceFile.resolvedPath, prevSignature || false);
- (state.hasCalledUpdateShapeSignature ||= new Set()).add(sourceFile.resolvedPath);
- info.signature = latestSignature;
- return latestSignature !== prevSignature;
+ if (!updateShapeSignature(state, programOfThisState, sourceFile, cancellationToken, computeHash, getCanonicalFileName)) {
+ return [sourceFile];
}
- /**
- * Coverts the declaration emit result into exported modules map
- */
- export function updateExportedModules(state: BuilderState, sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined) {
- if (!state.exportedModulesMap) return;
- (state.oldExportedModulesMap ||= new Map()).set(sourceFile.resolvedPath, state.exportedModulesMap.getValues(sourceFile.resolvedPath) || false);
- if (!exportedModulesFromDeclarationEmit) {
- state.exportedModulesMap.deleteKey(sourceFile.resolvedPath);
- return;
- }
+ return (state.referencedMap ? getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit)(state, programOfThisState, sourceFile, cancellationToken, computeHash, getCanonicalFileName);
+ }
- let exportedModules: Set | undefined;
- exportedModulesFromDeclarationEmit.forEach(symbol => addExportedModule(getReferencedFilesFromImportedModuleSymbol(symbol)));
- if (exportedModules) {
- state.exportedModulesMap.set(sourceFile.resolvedPath, exportedModules);
- }
- else {
- state.exportedModulesMap.deleteKey(sourceFile.resolvedPath);
- }
+ export function updateSignatureOfFile(state: BuilderState, signature: string | undefined, path: Path) {
+ state.fileInfos.get(path)!.signature = signature;
+ (state.hasCalledUpdateShapeSignature ||= new Set()).add(path);
+ }
- function addExportedModule(exportedModulePaths: Path[] | undefined) {
- if (exportedModulePaths?.length) {
- if (!exportedModules) {
- exportedModules = new Set();
+ /**
+ * Returns if the shape of the signature has changed since last emit
+ */
+ export function updateShapeSignature(
+ state: BuilderState,
+ programOfThisState: Program,
+ sourceFile: SourceFile,
+ cancellationToken: CancellationToken | undefined,
+ computeHash: ComputeHash,
+ getCanonicalFileName: GetCanonicalFileName,
+ useFileVersionAsSignature = state.useFileVersionAsSignature
+ ) {
+ // 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)) return false;
+
+ const info = state.fileInfos.get(sourceFile.resolvedPath)!;
+ const prevSignature = info.signature;
+ let latestSignature: string | undefined;
+ if (!sourceFile.isDeclarationFile && !useFileVersionAsSignature) {
+ programOfThisState.emit(
+ sourceFile,
+ (fileName, text, _writeByteOrderMark, _onError, sourceFiles, data) => {
+ Debug.assert(isDeclarationFileName(fileName), `File extension for signature expected to be dts: Got:: ${fileName}`);
+ latestSignature = computeSignatureWithDiagnostics(
+ sourceFile,
+ text,
+ computeHash,
+ getCanonicalFileName,
+ data,
+ );
+ if (latestSignature !== prevSignature) {
+ updateExportedModules(state, sourceFile, sourceFiles![0].exportedModulesFromDeclarationEmit);
}
- exportedModulePaths.forEach(path => exportedModules!.add(path));
+ },
+ cancellationToken,
+ /*emitOnlyDtsFiles*/ true,
+ /*customTransformers*/ undefined,
+ /*forceDtsEmit*/ true
+ );
+ }
+ // Default is to use file version as signature
+ if (latestSignature === undefined) {
+ latestSignature = sourceFile.version;
+ if (state.exportedModulesMap && latestSignature !== prevSignature) {
+ (state.oldExportedModulesMap ||= new Map()).set(sourceFile.resolvedPath, state.exportedModulesMap.getValues(sourceFile.resolvedPath) || false);
+ // All the references in this file are exported
+ const references = state.referencedMap ? state.referencedMap.getValues(sourceFile.resolvedPath) : undefined;
+ if (references) {
+ state.exportedModulesMap.set(sourceFile.resolvedPath, references);
+ }
+ else {
+ state.exportedModulesMap.deleteKey(sourceFile.resolvedPath);
}
}
}
+ (state.oldSignatures ||= new Map()).set(sourceFile.resolvedPath, prevSignature || false);
+ (state.hasCalledUpdateShapeSignature ||= new Set()).add(sourceFile.resolvedPath);
+ info.signature = 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
+ */
+ export function updateExportedModules(state: BuilderState, sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined) {
+ if (!state.exportedModulesMap) return;
+ (state.oldExportedModulesMap ||= new Map()).set(sourceFile.resolvedPath, state.exportedModulesMap.getValues(sourceFile.resolvedPath) || false);
+ if (!exportedModulesFromDeclarationEmit) {
+ state.exportedModulesMap.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: Set | undefined;
+ exportedModulesFromDeclarationEmit.forEach(symbol => addExportedModule(getReferencedFilesFromImportedModuleSymbol(symbol)));
+ if (exportedModules) {
+ state.exportedModulesMap.set(sourceFile.resolvedPath, exportedModules);
+ }
+ else {
+ state.exportedModulesMap.deleteKey(sourceFile.resolvedPath);
+ }
- // 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);
- }
- }
+ function addExportedModule(exportedModulePaths: Path[] | undefined) {
+ if (exportedModulePaths?.length) {
+ if (!exportedModules) {
+ exportedModules = new Set();
}
+ exportedModulePaths.forEach(path => exportedModules!.add(path));
}
-
- return arrayFrom(mapDefinedIterator(seenMap.keys(), path => programOfThisState.getSourceFileByPath(path)?.fileName ?? path));
}
+ }
- /**
- * 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 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 modular code, gets the files affected by the sourceFile whose shape has changed
- */
- function getFilesAffectedByUpdatedShapeWhenModuleEmit(
- state: BuilderState,
- programOfThisState: Program,
- sourceFileWithUpdatedShape: SourceFile,
- cancellationToken: CancellationToken | undefined,
- computeHash: ComputeHash,
- getCanonicalFileName: GetCanonicalFileName,
- ) {
- if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) {
- 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);
+ }
- const compilerOptions = programOfThisState.getCompilerOptions();
- if (compilerOptions && (compilerOptions.isolatedModules || outFile(compilerOptions))) {
- return [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,
+ cancellationToken: CancellationToken | undefined,
+ computeHash: ComputeHash,
+ getCanonicalFileName: GetCanonicalFileName,
+ ) {
+ if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) {
+ return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, 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, cancellationToken, computeHash, getCanonicalFileName)) {
- queue.push(...getReferencedByPaths(state, currentSourceFile.resolvedPath));
- }
+ 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, cancellationToken, computeHash, getCanonicalFileName)) {
+ 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 0b4ad73acc4d4..49fd891fa1c94 100644
--- a/src/compiler/builderStatePublic.ts
+++ b/src/compiler/builderStatePublic.ts
@@ -1,14 +1,14 @@
-namespace ts {
- export interface EmitOutput {
- outputFiles: OutputFile[];
- emitSkipped: boolean;
- /* @internal */ diagnostics: readonly Diagnostic[];
- }
+import { Diagnostic, WriteFileCallbackData } from "./_namespaces/ts";
- export interface OutputFile {
- name: string;
- writeByteOrderMark: boolean;
- text: string;
- /* @internal */ data?: WriteFileCallbackData;
- }
+export interface EmitOutput {
+ outputFiles: OutputFile[];
+ emitSkipped: boolean;
+ /* @internal */ diagnostics: readonly Diagnostic[];
+}
+
+export interface OutputFile {
+ name: string;
+ writeByteOrderMark: boolean;
+ text: string;
+ /* @internal */ data?: WriteFileCallbackData;
}
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 1bacbfbead2e4..790081808d742 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -1,25050 +1,25273 @@
-/* @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,
- }
-
- export 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
- IsUndefined = 1 << 24, // Contains undefined or intersection with undefined
- IsNull = 1 << 25, // Contains null or intersection with null
- IsUndefinedOrNull = IsUndefined | IsNull,
- All = (1 << 27) - 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,
- VoidFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy,
- UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy | IsUndefined,
- NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy | IsNull,
- EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull | IsUndefinedOrNull),
- EmptyObjectFacts = All & ~IsUndefinedOrNull,
- UnknownFacts = All & ~IsUndefinedOrNull,
- AllTypeofNE = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined,
- // Masks
- OrFactsMask = TypeofEQFunction | TypeofNEObject,
- AndFactsMask = All & ~OrFactsMask,
- }
-
- 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
- }));
-
- type TypeSystemEntity = Node | Symbol | Type | Signature;
-
- const enum TypeSystemPropertyName {
- Type,
- ResolvedBaseConstructorType,
- DeclaredType,
- ResolvedReturnType,
- ImmediateBaseConstraint,
- EnumTagType,
- ResolvedTypeArguments,
- ResolvedBaseTypes,
- WriteType,
- }
-
- export 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
- IsForStringLiteralArgumentCompletions = 1 << 5, // Do not infer from the argument currently being typed
- RestBindingElement = 1 << 6, // Checking a type that is going to be used to determine the type of a rest binding element
- // e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`,
- // we need to preserve generic types instead of substituting them for constraints
- }
-
- export const enum SignatureCheckMode {
- BivariantCallback = 1 << 0,
- StrictCallback = 1 << 1,
- IgnoreReturnTypes = 1 << 2,
- StrictArity = 1 << 3,
- Callback = BivariantCallback | StrictCallback,
- }
-
- const enum IntersectionState {
- None = 0,
- Source = 1 << 0,
- Target = 1 << 1,
- }
-
- const enum RecursionFlags {
- None = 0,
- Source = 1 << 0,
- Target = 1 << 1,
- Both = Source | Target,
- }
-
- const enum MappedTypeModifiers {
- IncludeReadonly = 1 << 0,
- ExcludeReadonly = 1 << 1,
- IncludeOptional = 1 << 2,
- ExcludeOptional = 1 << 3,
- }
-
- const enum ExpandingFlags {
- None = 0,
- Source = 1,
- Target = 1 << 1,
- Both = Source | Target,
- }
-
- const enum MembersOrExportsResolutionKind {
- resolvedExports = "resolvedExports",
- resolvedMembers = "resolvedMembers"
- }
+import * as ts from "./_namespaces/ts";
+import {
+ __String, AccessExpression, AccessFlags, AccessorDeclaration, addRange, addRelatedInfo, addSyntheticLeadingComment,
+ AllAccessorDeclarations, and, AnonymousType, AnyImportOrReExport, AnyImportSyntax, append, appendIfUnique,
+ ArrayBindingPattern, arrayFrom, arrayIsHomogeneous, ArrayLiteralExpression, arrayOf, arraysEqual, arrayToMultiMap,
+ ArrayTypeNode, ArrowFunction, AssertionExpression, AssignmentDeclarationKind, AssignmentKind, AssignmentPattern,
+ AwaitExpression, BaseType, BigIntLiteral, BigIntLiteralType, BinaryExpression, BinaryOperatorToken, binarySearch,
+ BindableObjectDefinePropertyCall, BindingElement, BindingElementGrandparent, BindingName, BindingPattern,
+ bindSourceFile, Block, BreakOrContinueStatement, CallChain, CallExpression, CallLikeExpression,
+ CallSignatureDeclaration, CancellationToken, canHaveDecorators, canHaveExportModifier, canHaveIllegalDecorators,
+ canHaveIllegalModifiers, canHaveModifiers, cartesianProduct, CaseBlock, CaseClause, CaseOrDefaultClause, cast,
+ chainDiagnosticMessages, CharacterCodes, CheckFlags, ClassDeclaration, ClassElement, ClassExpression,
+ ClassLikeDeclaration, ClassStaticBlockDeclaration, clear, combinePaths, compareDiagnostics, comparePaths,
+ compareValues, Comparison, CompilerOptions, ComputedPropertyName, concatenate, concatenateDiagnosticMessageChains,
+ ConditionalExpression, ConditionalRoot, ConditionalType, ConditionalTypeNode, ConstructorDeclaration,
+ ConstructorTypeNode, ConstructSignatureDeclaration, contains, containsParseError, ContextFlags, copyEntries,
+ countWhere, createBinaryExpressionTrampoline, createCompilerDiagnostic, createDiagnosticCollection,
+ createDiagnosticForFileFromMessageChain, createDiagnosticForNode, createDiagnosticForNodeArray,
+ createDiagnosticForNodeFromMessageChain, createDiagnosticMessageChainFromDiagnostic, createEmptyExports,
+ createFileDiagnostic, createGetCanonicalFileName, createGetSymbolWalker, createPrinter,
+ createPropertyNameNodeForIdentifierOrLiteral, createScanner, createSymbolTable, createTextWriter,
+ createUnderscoreEscapedMultiMap, Debug, Declaration, DeclarationName, declarationNameToString, DeclarationStatement,
+ DeclarationWithTypeParameterChildren, DeclarationWithTypeParameters, Decorator, deduplicate, DefaultClause,
+ defaultMaximumTruncationLength, DeferredTypeReference, DeleteExpression, Diagnostic, DiagnosticCategory,
+ DiagnosticMessage, DiagnosticMessageChain, DiagnosticRelatedInformation, Diagnostics, DiagnosticWithLocation,
+ DoStatement, DynamicNamedDeclaration, ElementAccessChain, ElementAccessExpression, ElementFlags, EmitFlags,
+ EmitHint, EmitResolver, EmitTextWriter, emptyArray, endsWith, EntityName, EntityNameExpression,
+ EntityNameOrEntityNameExpression, entityNameToString, EnumDeclaration, EnumMember, equateValues,
+ escapeLeadingUnderscores, escapeString, ESMap, every, EvolvingArrayType, ExclamationToken, ExportAssignment,
+ exportAssignmentIsAlias, ExportDeclaration, ExportSpecifier, Expression, expressionResultIsUnused,
+ ExpressionStatement, ExpressionWithTypeArguments, Extension, ExternalEmitHelpers, externalHelpersModuleNameText,
+ factory, fileExtensionIs, fileExtensionIsOneOf, filter, find, findAncestor, findBestPatternMatch, findIndex,
+ findLast, findLastIndex, findUseStrictPrologue, first, firstDefined, firstOrUndefined, flatMap, flatten,
+ FlowArrayMutation, FlowAssignment, FlowCall, FlowCondition, FlowFlags, FlowLabel, FlowNode, FlowReduceLabel,
+ FlowStart, FlowSwitchClause, FlowType, forEach, forEachChild, forEachChildRecursively,
+ forEachEnclosingBlockScopeContainer, forEachEntry, forEachImportClauseDeclaration, forEachKey,
+ forEachReturnStatement, forEachYieldExpression, ForInOrOfStatement, ForInStatement, formatMessage, ForOfStatement,
+ ForStatement, FreshableIntrinsicType, FreshableType, FreshObjectLiteralType, FunctionDeclaration,
+ FunctionExpression, FunctionFlags, FunctionLikeDeclaration, FunctionTypeNode, GenericType, GetAccessorDeclaration,
+ getAliasDeclarationFromName, getAllAccessorDeclarations, getAllowSyntheticDefaultImports, getAncestor,
+ getAssignedExpandoInitializer, getAssignmentDeclarationKind, getAssignmentDeclarationPropertyAccessKind,
+ getAssignmentTargetKind, getCheckFlags, getClassExtendsHeritageElement, getClassLikeDeclarationOfSymbol,
+ getCombinedLocalAndExportSymbolFlags, getCombinedModifierFlags, getCombinedNodeFlags, getContainingClass,
+ getContainingFunction, getContainingFunctionOrClassStaticBlock, getDeclarationModifierFlagsFromSymbol,
+ getDeclarationOfKind, getDeclarationsOfKind, getDeclaredExpandoInitializer, getDirectoryPath,
+ getEffectiveBaseTypeNode, getEffectiveConstraintOfTypeParameter, getEffectiveContainerForJSDocTemplateTag,
+ getEffectiveImplementsTypeNodes, getEffectiveInitializer, getEffectiveJSDocHost, getEffectiveModifierFlags,
+ getEffectiveReturnTypeNode, getEffectiveSetAccessorTypeAnnotationNode, getEffectiveTypeAnnotationNode,
+ getEffectiveTypeParameterDeclarations, getElementOrPropertyAccessName, getEmitDeclarations, getEmitModuleKind,
+ getEmitModuleResolutionKind, getEmitScriptTarget, getEnclosingBlockScopeContainer, getEntityNameFromTypeNode,
+ getEntries, getErrorSpanForNode, getEscapedTextOfIdentifierOrLiteral, getESModuleInterop, getExpandoInitializer,
+ getExportAssignmentExpression, getExternalModuleImportEqualsDeclarationExpression, getExternalModuleName,
+ getExternalModuleRequireArgument, getFirstConstructorWithBody, getFirstIdentifier, getFunctionFlags,
+ getHostSignatureFromJSDoc, getImmediatelyInvokedFunctionExpression, getInitializerOfBinaryExpression,
+ getInterfaceBaseTypeNodes, getInvokedExpression, getJSDocClassTag, getJSDocDeprecatedTag, getJSDocEnumTag,
+ getJSDocHost, getJSDocParameterTags, getJSDocRoot, getJSDocTags, getJSDocThisTag, getJSDocType,
+ getJSDocTypeAssertionType, getJSDocTypeParameterDeclarations, getJSDocTypeTag, getJSXImplicitImportBase,
+ getJSXRuntimeImport, getJSXTransformEnabled, getLeftmostAccessExpression, getLineAndCharacterOfPosition,
+ getLocalSymbolForExportDefault, getMembersOfDeclaration, getModeForUsageLocation, getModifiers,
+ getModuleInstanceState, getNameFromIndexInfo, getNameOfDeclaration, getNameOfExpando, getNamespaceDeclarationNode,
+ getNewTargetContainer, getNonAugmentationDeclaration, getNormalizedAbsolutePath, getObjectFlags, getOriginalNode,
+ getOrUpdate, getOwnKeys, getParameterSymbolFromJSDoc, getParseTreeNode, getPropertyAssignmentAliasLikeExpression,
+ getPropertyNameForPropertyNameNode, getResolutionDiagnostic, getResolutionModeOverrideForClause,
+ getResolvedExternalModuleName, getResolvedModule, getRestParameterElementType, getRootDeclaration,
+ getScriptTargetFeatures, getSelectedEffectiveModifierFlags, getSemanticJsxChildren, getSetAccessorValueParameter,
+ getSingleVariableOfVariableStatement, getSourceFileOfModule, getSourceFileOfNode, getSpanOfTokenAtPosition,
+ getSpellingSuggestion, getStrictOptionValue, getSuperContainer, getSymbolNameForPrivateIdentifier,
+ getTextOfIdentifierOrLiteral, getTextOfJSDocComment, getTextOfNode, getTextOfPropertyName, getThisContainer,
+ getThisParameter, getTrailingSemicolonDeferringWriter, getTypeParameterFromJsDoc, getTypesPackageName,
+ getUseDefineForClassFields, group, hasAbstractModifier, hasAccessorModifier, hasAmbientModifier,
+ hasContextSensitiveParameters, hasDecorators, HasDecorators, hasDynamicName, hasEffectiveModifier,
+ hasEffectiveModifiers, hasEffectiveReadonlyModifier, HasExpressionInitializer, hasExtension, HasIllegalDecorators,
+ HasIllegalModifiers, hasInitializer, HasInitializer, hasJSDocNodes, hasJSDocParameterTags, hasJsonModuleEmitEnabled,
+ HasModifiers, hasOnlyExpressionInitializer, hasOverrideModifier, hasPossibleExternalModuleReference,
+ hasQuestionToken, hasRestParameter, hasScopeMarker, hasStaticModifier, hasSyntacticModifier, hasSyntacticModifiers,
+ HeritageClause, Identifier, IdentifierTypePredicate, idText, IfStatement, ImportCall, ImportClause,
+ ImportDeclaration, ImportEqualsDeclaration, ImportOrExportSpecifier, ImportsNotUsedAsValues, ImportSpecifier,
+ ImportTypeAssertionContainer, ImportTypeNode, IncompleteType, IndexedAccessType, IndexedAccessTypeNode, IndexInfo,
+ IndexKind, indexOfNode, IndexSignatureDeclaration, IndexType, indicesOf, InferenceContext, InferenceFlags,
+ InferenceInfo, InferencePriority, InferTypeNode, InstantiableType, InstantiationExpressionType,
+ InterfaceDeclaration, InterfaceType, InterfaceTypeWithDeclaredMembers, InternalSymbolName, IntersectionType,
+ IntersectionTypeNode, IntrinsicType, introducesArgumentsExoticObject, isAccessExpression, isAccessor,
+ isAliasableExpression, isAmbientModule, isArray, isArrayBindingPattern, isArrayLiteralExpression, isArrowFunction,
+ isAssertionExpression, isAssignmentDeclaration, isAssignmentExpression, isAssignmentOperator, isAssignmentPattern,
+ isAssignmentTarget, isAsyncFunction, isAutoAccessorPropertyDeclaration, isBinaryExpression,
+ isBindableObjectDefinePropertyCall, isBindableStaticElementAccessExpression, isBindableStaticNameExpression,
+ isBindingElement, isBindingPattern, isBlock, isBlockOrCatchScoped, isBlockScopedContainerTopLevel, isCallChain,
+ isCallExpression, isCallLikeExpression, isCallOrNewExpression, isCallSignatureDeclaration, isCatchClause,
+ isCatchClauseVariableDeclarationOrBindingElement, isCheckJsEnabledForFile, isChildOfNodeWithKind,
+ isClassDeclaration, isClassElement, isClassExpression, isClassLike, isClassStaticBlockDeclaration, isCommaSequence,
+ isCommonJsExportedExpression, isCommonJsExportPropertyAssignment, isComputedNonLiteralName, isComputedPropertyName,
+ isConstructorDeclaration, isConstructorTypeNode, isConstTypeReference, isDeclaration, isDeclarationName,
+ isDeclarationReadonly, isDecorator, isDefaultedExpandoInitializer, isDeleteTarget, isDottedName, isDynamicName,
+ isEffectiveExternalModule, isElementAccessExpression, isEntityName, isEntityNameExpression, isEnumConst,
+ isEnumDeclaration, isEnumMember, isExclusivelyTypeOnlyImportOrExport, isExportAssignment, isExportDeclaration,
+ isExportsIdentifier, isExportSpecifier, isExpression, isExpressionNode, isExpressionOfOptionalChainRoot,
+ isExpressionStatement, isExpressionWithTypeArguments, isExpressionWithTypeArgumentsInClassExtendsClause,
+ isExternalModule, isExternalModuleAugmentation, isExternalModuleImportEqualsDeclaration, isExternalModuleIndicator,
+ isExternalModuleNameRelative, isExternalModuleReference, isExternalOrCommonJsModule, isForInOrOfStatement,
+ isForInStatement, isForOfStatement, isForStatement, isFunctionDeclaration, isFunctionExpression,
+ isFunctionExpressionOrArrowFunction, isFunctionLike, isFunctionLikeDeclaration,
+ isFunctionLikeOrClassStaticBlockDeclaration, isFunctionOrModuleBlock, isFunctionTypeNode, isGeneratedIdentifier,
+ isGetAccessor, isGetAccessorDeclaration, isGetOrSetAccessorDeclaration, isGlobalScopeAugmentation, isHeritageClause,
+ isIdentifier, isIdentifierStart, isIdentifierText, isIdentifierTypePredicate, isIdentifierTypeReference,
+ isIfStatement, isImportCall, isImportClause, isImportDeclaration, isImportEqualsDeclaration, isImportKeyword,
+ isImportOrExportSpecifier, isImportSpecifier, isImportTypeNode, isIndexedAccessTypeNode, isInExpressionContext,
+ isInfinityOrNaNString, isInJSDoc, isInJSFile, isInJsonFile, isInterfaceDeclaration,
+ isInternalModuleImportEqualsDeclaration, isInTopLevelContext, isIntrinsicJsxName, isIterationStatement,
+ isJSDocAllType, isJSDocAugmentsTag, isJSDocCallbackTag, isJSDocConstructSignature, isJSDocFunctionType,
+ isJSDocIndexSignature, isJSDocLinkLike, isJSDocMemberName, isJSDocNameReference, isJSDocNode,
+ isJSDocNonNullableType, isJSDocNullableType, isJSDocOptionalType, isJSDocParameterTag, isJSDocPropertyLikeTag,
+ isJSDocReturnTag, isJSDocSignature, isJSDocTemplateTag, isJSDocTypeAlias, isJSDocTypeAssertion, isJSDocTypedefTag,
+ isJSDocTypeExpression, isJSDocTypeLiteral, isJSDocTypeTag, isJSDocUnknownType, isJSDocVariadicType,
+ isJsonSourceFile, isJsxAttribute, isJsxAttributeLike, isJsxAttributes, isJsxElement, isJsxOpeningElement,
+ isJsxOpeningFragment, isJsxOpeningLikeElement, isJsxSelfClosingElement, isJsxSpreadAttribute, isJSXTagName,
+ isKnownSymbol, isLateVisibilityPaintedStatement, isLeftHandSideExpression, isLet, isLineBreak,
+ isLiteralComputedPropertyDeclarationName, isLiteralExpressionOfObject, isLiteralImportTypeNode, isLiteralTypeNode,
+ isMetaProperty, isMethodDeclaration, isMethodSignature, isModifier, isModuleBlock, isModuleDeclaration,
+ isModuleExportsAccessExpression, isModuleIdentifier, isModuleOrEnumDeclaration, isModuleWithStringLiteralName,
+ isNamedDeclaration, isNamedExports, isNamedTupleMember, isNamespaceExport, isNamespaceExportDeclaration,
+ isNamespaceReexportDeclaration, isNewExpression, isNightly, isNodeDescendantOf, isNullishCoalesce, isNumericLiteral,
+ isNumericLiteralName, isObjectBindingPattern, isObjectLiteralElementLike, isObjectLiteralExpression,
+ isObjectLiteralMethod, isObjectLiteralOrClassExpressionMethodOrAccessor, isOmittedExpression, isOptionalChain,
+ isOptionalChainRoot, isOptionalTypeNode, isOutermostOptionalChain, isParameter, isParameterDeclaration,
+ isParameterOrCatchClauseVariable, isParameterPropertyDeclaration, isParenthesizedExpression,
+ isParenthesizedTypeNode, isPartOfTypeNode, isPartOfTypeQuery, isPlainJsFile, isPrefixUnaryExpression,
+ isPrivateIdentifier, isPrivateIdentifierClassElementDeclaration, isPrivateIdentifierPropertyAccessExpression,
+ isPropertyAccessEntityNameExpression, isPropertyAccessExpression, isPropertyAccessOrQualifiedNameOrImportTypeNode,
+ isPropertyAssignment, isPropertyDeclaration, isPropertyName, isPropertyNameLiteral, isPropertySignature,
+ isPrototypeAccess, isPrototypePropertyAssignment, isPushOrUnshiftIdentifier, isQualifiedName, isRequireCall,
+ isRestParameter, isRestTypeNode, isRightSideOfQualifiedNameOrPropertyAccess,
+ isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName, isSameEntityName, isSetAccessor,
+ isShorthandAmbientModuleSymbol, isShorthandPropertyAssignment, isSingleOrDoubleQuote, isSourceFile, isSourceFileJS,
+ isSpreadAssignment, isSpreadElement, isStatement, isStatementWithLocals, isStatic, isString,
+ isStringANonContextualKeyword, isStringLiteral, isStringLiteralLike, isStringOrNumericLiteralLike, isSuperCall,
+ isSuperProperty, isTaggedTemplateExpression, isTemplateSpan, isThisContainerOrFunctionBlock, isThisIdentifier,
+ isThisInitializedDeclaration, isThisInitializedObjectBindingExpression, isThisInTypeQuery, isThisProperty,
+ isThisTypeParameter, isTransientSymbol, isTupleTypeNode, isTypeAlias, isTypeAliasDeclaration, isTypeDeclaration,
+ isTypeLiteralNode, isTypeNode, isTypeNodeKind, isTypeOfExpression, isTypeOnlyImportOrExportDeclaration,
+ isTypeOperatorNode, isTypeParameterDeclaration, isTypePredicateNode, isTypeQueryNode, isTypeReferenceNode,
+ isTypeReferenceType, isUMDExportSymbol, isValidESSymbolDeclaration, isValidTypeOnlyAliasUseSite,
+ isValueSignatureDeclaration, isVarConst, isVariableDeclaration,
+ isVariableDeclarationInitializedToBareOrAccessedRequire, isVariableDeclarationInVariableStatement,
+ isVariableDeclarationList, isVariableLike, isVariableLikeOrAccessor, isVariableStatement, isWriteAccess,
+ isWriteOnlyAccess, IterableOrIteratorType, IterationTypes, JSDoc, JSDocAugmentsTag, JSDocCallbackTag, JSDocComment,
+ JSDocContainer, JSDocEnumTag, JSDocFunctionType, JSDocImplementsTag, JSDocLink, JSDocLinkCode, JSDocLinkPlain,
+ JSDocMemberName, JSDocNullableType, JSDocOptionalType, JSDocParameterTag, JSDocPrivateTag, JSDocPropertyLikeTag,
+ JSDocPropertyTag, JSDocProtectedTag, JSDocPublicTag, JSDocSignature, JSDocTemplateTag, JSDocTypedefTag,
+ JSDocTypeExpression, JSDocTypeReferencingNode, JSDocTypeTag, JSDocVariadicType, JsxAttribute, JsxAttributeLike,
+ JsxAttributes, JsxChild, JsxClosingElement, JsxElement, JsxEmit, JsxExpression, JsxFlags, JsxFragment,
+ JsxOpeningElement, JsxOpeningFragment, JsxOpeningLikeElement, JsxReferenceKind, JsxSelfClosingElement,
+ JsxSpreadAttribute, JsxTagNameExpression, KeywordTypeNode, LabeledStatement, last, lastOrUndefined,
+ LateBoundBinaryExpressionDeclaration, LateBoundDeclaration, LateBoundName, LateVisibilityPaintedStatement,
+ LeftHandSideExpression, length, LiteralExpression, LiteralType, LiteralTypeNode, mangleScopedPackageName, map, Map,
+ mapDefined, MappedSymbol, MappedType, MappedTypeNode, MatchingKeys, maybeBind, MemberName, MemberOverrideStatus,
+ memoize, MetaProperty, MethodDeclaration, MethodSignature, minAndMax, MinusToken, Modifier, ModifierFlags,
+ modifiersToFlags, modifierToFlag, ModuleBlock, ModuleDeclaration, ModuleInstanceState, ModuleKind,
+ ModuleResolutionKind, moduleSpecifiers, NamedDeclaration, NamedExports, NamedImportsOrExports, NamedTupleMember,
+ NamespaceDeclaration, NamespaceExport, NamespaceExportDeclaration, NamespaceImport, needsScopeMarker, NewExpression,
+ Node, NodeArray, NodeBuilderFlags, nodeCanBeDecorated, NodeCheckFlags, NodeFlags, nodeHasName, nodeIsDecorated,
+ nodeIsMissing, nodeIsPresent, nodeIsSynthesized, NodeLinks, nodeStartsNewLexicalEnvironment, NodeWithTypeArguments,
+ NonNullChain, NonNullExpression, not, noTruncationMaximumTruncationLength, nullTransformationContext,
+ NumberLiteralType, NumericLiteral, objectAllocator, ObjectBindingPattern, ObjectFlags, ObjectFlagsType,
+ ObjectLiteralElementLike, ObjectLiteralExpression, ObjectType, OptionalChain, OptionalTypeNode, or,
+ orderedRemoveItemAt, OuterExpressionKinds, outFile, ParameterDeclaration, parameterIsThisKeyword,
+ ParameterPropertyDeclaration, ParenthesizedExpression, ParenthesizedTypeNode, parseIsolatedEntityName,
+ parseNodeFactory, parsePseudoBigInt, Path, pathIsRelative, PatternAmbientModule, PlusToken, PostfixUnaryExpression,
+ PrefixUnaryExpression, PrivateIdentifier, Program, PromiseOrAwaitableType, PropertyAccessChain,
+ PropertyAccessEntityNameExpression, PropertyAccessExpression, PropertyAssignment, PropertyDeclaration, PropertyName,
+ PropertySignature, PseudoBigInt, pseudoBigIntToString, pushIfUnique, QualifiedName, QuestionToken, rangeEquals,
+ rangeOfNode, rangeOfTypeParameters, ReadonlyESMap, ReadonlyKeyword, reduceLeft, RelationComparisonResult,
+ relativeComplement, removeExtension, removePrefix, replaceElement, resolutionExtensionIsTSOrJson,
+ ResolvedModuleFull, ResolvedType, resolveTripleslashReference, resolvingEmptyArray, RestTypeNode, ReturnStatement,
+ ReverseMappedSymbol, ReverseMappedType, sameMap, SatisfiesExpression, ScriptKind, ScriptTarget, Set,
+ SetAccessorDeclaration, setCommentRange, setEmitFlags, setNodeFlags, setOriginalNode, setParent,
+ setSyntheticLeadingComments, setTextRange, setTextRangePosEnd, setValueDeclaration, ShorthandPropertyAssignment,
+ shouldPreserveConstEnums, Signature, SignatureDeclaration, SignatureFlags, SignatureKind, singleElementArray,
+ skipOuterExpressions, skipParentheses, skipTrivia, skipTypeChecking, some, SourceFile, SpreadAssignment,
+ SpreadElement, startsWith, Statement, stringContains, StringLiteral, StringLiteralLike, StringLiteralType,
+ StringMappingType, stripQuotes, StructuredType, SubstitutionType, sum, SuperCall, SwitchStatement, Symbol,
+ SymbolAccessibility, SymbolAccessibilityResult, SymbolFlags, SymbolFormatFlags, SymbolId, SymbolLinks, symbolName,
+ SymbolTable, SymbolTracker, SymbolVisibilityResult, SyntaxKind, SyntheticDefaultModuleType, SyntheticExpression,
+ TaggedTemplateExpression, TemplateExpression, TemplateLiteralType, TemplateLiteralTypeNode, Ternary,
+ textRangeContainsPositionInclusive, TextSpan, textSpanContainsPosition, textSpanEnd, ThisExpression, ThisTypeNode,
+ ThrowStatement, TokenFlags, tokenToString, tracing, TracingNode, TransientSymbol, tryAddToSet, tryCast,
+ tryExtractTSExtension, tryGetClassImplementingOrExtendingExpressionWithTypeArguments, tryGetExtensionFromPath,
+ tryGetModuleSpecifierFromDeclaration, tryGetPropertyAccessOrIdentifierToString, TryStatement, TupleType,
+ TupleTypeNode, TupleTypeReference, Type, TypeAliasDeclaration, TypeAssertion, TypeChecker, TypeCheckerHost,
+ TypeComparer, TypeElement, TypeFlags, TypeFormatFlags, TypeId, TypeLiteralNode, TypeMapKind, TypeMapper, TypeNode,
+ TypeNodeSyntaxKind, TypeOfExpression, TypeOnlyAliasDeclaration, TypeOnlyCompatibleAliasDeclaration,
+ TypeOperatorNode, TypeParameter, TypeParameterDeclaration, TypePredicate, TypePredicateKind, TypePredicateNode,
+ TypeQueryNode, TypeReference, TypeReferenceNode, TypeReferenceSerializationKind, TypeReferenceType, TypeVariable,
+ UnaryExpression, UnderscoreEscapedMap, unescapeLeadingUnderscores, UnionOrIntersectionType,
+ UnionOrIntersectionTypeNode, UnionReduction, UnionType, UnionTypeNode, UniqueESSymbolType,
+ usingSingleLineStringWriter, VariableDeclaration, VariableDeclarationList, VariableLikeDeclaration,
+ VariableStatement, VarianceFlags, visitEachChild, visitNode, visitNodes, Visitor, VisitResult, VoidExpression,
+ walkUpBindingElementsAndPatterns, walkUpParenthesizedExpressions, walkUpParenthesizedTypes,
+ walkUpParenthesizedTypesAndGetParentAndChild, WhileStatement, WideningContext, WithStatement, YieldExpression,
+} from "./_namespaces/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 UnusedKind {
- Local,
- Parameter,
- }
+}
- /** @param containingNode Node to check for parse error */
- type AddUnusedDiagnostic = (containingNode: Node, type: UnusedKind, diagnostic: DiagnosticWithLocation) => void;
+const enum IterationTypeKind {
+ Yield,
+ Return,
+ Next,
+}
- const isNotOverloadAndNotAccessor = and(isNotOverload, isNotAccessor);
+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 DeclarationMeaning {
- GetAccessor = 1,
- SetAccessor = 2,
- PropertyAssignment = 4,
- Method = 8,
- PrivateStatic = 16,
- GetOrSetAccessor = GetAccessor | SetAccessor,
- PropertyAssignmentOrMethod = PropertyAssignment | Method,
- }
+const enum WideningKind {
+ Normal,
+ FunctionReturn,
+ GeneratorNext,
+ GeneratorYield,
+}
- const enum DeclarationSpaces {
- None = 0,
- ExportValue = 1 << 0,
- ExportType = 1 << 1,
- ExportNamespace = 1 << 2,
- }
+/** @internal */
+export 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
+ IsUndefined = 1 << 24, // Contains undefined or intersection with undefined
+ IsNull = 1 << 25, // Contains null or intersection with null
+ IsUndefinedOrNull = IsUndefined | IsNull,
+ All = (1 << 27) - 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,
+ VoidFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy,
+ UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy | IsUndefined,
+ NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy | IsNull,
+ EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull | IsUndefinedOrNull),
+ EmptyObjectFacts = All & ~IsUndefinedOrNull,
+ UnknownFacts = All & ~IsUndefinedOrNull,
+ AllTypeofNE = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined,
+ // Masks
+ OrFactsMask = TypeofEQFunction | TypeofNEObject,
+ AndFactsMask = All & ~OrFactsMask,
+}
- const enum MinArgumentCountFlags {
- None = 0,
- StrongArityForUntypedJS = 1 << 0,
- VoidIsNonOptional = 1 << 1,
- }
+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
+}));
+
+type TypeSystemEntity = Node | Symbol | Type | Signature;
+
+const enum TypeSystemPropertyName {
+ Type,
+ ResolvedBaseConstructorType,
+ DeclaredType,
+ ResolvedReturnType,
+ ImmediateBaseConstraint,
+ EnumTagType,
+ ResolvedTypeArguments,
+ ResolvedBaseTypes,
+ WriteType,
+}
- const enum IntrinsicTypeKind {
- Uppercase,
- Lowercase,
- Capitalize,
- Uncapitalize
- }
+/** @internal */
+export 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
+ IsForStringLiteralArgumentCompletions = 1 << 5, // Do not infer from the argument currently being typed
+ RestBindingElement = 1 << 6, // Checking a type that is going to be used to determine the type of a rest binding element
+ // e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`,
+ // we need to preserve generic types instead of substituting them for constraints
+}
- const intrinsicTypeKinds: ReadonlyESMap = new Map(getEntries({
- Uppercase: IntrinsicTypeKind.Uppercase,
- Lowercase: IntrinsicTypeKind.Lowercase,
- Capitalize: IntrinsicTypeKind.Capitalize,
- Uncapitalize: IntrinsicTypeKind.Uncapitalize
- }));
+/** @internal */
+export const enum SignatureCheckMode {
+ BivariantCallback = 1 << 0,
+ StrictCallback = 1 << 1,
+ IgnoreReturnTypes = 1 << 2,
+ StrictArity = 1 << 3,
+ Callback = BivariantCallback | StrictCallback,
+}
- function SymbolLinks(this: SymbolLinks) {
- }
+const enum IntersectionState {
+ None = 0,
+ Source = 1 << 0,
+ Target = 1 << 1,
+}
- function NodeLinks(this: NodeLinks) {
- this.flags = 0;
- }
+const enum RecursionFlags {
+ None = 0,
+ Source = 1 << 0,
+ Target = 1 << 1,
+ Both = Source | Target,
+}
- export function getNodeId(node: Node): number {
- if (!node.id) {
- node.id = nextNodeId;
- nextNodeId++;
- }
- return node.id;
- }
+const enum MappedTypeModifiers {
+ IncludeReadonly = 1 << 0,
+ ExcludeReadonly = 1 << 1,
+ IncludeOptional = 1 << 2,
+ ExcludeOptional = 1 << 3,
+}
- export function getSymbolId(symbol: Symbol): SymbolId {
- if (!symbol.id) {
- symbol.id = nextSymbolId;
- nextSymbolId++;
- }
+const enum ExpandingFlags {
+ None = 0,
+ Source = 1,
+ Target = 1 << 1,
+ Both = Source | Target,
+}
- return symbol.id;
- }
+const enum MembersOrExportsResolutionKind {
+ resolvedExports = "resolvedExports",
+ resolvedMembers = "resolvedMembers"
+}
- export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) {
- const moduleState = getModuleInstanceState(node);
- return moduleState === ModuleInstanceState.Instantiated ||
- (preserveConstEnums && moduleState === ModuleInstanceState.ConstEnumOnly);
- }
+const enum UnusedKind {
+ Local,
+ Parameter,
+}
- export function createTypeChecker(host: TypeCheckerHost): 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;
+/** @param containingNode Node to check for parse error */
+type AddUnusedDiagnostic = (containingNode: Node, type: UnusedKind, diagnostic: DiagnosticWithLocation) => void;
- sf.resolvedModules.forEach(r => {
- if (r && r.packageId) map.set(r.packageId.name, r.extension === Extension.Dts || !!map.get(r.packageId.name));
- });
- });
- return map;
- });
+const isNotOverloadAndNotAccessor = and(isNotOverload, isNotAccessor);
- let deferredDiagnosticsCallbacks: (() => void)[] = [];
+const enum DeclarationMeaning {
+ GetAccessor = 1,
+ SetAccessor = 2,
+ PropertyAssignment = 4,
+ Method = 8,
+ PrivateStatic = 16,
+ GetOrSetAccessor = GetAccessor | SetAccessor,
+ PropertyAssignmentOrMethod = PropertyAssignment | Method,
+}
- let addLazyDiagnostic = (arg: () => void) => {
- deferredDiagnosticsCallbacks.push(arg);
- };
+const enum DeclarationSpaces {
+ None = 0,
+ ExportValue = 1 << 0,
+ ExportType = 1 << 1,
+ ExportNamespace = 1 << 2,
+}
- // 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 totalInstantiationCount = 0;
- let instantiationCount = 0;
- let instantiationDepth = 0;
- let inlineLevel = 0;
- let currentNode: Node | undefined;
- let varianceTypeParameter: TypeParameter | 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,
- getIndexInfosOfIndexSymbol,
- 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,
- symbolToNode: nodeBuilder.symbolToNode,
- 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;
- }
- if (contextFlags! & ContextFlags.Completions) {
- return runWithInferenceBlockedFromSourceNode(node, () => getContextualType(node, contextFlags));
- }
- return getContextualType(node, contextFlags);
- },
- getContextualTypeForObjectLiteralElement: nodeIn => {
- const node = getParseTreeNode(nodeIn, isObjectLiteralElementLike);
- return node ? getContextualTypeForObjectLiteralElement(node, /*contextFlags*/ undefined) : 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, /*contextFlags*/ undefined);
- },
- isContextSensitive,
- getTypeOfPropertyOfContextualType,
- getFullyQualifiedName,
- getResolvedSignature: (node, candidatesOutArray, argumentCount) =>
- getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal),
- getResolvedSignatureForStringLiteralCompletions: (call, editingArgument, candidatesOutArray) =>
- getResolvedSignatureWorker(call, candidatesOutArray, /*argumentCount*/ undefined, CheckMode.IsForStringLiteralArgumentCompletions, editingArgument),
- 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),
- getAsyncIterableType: () => {
- const type = getGlobalAsyncIterableType(/*reportErrors*/ false);
- if (type === emptyGenericType) return undefined;
- return type;
- },
- 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, container) => {
- const node = getParseTreeNode(nodeIn);
- return node && tryGetThisTypeAt(node, includeGlobalThis, container);
- },
- 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 enum MinArgumentCountFlags {
+ None = 0,
+ StrongArityForUntypedJS = 1 << 0,
+ VoidIsNonOptional = 1 << 1,
+}
- 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 enum IntrinsicTypeKind {
+ Uppercase,
+ Lowercase,
+ Capitalize,
+ Uncapitalize
+}
- // Ensure file is type checked, with _eager_ diagnostic production, so identifiers are registered as potentially unused
- checkSourceFileWithEagerDiagnostics(file);
- Debug.assert(!!(getNodeLinks(file).flags & NodeCheckFlags.TypeChecked));
+const intrinsicTypeKinds: ReadonlyESMap = new Map(getEntries({
+ Uppercase: IntrinsicTypeKind.Uppercase,
+ Lowercase: IntrinsicTypeKind.Lowercase,
+ Capitalize: IntrinsicTypeKind.Capitalize,
+ Uncapitalize: IntrinsicTypeKind.Uncapitalize
+}));
- 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 });
- }
- });
+function SymbolLinks(this: SymbolLinks) {
+}
- return diagnostics || emptyArray;
- }
- finally {
- cancellationToken = undefined;
- }
- },
+function NodeLinks(this: NodeLinks) {
+ this.flags = 0;
+}
- runWithCancellationToken: (token, callback) => {
- try {
- cancellationToken = token;
- return callback(checker);
- }
- finally {
- cancellationToken = undefined;
- }
- },
+/** @internal */
+export function getNodeId(node: Node): number {
+ if (!node.id) {
+ node.id = nextNodeId;
+ nextNodeId++;
+ }
+ return node.id;
+}
- getLocalTypeParametersOfClassOrInterfaceOrTypeAlias,
- isDeclarationVisible,
- isPropertyAccessible,
- getTypeOnlyAliasDeclaration,
- getMemberOverrideModifierStatus,
- isTypeParameterPossiblyReferenced,
- };
+/** @internal */
+export function getSymbolId(symbol: Symbol): SymbolId {
+ if (!symbol.id) {
+ symbol.id = nextSymbolId;
+ nextSymbolId++;
+ }
- function runWithInferenceBlockedFromSourceNode(node: Node | undefined, fn: () => T): T {
- const containingCall = findAncestor(node, isCallLikeExpression);
- const containingCallResolvedSignature = containingCall && getNodeLinks(containingCall).resolvedSignature;
- if (containingCall) {
- let toMarkSkip = node!;
- do {
- getNodeLinks(toMarkSkip).skipDirectInference = true;
- toMarkSkip = toMarkSkip.parent;
- } while (toMarkSkip && toMarkSkip !== containingCall);
- getNodeLinks(containingCall).resolvedSignature = undefined;
- }
- const result = fn();
- if (containingCall) {
- let toMarkSkip = node!;
- do {
- getNodeLinks(toMarkSkip).skipDirectInference = undefined;
- toMarkSkip = toMarkSkip.parent;
- } while (toMarkSkip && toMarkSkip !== containingCall);
- getNodeLinks(containingCall).resolvedSignature = containingCallResolvedSignature;
- }
- return result;
- }
+ return symbol.id;
+}
- function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode, editingArgument?: Node): Signature | undefined {
- const node = getParseTreeNode(nodeIn, isCallLikeExpression);
- apparentArgumentCount = argumentCount;
- const res =
- !node ? undefined :
- editingArgument ? runWithInferenceBlockedFromSourceNode(editingArgument, () => getResolvedSignature(node, candidatesOutArray, checkMode)) :
- getResolvedSignature(node, candidatesOutArray, checkMode);
- apparentArgumentCount = undefined;
- return res;
- }
+/** @internal */
+export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) {
+ const moduleState = getModuleInstanceState(node);
+ return moduleState === ModuleInstanceState.Instantiated ||
+ (preserveConstEnums && moduleState === ModuleInstanceState.ConstEnumOnly);
+}
- 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 cachedTypes = new Map();
- const evolvingArrayTypes: EvolvingArrayType[] = [];
- const undefinedProperties: SymbolTable = new Map();
- const markerTypes = new Set();
-
- 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", ObjectFlags.NonInferrableType);
- 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", 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 numericStringType = getTemplateLiteralType(["", ""], [numberType]); // The `${number}` type
-
- const restrictiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(t as TypeParameter) : t, () => "(restrictive mapper)");
- const permissiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? wildcardType : t, () => "(permissive mapper)");
- const uniqueLiteralType = createIntrinsicType(TypeFlags.Never, "never"); // `uniqueLiteralType` is a special `never` flagged by union reduction to behave as a literal
- const uniqueLiteralMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? uniqueLiteralType : t, () => "(unique literal mapper)"); // replace all type parameters with the unique literal type (disregarding constraints)
- let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined;
- const reportUnreliableMapper = makeFunctionTypeMapper(t => {
- if (outofbandVarianceMarkerHandler && (t === markerSuperType || t === markerSubType || t === markerOtherType)) {
- outofbandVarianceMarkerHandler(/*onlyUnreliable*/ true);
+/** @internal */
+export function createTypeChecker(host: TypeCheckerHost): 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;
+
+ sf.resolvedModules.forEach(r => {
+ if (r && r.packageId) map.set(r.packageId.name, r.extension === Extension.Dts || !!map.get(r.packageId.name));
+ });
+ });
+ return map;
+ });
+
+ let deferredDiagnosticsCallbacks: (() => void)[] = [];
+
+ let addLazyDiagnostic = (arg: () => void) => {
+ deferredDiagnosticsCallbacks.push(arg);
+ };
+
+ // 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 totalInstantiationCount = 0;
+ let instantiationCount = 0;
+ let instantiationDepth = 0;
+ let inlineLevel = 0;
+ let currentNode: Node | undefined;
+ let varianceTypeParameter: TypeParameter | 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;
}
- return t;
- }, () => "(unmeasurable reporter)");
- const reportUnmeasurableMapper = makeFunctionTypeMapper(t => {
- if (outofbandVarianceMarkerHandler && (t === markerSuperType || t === markerSubType || t === markerOtherType)) {
- outofbandVarianceMarkerHandler(/*onlyUnreliable*/ false);
+ 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,
+ getIndexInfosOfIndexSymbol,
+ 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,
+ symbolToNode: nodeBuilder.symbolToNode,
+ 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;
+ }
+ if (contextFlags! & ContextFlags.Completions) {
+ return runWithInferenceBlockedFromSourceNode(node, () => getContextualType(node, contextFlags));
+ }
+ return getContextualType(node, contextFlags);
+ },
+ getContextualTypeForObjectLiteralElement: nodeIn => {
+ const node = getParseTreeNode(nodeIn, isObjectLiteralElementLike);
+ return node ? getContextualTypeForObjectLiteralElement(node, /*contextFlags*/ undefined) : 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, /*contextFlags*/ undefined);
+ },
+ isContextSensitive,
+ getTypeOfPropertyOfContextualType,
+ getFullyQualifiedName,
+ getResolvedSignature: (node, candidatesOutArray, argumentCount) =>
+ getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal),
+ getResolvedSignatureForStringLiteralCompletions: (call, editingArgument, candidatesOutArray) =>
+ getResolvedSignatureWorker(call, candidatesOutArray, /*argumentCount*/ undefined, CheckMode.IsForStringLiteralArgumentCompletions, editingArgument),
+ 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),
+ getAsyncIterableType: () => {
+ const type = getGlobalAsyncIterableType(/*reportErrors*/ false);
+ if (type === emptyGenericType) return undefined;
+ return type;
+ },
+ 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, container) => {
+ const node = getParseTreeNode(nodeIn);
+ return node && tryGetThisTypeAt(node, includeGlobalThis, container);
+ },
+ 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;
}
- return t;
- }, () => "(unreliable reporter)");
-
- 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 unknownEmptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
- const unknownUnionType = strictNullChecks ? getUnionType([undefinedType, nullType, unknownEmptyObjectType]) : unknownType;
-
- 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 markerSuperTypeForCheck = createTypeParameter();
- const markerSubTypeForCheck = createTypeParameter();
- markerSubTypeForCheck.constraint = markerSuperTypeForCheck;
+ 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 noTypePredicate = createTypePredicate(TypePredicateKind.Identifier, "<>", 0, anyType);
+ // Ensure file is type checked, with _eager_ diagnostic production, so identifiers are registered as potentially unused
+ checkSourceFileWithEagerDiagnostics(file);
+ Debug.assert(!!(getNodeLinks(file).flags & NodeCheckFlags.TypeChecked));
- 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);
+ 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 });
+ }
+ });
- const enumNumberIndexInfo = createIndexInfo(numberType, stringType, /*isReadonly*/ true);
+ return diagnostics || emptyArray;
+ }
+ finally {
+ cancellationToken = undefined;
+ }
+ },
- 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"); },
- };
+ runWithCancellationToken: (token, callback) => {
+ try {
+ cancellationToken = token;
+ return callback(checker);
+ }
+ finally {
+ cancellationToken = undefined;
+ }
+ },
+
+ getLocalTypeParametersOfClassOrInterfaceOrTypeAlias,
+ isDeclarationVisible,
+ isPropertyAccessible,
+ getTypeOnlyAliasDeclaration,
+ getMemberOverrideModifierStatus,
+ isTypeParameterPossiblyReferenced,
+ };
+
+ function runWithInferenceBlockedFromSourceNode(node: Node | undefined, fn: () => T): T {
+ const containingCall = findAncestor(node, isCallLikeExpression);
+ const containingCallResolvedSignature = containingCall && getNodeLinks(containingCall).resolvedSignature;
+ if (containingCall) {
+ let toMarkSkip = node!;
+ do {
+ getNodeLinks(toMarkSkip).skipDirectInference = true;
+ toMarkSkip = toMarkSkip.parent;
+ } while (toMarkSkip && toMarkSkip !== containingCall);
+ getNodeLinks(containingCall).resolvedSignature = undefined;
+ }
+ const result = fn();
+ if (containingCall) {
+ let toMarkSkip = node!;
+ do {
+ getNodeLinks(toMarkSkip).skipDirectInference = undefined;
+ toMarkSkip = toMarkSkip.parent;
+ } while (toMarkSkip && toMarkSkip !== containingCall);
+ getNodeLinks(containingCall).resolvedSignature = containingCallResolvedSignature;
+ }
+ return result;
+ }
- 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,
- };
+ function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode, editingArgument?: Node): Signature | undefined {
+ const node = getParseTreeNode(nodeIn, isCallLikeExpression);
+ apparentArgumentCount = argumentCount;
+ const res =
+ !node ? undefined :
+ editingArgument ? runWithInferenceBlockedFromSourceNode(editingArgument, () => getResolvedSignature(node, candidatesOutArray, checkMode)) :
+ getResolvedSignature(node, candidatesOutArray, checkMode);
+ apparentArgumentCount = undefined;
+ return res;
+ }
- 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,
- };
+ 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 cachedTypes = new Map();
+ const evolvingArrayTypes: EvolvingArrayType[] = [];
+ const undefinedProperties: SymbolTable = new Map();
+ const markerTypes = new Set();
+
+ 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", ObjectFlags.NonInferrableType);
+ 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", 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 numericStringType = getTemplateLiteralType(["", ""], [numberType]); // The `${number}` type
+
+ const restrictiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? getRestrictiveTypeParameter(t as TypeParameter) : t, () => "(restrictive mapper)");
+ const permissiveMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? wildcardType : t, () => "(permissive mapper)");
+ const uniqueLiteralType = createIntrinsicType(TypeFlags.Never, "never"); // `uniqueLiteralType` is a special `never` flagged by union reduction to behave as a literal
+ const uniqueLiteralMapper: TypeMapper = makeFunctionTypeMapper(t => t.flags & TypeFlags.TypeParameter ? uniqueLiteralType : t, () => "(unique literal mapper)"); // replace all type parameters with the unique literal type (disregarding constraints)
+ let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined;
+ const reportUnreliableMapper = makeFunctionTypeMapper(t => {
+ if (outofbandVarianceMarkerHandler && (t === markerSuperType || t === markerSubType || t === markerOtherType)) {
+ outofbandVarianceMarkerHandler(/*onlyUnreliable*/ true);
+ }
+ return t;
+ }, () => "(unmeasurable reporter)");
+ const reportUnmeasurableMapper = makeFunctionTypeMapper(t => {
+ if (outofbandVarianceMarkerHandler && (t === markerSuperType || t === markerSubType || t === markerOtherType)) {
+ outofbandVarianceMarkerHandler(/*onlyUnreliable*/ false);
+ }
+ return t;
+ }, () => "(unreliable reporter)");
+
+ 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 unknownEmptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
+ const unknownUnionType = strictNullChecks ? getUnionType([undefinedType, nullType, unknownEmptyObjectType]) : unknownType;
+
+ 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 markerSuperTypeForCheck = createTypeParameter();
+ const markerSubTypeForCheck = createTypeParameter();
+ markerSubTypeForCheck.constraint = markerSuperTypeForCheck;
+
+ 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"); },
+ };
+
+ 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 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;
+ let deferredGlobalNaNSymbol: Symbol | undefined;
+ let deferredGlobalRecordSymbol: Symbol | 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 potentialUnusedRenamedBindingElementsInTypes: BindingElement[] = [];
+ const awaitedTypeStack: number[] = [];
+
+ const diagnostics = createDiagnosticCollection();
+ const suggestionDiagnostics = createDiagnosticCollection();
+
+ const typeofType = createTypeofType();
+
+ let _jsxNamespace: __String;
+ let _jsxFactoryEntity: EntityName | 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 node16 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 getCachedType(key: string | undefined) {
+ return key ? cachedTypes.get(key) : undefined;
+ }
- 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;
- let deferredGlobalNaNSymbol: Symbol | undefined;
- let deferredGlobalRecordSymbol: Symbol | 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 potentialUnusedRenamedBindingElementsInTypes: BindingElement[] = [];
- const awaitedTypeStack: number[] = [];
-
- const diagnostics = createDiagnosticCollection();
- const suggestionDiagnostics = createDiagnosticCollection();
-
- const typeofType = createTypeofType();
-
- let _jsxNamespace: __String;
- let _jsxFactoryEntity: EntityName | 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 node16 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 getCachedType(key: string | undefined) {
- return key ? cachedTypes.get(key) : undefined;
- }
-
- function setCachedType(key: string | undefined, type: Type) {
- if (key) cachedTypes.set(key, type);
- return type;
- }
+ function setCachedType(key: string | undefined, type: Type) {
+ if (key) cachedTypes.set(key, type);
+ return type;
+ }
- 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;
- }
+ 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 local/no-in-operator
- return;
- }
- addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message)); // eslint-disable-line local/no-in-operator
+ else {
+ suggestionDiagnostics.add({ ...diagnostic, category: DiagnosticCategory.Suggestion });
}
-
- 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 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 local/no-in-operator
+ return;
}
+ addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message)); // eslint-disable-line local/no-in-operator
+ }
- 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 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 isDeprecatedSymbol(symbol: Symbol) {
- return !!(getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Deprecated);
+ 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 isDeprecatedSymbol(symbol: Symbol) {
+ return !!(getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Deprecated);
+ }
- 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 addDeprecatedSuggestion(location: Node, declarations: Node[], deprecatedEntity: string) {
+ const diagnostic = createDiagnosticForNode(location, Diagnostics._0_is_deprecated, deprecatedEntity);
+ return addDeprecatedSuggestionWorker(declarations, 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 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 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 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 recordMergedSymbol(target: Symbol, source: Symbol) {
- if (!source.mergeId) {
- source.mergeId = nextMergeId;
- nextMergeId++;
- }
- mergedSymbols[source.mergeId] = target;
- }
+ 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 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 recordMergedSymbol(target: Symbol, source: Symbol) {
+ if (!source.mergeId) {
+ source.mergeId = nextMergeId;
+ nextMergeId++;
}
+ mergedSymbols[source.mergeId] = target;
+ }
- /**
- * 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 isSourcePlainJs = isPlainJsFile(sourceSymbolFile, compilerOptions.checkJs);
- const isTargetPlainJs = isPlainJsFile(targetSymbolFile, compilerOptions.checkJs);
- 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));
- if (!isSourcePlainJs) addDuplicateLocations(conflictingSymbolInfo.firstFileLocations, source);
- if (!isTargetPlainJs) addDuplicateLocations(conflictingSymbolInfo.secondFileLocations, target);
- }
- else {
- if (!isSourcePlainJs) addDuplicateDeclarationErrorsForSymbols(source, message, symbolName, target);
- if (!isTargetPlainJs) addDuplicateDeclarationErrorsForSymbols(target, message, symbolName, source);
- }
- }
- return 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 addDuplicateLocations(locs: Declaration[], symbol: Symbol): void {
- if (symbol.declarations) {
- for (const decl of symbol.declarations) {
- pushIfUnique(locs, decl);
- }
+ /**
+ * 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);
}
}
-
- function addDuplicateDeclarationErrorsForSymbols(target: Symbol, message: DiagnosticMessage, symbolName: string, source: Symbol) {
- forEach(target.declarations, node => {
- addDuplicateDeclarationError(node, message, symbolName, source.declarations);
- });
+ 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 isSourcePlainJs = isPlainJsFile(sourceSymbolFile, compilerOptions.checkJs);
+ const isTargetPlainJs = isPlainJsFile(targetSymbolFile, compilerOptions.checkJs);
+ 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));
+ if (!isSourcePlainJs) addDuplicateLocations(conflictingSymbolInfo.firstFileLocations, source);
+ if (!isTargetPlainJs) addDuplicateLocations(conflictingSymbolInfo.secondFileLocations, target);
+ }
+ else {
+ if (!isSourcePlainJs) addDuplicateDeclarationErrorsForSymbols(source, message, symbolName, target);
+ if (!isTargetPlainJs) addDuplicateDeclarationErrorsForSymbols(target, message, symbolName, source);
+ }
}
+ return target;
- 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 addDuplicateLocations(locs: Declaration[], symbol: 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 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 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 mergeSymbolTable(target: SymbolTable, source: SymbolTable, unidirectional = false) {
- source.forEach((sourceSymbol, id) => {
- const targetSymbol = target.get(id);
- target.set(id, targetSymbol ? mergeSymbol(targetSymbol, sourceSymbol, unidirectional) : getMergedSymbol(sourceSymbol));
- });
+ 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) : getMergedSymbol(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 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: 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 isGlobalSourceFile(node: Node) {
- return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node as SourceFile);
- }
+ function getNodeLinks(node: Node): 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): 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 targetFlags = getAllSymbolFlags(symbol);
+ // `targetFlags` will be `SymbolFlags.All` if an error occurred in alias resolution; this avoids cascading errors
+ if (targetFlags & meaning) {
return symbol;
}
- if (symbol.flags & SymbolFlags.Alias) {
- const targetFlags = getAllSymbolFlags(symbol);
- // `targetFlags` will be `SymbolFlags.All` if an error occurred in alias resolution; this avoids cascading errors
- if (targetFlags & 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): [Symbol, 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: 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;
- function isConstAssertion(location: Node) {
- return (isAssertionExpression(location) && isConstTypeReference(location.type))
- || (isJSDocTypeTag(location) && isConstTypeReference(location.typeExpression));
+ function requiresScopeChange(node: ParameterDeclaration): boolean {
+ return requiresScopeChangeWorker(node.name)
+ || !!node.initializer && requiresScopeChangeWorker(node.initializer);
}
- /**
- * 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 nameNotFoundMessage If defined, we will report errors found during resolve.
- * @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,
- getSpellingSuggestions = true): Symbol | undefined {
- return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSpellingSuggestions, 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: PropertyDeclaration | undefined;
- let associatedDeclarationForContainingInitializerOrBindingName: ParameterDeclaration | BindingElement | undefined;
- let withinDeferredContext = false;
- const errorLocation = location;
- let grandparent: Node;
- let isInExternalModule = false;
-
- loop: while (location) {
- if (name === "const" && isConstAssertion(location)) {
- // `const` in an `as const` has no symbol, but issues no error because there is no *actual* lookup of the type
- // (it refers to the constant type of the expression instead)
- return undefined;
- }
- // 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.JSDoc) {
- useResult = result.flags & SymbolFlags.TypeParameter
- // type parameters are visible in parameter list, return type and type parameter list
- ? lastLocation === (location as FunctionLikeDeclaration).type ||
+ 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;
+ }
+ }
+ }
+
+ function isConstAssertion(location: Node) {
+ return (isAssertionExpression(location) && isConstTypeReference(location.type))
+ || (isJSDocTypeTag(location) && isConstTypeReference(location.typeExpression));
+ }
+
+ /**
+ * 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 nameNotFoundMessage If defined, we will report errors found during resolve.
+ * @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,
+ getSpellingSuggestions = true): Symbol | undefined {
+ return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSpellingSuggestions, 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: PropertyDeclaration | undefined;
+ let associatedDeclarationForContainingInitializerOrBindingName: ParameterDeclaration | BindingElement | undefined;
+ let withinDeferredContext = false;
+ const errorLocation = location;
+ let grandparent: Node;
+ let isInExternalModule = false;
+
+ loop: while (location) {
+ if (name === "const" && isConstAssertion(location)) {
+ // `const` in an `as const` has no symbol, but issues no error because there is no *actual* lookup of the type
+ // (it refers to the constant type of the expression instead)
+ return undefined;
+ }
+ // 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.JSDoc) {
+ 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.JSDocParameterTag ||
+ lastLocation.kind === SyntaxKind.JSDocReturnTag ||
+ 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.JSDocParameterTag ||
- lastLocation.kind === SyntaxKind.JSDocReturnTag ||
- 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
+ Debug.assertNode(location, isPropertyDeclaration);
+ 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
- Debug.assertNode(location, isPropertyDeclaration);
- 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.
+ if (nameNotFoundMessage) {
+ 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.
- if (nameNotFoundMessage) {
- 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)) {
+ if (nameNotFoundMessage) {
+ 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)) {
- if (nameNotFoundMessage) {
- 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 :
- isJSDocParameterTag(location) || isJSDocReturnTag(location) ? getHostSignatureFromJSDoc(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 :
+ isJSDocParameterTag(location) || isJSDocReturnTag(location) ? getHostSignatureFromJSDoc(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;
- }
- }
- }
-
- // The invalid initializer error is needed in two situation:
- // 1. When result is undefined, after checking for a missing "this."
- // 2. When result is defined
- function checkAndReportErrorForInvalidInitializer() {
- if (propertyWithInvalidInitializer && !(useDefineForClassFields && getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2022)) {
- // 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.
- error(errorLocation,
- errorLocation && propertyWithInvalidInitializer.type && textRangeContainsPositionInclusive(propertyWithInvalidInitializer.type, errorLocation.pos)
- ? Diagnostics.Type_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor
- : Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor,
- declarationNameToString(propertyWithInvalidInitializer.name), diagnosticName(nameArg!));
- return true;
- }
- return false;
- }
- if (!result) {
- if (nameNotFoundMessage) {
- addLazyDiagnostic(() => {
- if (!errorLocation ||
- !checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg!) && // TODO: GH#18217
- !checkAndReportErrorForInvalidInitializer() &&
- !checkAndReportErrorForExtendingInterface(errorLocation) &&
- !checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) &&
- !checkAndReportErrorForExportingPrimitiveType(errorLocation, name) &&
- !checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation, name, meaning) &&
- !checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) &&
- !checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning)) {
- let suggestion: Symbol | undefined;
- let suggestedLib: string | undefined;
- // Report missing lib first
- if (nameArg) {
- suggestedLib = getSuggestedLibForNonExistentName(nameArg);
- if (suggestedLib) {
- error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg), suggestedLib);
- }
- }
- // then spelling suggestions
- if (!suggestedLib && 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)
- );
- }
- }
- }
- // And then fall back to unspecified "not found"
- if (!suggestion && !suggestedLib && nameArg) {
- error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg));
- }
- suggestionCount++;
- }
- });
+ if (!excludeGlobals) {
+ result = lookup(globals, name, meaning);
+ }
+ }
+ if (!result) {
+ if (originalLocation && isInJSFile(originalLocation) && originalLocation.parent) {
+ if (isRequireCall(originalLocation.parent, /*checkArgumentIsStringLiteralLike*/ false)) {
+ return requireSymbol;
}
- return undefined;
}
- else if (nameNotFoundMessage && checkAndReportErrorForInvalidInitializer()) {
- return undefined;
+ }
+
+ // The invalid initializer error is needed in two situation:
+ // 1. When result is undefined, after checking for a missing "this."
+ // 2. When result is defined
+ function checkAndReportErrorForInvalidInitializer() {
+ if (propertyWithInvalidInitializer && !(useDefineForClassFields && getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2022)) {
+ // 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.
+ error(errorLocation,
+ errorLocation && propertyWithInvalidInitializer.type && textRangeContainsPositionInclusive(propertyWithInvalidInitializer.type, errorLocation.pos)
+ ? Diagnostics.Type_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor
+ : Diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor,
+ declarationNameToString(propertyWithInvalidInitializer.name), diagnosticName(nameArg!));
+ return true;
}
+ return false;
+ }
- // Perform extra checks only if error reporting was requested
+ if (!result) {
if (nameNotFoundMessage) {
addLazyDiagnostic(() => {
- // 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 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 (result && errorLocation && meaning & SymbolFlags.Value && result.flags & SymbolFlags.Alias && !(result.flags & SymbolFlags.Value) && !isValidTypeOnlyAliasUseSite(errorLocation)) {
- const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(result, SymbolFlags.Value);
- 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(errorLocation, message, unescapedName),
- typeOnlyDeclaration,
- unescapedName);
+ if (!errorLocation ||
+ !checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg!) && // TODO: GH#18217
+ !checkAndReportErrorForInvalidInitializer() &&
+ !checkAndReportErrorForExtendingInterface(errorLocation) &&
+ !checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) &&
+ !checkAndReportErrorForExportingPrimitiveType(errorLocation, name) &&
+ !checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation, name, meaning) &&
+ !checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) &&
+ !checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning)) {
+ let suggestion: Symbol | undefined;
+ let suggestedLib: string | undefined;
+ // Report missing lib first
+ if (nameArg) {
+ suggestedLib = getSuggestedLibForNonExistentName(nameArg);
+ if (suggestedLib) {
+ error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg), suggestedLib);
+ }
+ }
+ // then spelling suggestions
+ if (!suggestedLib && 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)
+ );
+ }
+ }
}
+ // And then fall back to unspecified "not found"
+ if (!suggestion && !suggestedLib && nameArg) {
+ error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg));
+ }
+ suggestionCount++;
}
});
}
- return result;
+ return undefined;
}
-
- 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));
+ else if (nameNotFoundMessage && checkAndReportErrorForInvalidInitializer()) {
+ return undefined;
}
- 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);
+ // Perform extra checks only if error reporting was requested
+ if (nameNotFoundMessage) {
+ addLazyDiagnostic(() => {
+ // 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 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 (result && errorLocation && meaning & SymbolFlags.Value && result.flags & SymbolFlags.Alias && !(result.flags & SymbolFlags.Value) && !isValidTypeOnlyAliasUseSite(errorLocation)) {
+ const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(result, SymbolFlags.Value);
+ 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(errorLocation, message, unescapedName),
+ typeOnlyDeclaration,
+ unescapedName);
+ }
+ }
+ });
}
+ return result;
+ }
- 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 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 diagnosticName(nameArg: __String | Identifier | PrivateIdentifier) {
- return isString(nameArg) ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier);
+ 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 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));
- }
+ function diagnosticName(nameArg: __String | Identifier | PrivateIdentifier) {
+ return isString(nameArg) ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier);
+ }
+
+ 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));
}
}
}
+ }
+
+ 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 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;
+ }
- 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;
+ }
- // 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));
+ // 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;
}
-
- // 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;
}
- return false;
+
+ location = location.parent;
}
+ 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));
- return true;
- }
- 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));
+ return true;
}
- /**
- * 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;
+ }
+ /**
+ * 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;
}
+ }
- 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;
- }
+ 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;
}
+ error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, unescapeLeadingUnderscores(name));
+ 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;
- }
+ 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;
}
- return false;
}
+ return false;
+ }
- function isPrimitiveTypeName(name: __String) {
- return name === "any" || name === "string" || name === "number" || name === "boolean" || name === "never" || name === "unknown";
+ function isPrimitiveTypeName(name: __String) {
+ return name === "any" || name === "string" || name === "number" || name === "boolean" || name === "never" || name === "unknown";
+ }
+
+ 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 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 checkAndReportErrorForUsingTypeAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
+ if (meaning & SymbolFlags.Value) {
+ if (isPrimitiveTypeName(name)) {
+ if (isExtendedByInterface(errorLocation)) {
+ error(errorLocation, Diagnostics.An_interface_cannot_extend_a_primitive_type_like_0_an_interface_can_only_extend_named_types_and_classes, unescapeLeadingUnderscores(name));
+ }
+ else {
+ error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, unescapeLeadingUnderscores(name));
+ }
return true;
}
- return false;
- }
-
- function checkAndReportErrorForUsingTypeAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
- if (meaning & SymbolFlags.Value) {
- if (isPrimitiveTypeName(name)) {
- if (isExtendedByInterface(errorLocation)) {
- error(errorLocation, Diagnostics.An_interface_cannot_extend_a_primitive_type_like_0_an_interface_can_only_extend_named_types_and_classes, unescapeLeadingUnderscores(name));
- }
- else {
- 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));
+ const allFlags = symbol && getAllSymbolFlags(symbol);
+ if (symbol && allFlags !== undefined && !(allFlags & SymbolFlags.Value)) {
+ 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);
}
- const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
- const allFlags = symbol && getAllSymbolFlags(symbol);
- if (symbol && allFlags !== undefined && !(allFlags & SymbolFlags.Value)) {
- 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;
+ 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 isExtendedByInterface(node: Node): boolean {
- const grandparent = node.parent.parent;
- const parentOfGrandparent = grandparent.parent;
- if(grandparent && parentOfGrandparent){
- const isExtending = isHeritageClause(grandparent) && grandparent.token === SyntaxKind.ExtendsKeyword;
- const isInterface = isInterfaceDeclaration(parentOfGrandparent);
- return isExtending && isInterface;
- }
- return false;
+ function isExtendedByInterface(node: Node): boolean {
+ const grandparent = node.parent.parent;
+ const parentOfGrandparent = grandparent.parent;
+ if(grandparent && parentOfGrandparent){
+ const isExtending = isHeritageClause(grandparent) && grandparent.token === SyntaxKind.ExtendsKeyword;
+ const isInterface = isInterfaceDeclaration(parentOfGrandparent);
+ return isExtending && isInterface;
}
+ 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);
- }
- 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);
}
+ 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 isES2015OrLaterConstructorName(n: __String) {
+ switch (n) {
+ case "Promise":
+ case "Symbol":
+ case "Map":
+ case "WeakMap":
+ case "Set":
+ case "WeakSet":
+ return true;
}
+ return false;
+ }
- function checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
- if (meaning & (SymbolFlags.Value & ~SymbolFlags.Type)) {
- const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
- if (symbol) {
- error(
- errorLocation,
- Diagnostics.Cannot_use_namespace_0_as_a_value,
- unescapeLeadingUnderscores(name));
- return true;
- }
+ function checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
+ if (meaning & (SymbolFlags.Value & ~SymbolFlags.Type)) {
+ const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
+ if (symbol) {
+ error(
+ errorLocation,
+ Diagnostics.Cannot_use_namespace_0_as_a_value,
+ unescapeLeadingUnderscores(name));
+ return true;
}
- else if (meaning & (SymbolFlags.Type & ~SymbolFlags.Value)) {
- const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Module, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
- if (symbol) {
- error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, unescapeLeadingUnderscores(name));
- return true;
- }
+ }
+ else if (meaning & (SymbolFlags.Type & ~SymbolFlags.Value)) {
+ const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Module, /*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 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;
- }
- // Block-scoped variables cannot be used before their definition
- const declaration = result.declarations?.find(
- d => isBlockOrCatchScoped(d) || isClassLike(d) || (d.kind === SyntaxKind.EnumDeclaration));
+ 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;
+ }
+ // 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 === 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) {
+ 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) {
+ 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);
}
- 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) || isAsyncFunction(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) || isAsyncFunction(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: 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 from ...
- * import { x as } from ...
- * export { x as } from ...
- * export * as ns from ...
- * export =
- * export default
- * module.exports =
- * {}
- * {name: }
- * const { x } = require ...
- */
- function isAliasSymbolDeclaration(node: Node): boolean {
- return node.kind === SyntaxKind.ImportEqualsDeclaration
- || node.kind === SyntaxKind.NamespaceExportDeclaration
- || node.kind === SyntaxKind.ImportClause && !!(node as ImportClause).name
- || node.kind === SyntaxKind.NamespaceImport
- || node.kind === SyntaxKind.NamespaceExport
- || node.kind === SyntaxKind.ImportSpecifier
- || node.kind === SyntaxKind.ExportSpecifier
- || node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node as ExportAssignment)
- || isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ModuleExports && exportAssignmentIsAlias(node)
- || isAccessExpression(node)
- && isBinaryExpression(node.parent)
- && node.parent.left === node
- && node.parent.operatorToken.kind === SyntaxKind.EqualsToken
- && isAliasableOrJsExpression(node.parent.right)
- || node.kind === SyntaxKind.ShorthandPropertyAssignment
- || node.kind === SyntaxKind.PropertyAssignment && isAliasableOrJsExpression((node as PropertyAssignment).initializer)
- || node.kind === SyntaxKind.VariableDeclaration && isVariableDeclarationInitializedToBareOrAccessedRequire(node)
- || node.kind === SyntaxKind.BindingElement && isVariableDeclarationInitializedToBareOrAccessedRequire(node.parent.parent);
- }
-
- function isAliasableOrJsExpression(e: Expression) {
- return isAliasableExpression(e) || isFunctionExpression(e) && isJSConstructor(e);
- }
-
- function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration | VariableDeclaration, dontResolveAlias: boolean): Symbol | undefined {
- const commonJSPropertyAccess = getCommonJSPropertyAccess(node);
- if (commonJSPropertyAccess) {
- const name = (getLeftmostAccessExpression(commonJSPropertyAccess.expression) as CallExpression).arguments[0] as StringLiteral;
- return isIdentifier(commonJSPropertyAccess.name)
- ? resolveSymbol(getPropertyOfType(resolveExternalModuleTypeByLiteral(name), commonJSPropertyAccess.name.escapedText))
- : undefined;
- }
- if (isVariableDeclaration(node) || node.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
- const immediate = resolveExternalModuleName(
- node,
- getExternalModuleRequireArgument(node) || getExternalModuleImportEqualsDeclarationExpression(node));
- const resolved = resolveExternalModuleSymbol(immediate);
- markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, /*overwriteEmpty*/ false);
- return resolved;
- }
- const resolved = getSymbolOfPartOfRightHandSideOfImportEquals(node.moduleReference, dontResolveAlias);
- checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node, resolved);
- return resolved;
- }
+ /**
+ * An alias symbol is created by one of the following declarations:
+ * import = ...
+ * import from ...
+ * import * as from ...
+ * import { x as