diff --git a/.gitignore b/.gitignore
index eb0df177040..8c83d319ed7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -59,3 +59,4 @@ internal/
.idea
yarn.lock
package-lock.json
+.parallelperf.json
diff --git a/Gulpfile.ts b/Gulpfile.ts
index a3db20dfd8a..4ca099b56e4 100644
--- a/Gulpfile.ts
+++ b/Gulpfile.ts
@@ -31,8 +31,6 @@ import merge2 = require("merge2");
import * as os from "os";
import fold = require("travis-fold");
const gulp = helpMaker(originalGulp);
-const mochaParallel = require("./scripts/mocha-parallel.js");
-const {runTestsInParallel} = mochaParallel;
Error.stackTraceLimit = 1000;
@@ -668,36 +666,18 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
}
else {
// run task to load all tests and partition them between workers
- const args = [];
- args.push("-R", "min");
- if (colors) {
- args.push("--colors");
- }
- else {
- args.push("--no-colors");
- }
- args.push(run);
setNodeEnvToDevelopment();
- runTestsInParallel(taskConfigsFolder, run, { testTimeout, noColors: colors === " --no-colors " }, function(err) {
- // last worker clean everything and runs linter in case if there were no errors
- del(taskConfigsFolder).then(() => {
- if (!err) {
- lintThenFinish();
- }
- else {
- finish(err);
- }
- });
+ exec(host, [run], lintThenFinish, function(e, status) {
+ finish(e, status);
});
}
});
function failWithStatus(err?: any, status?: number) {
- if (err) {
- console.log(err);
+ if (err || status) {
+ process.exit(typeof status === "number" ? status : 2);
}
- done(err || status);
- process.exit(status);
+ done();
}
function lintThenFinish() {
@@ -711,7 +691,7 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
function finish(error?: any, errorStatus?: number) {
restoreSavedNodeEnv();
- deleteTemporaryProjectOutput().then(() => {
+ deleteTestConfig().then(deleteTemporaryProjectOutput).then(() => {
if (error !== undefined || errorStatus !== undefined) {
failWithStatus(error, errorStatus);
}
@@ -720,6 +700,10 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
}
});
}
+
+ function deleteTestConfig() {
+ return del("test.config");
+ }
}
gulp.task("runtests-parallel", "Runs all the tests in parallel using the built run.js file. Optional arguments are: --t[ests]=category1|category2|... --d[ebug]=true.", ["build-rules", "tests"], (done) => {
@@ -836,7 +820,7 @@ function cleanTestDirs(done: (e?: any) => void) {
// used to pass data from jake command line directly to run.js
function writeTestConfigFile(tests: string, light: boolean, taskConfigsFolder?: string, workerCount?: number, stackTraceLimit?: string) {
- const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light, workerCount, stackTraceLimit, taskConfigsFolder });
+ const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light, workerCount, stackTraceLimit, taskConfigsFolder, noColor: !cmdLineOptions["colors"] });
console.log("Running tests with config: " + testConfigContents);
fs.writeFileSync("test.config", testConfigContents);
}
@@ -1066,10 +1050,11 @@ gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are:
const fileMatcher = cmdLineOptions["files"];
const files = fileMatcher
? `src/**/${fileMatcher}`
- : "Gulpfile.ts 'scripts/tslint/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
- const cmd = `node node_modules/tslint/bin/tslint ${files} --format stylish`;
+ : "Gulpfile.ts 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
+ const cmd = `node node_modules/tslint/bin/tslint ${files} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`;
console.log("Linting: " + cmd);
child_process.execSync(cmd, { stdio: [0, 1, 2] });
+ if (fold.isTravis()) console.log(fold.end("lint"));
});
gulp.task("default", "Runs 'local'", ["local"]);
diff --git a/Jakefile.js b/Jakefile.js
index b3e18e8cb1a..7de0635542b 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -1,11 +1,11 @@
// This file contains the build logic for the public repo
+// @ts-check
var fs = require("fs");
var os = require("os");
var path = require("path");
var child_process = require("child_process");
var fold = require("travis-fold");
-var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel;
var ts = require("./lib/typescript");
@@ -38,7 +38,7 @@ else if (process.env.PATH !== undefined) {
function filesFromConfig(configPath) {
var configText = fs.readFileSync(configPath).toString();
- var config = ts.parseConfigFileTextToJson(configPath, configText, /*stripComments*/ true);
+ var config = ts.parseConfigFileTextToJson(configPath, configText);
if (config.error) {
throw new Error(diagnosticsToString([config.error]));
}
@@ -104,6 +104,9 @@ var harnessCoreSources = [
"loggedIO.ts",
"rwcRunner.ts",
"test262Runner.ts",
+ "./parallel/shared.ts",
+ "./parallel/host.ts",
+ "./parallel/worker.ts",
"runner.ts"
].map(function (f) {
return path.join(harnessDirectory, f);
@@ -143,6 +146,7 @@ var harnessSources = harnessCoreSources.concat([
"customTransforms.ts",
"programMissingFiles.ts",
"symbolWalker.ts",
+ "languageService.ts",
].map(function (f) {
return path.join(unittestsDirectory, f);
})).concat([
@@ -595,7 +599,7 @@ file(typesMapOutputPath, function() {
var content = fs.readFileSync(path.join(serverDirectory, 'typesMap.json'));
// Validate that it's valid JSON
try {
- JSON.parse(content);
+ JSON.parse(content.toString());
} catch (e) {
console.log("Parse error in typesMap.json: " + e);
}
@@ -739,7 +743,7 @@ desc("Builds the test infrastructure using the built compiler");
task("tests", ["local", run].concat(libraryTargets));
function exec(cmd, completeHandler, errorHandler) {
- var ex = jake.createExec([cmd], { windowsVerbatimArguments: true });
+ var ex = jake.createExec([cmd], { windowsVerbatimArguments: true, interactive: true });
// Add listeners for output and error
ex.addListener("stdout", function (output) {
process.stdout.write(output);
@@ -765,15 +769,16 @@ function exec(cmd, completeHandler, errorHandler) {
ex.run();
}
+const del = require("del");
function cleanTestDirs() {
// Clean the local baselines directory
if (fs.existsSync(localBaseline)) {
- jake.rmRf(localBaseline);
+ del.sync(localBaseline);
}
// Clean the local Rwc baselines directory
if (fs.existsSync(localRwcBaseline)) {
- jake.rmRf(localRwcBaseline);
+ del.sync(localRwcBaseline);
}
jake.mkdirP(localRwcBaseline);
@@ -782,13 +787,14 @@ function cleanTestDirs() {
}
// used to pass data from jake command line directly to run.js
-function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit) {
+function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit, colors) {
var testConfigContents = JSON.stringify({
test: tests ? [tests] : undefined,
light: light,
workerCount: workerCount,
taskConfigsFolder: taskConfigsFolder,
- stackTraceLimit: stackTraceLimit
+ stackTraceLimit: stackTraceLimit,
+ noColor: !colors
});
fs.writeFileSync('test.config', testConfigContents);
}
@@ -830,7 +836,7 @@ function runConsoleTests(defaultReporter, runInParallel) {
}
if (tests || light || taskConfigsFolder) {
- writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit);
+ writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit, colors);
}
if (tests && tests.toLocaleLowerCase() === "rwc") {
@@ -893,19 +899,15 @@ function runConsoleTests(defaultReporter, runInParallel) {
var savedNodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = "development";
var startTime = mark();
- runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: !colors }, function (err) {
+ exec(host + " " + run, function () {
process.env.NODE_ENV = savedNodeEnv;
measure(startTime);
- // last worker clean everything and runs linter in case if there were no errors
- deleteTemporaryProjectOutput();
- jake.rmRf(taskConfigsFolder);
- if (err) {
- fail(err);
- }
- else {
- runLinter();
- complete();
- }
+ runLinter();
+ finish();
+ }, function (e, status) {
+ process.env.NODE_ENV = savedNodeEnv;
+ measure(startTime);
+ finish(status);
});
}
@@ -968,8 +970,8 @@ desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is
task("runtests-browser", ["browserify", nodeServerOutFile], function () {
cleanTestDirs();
host = "node";
- browser = process.env.browser || process.env.b || (os.platform() === "linux" ? "chrome" : "IE");
- tests = process.env.test || process.env.tests || process.env.t;
+ var browser = process.env.browser || process.env.b || (os.platform() === "linux" ? "chrome" : "IE");
+ var tests = process.env.test || process.env.tests || process.env.t;
var light = process.env.light || false;
var testConfigFile = 'test.config';
if (fs.existsSync(testConfigFile)) {
@@ -1041,6 +1043,7 @@ function acceptBaseline(sourceFolder, targetFolder) {
if (fs.existsSync(target)) {
fs.unlinkSync(target);
}
+ jake.mkdirP(path.dirname(target));
fs.renameSync(path.join(sourceFolder, filename), target);
}
}
@@ -1118,7 +1121,7 @@ task("update-sublime", ["local", serverFile], function () {
jake.cpR(serverFile + ".map", "../TypeScript-Sublime-Plugin/tsserver/");
});
-var tslintRuleDir = "scripts/tslint";
+var tslintRuleDir = "scripts/tslint/rules";
var tslintRules = [
"booleanTriviaRule",
"debugAssertRule",
@@ -1134,13 +1137,27 @@ var tslintRulesFiles = tslintRules.map(function (p) {
return path.join(tslintRuleDir, p + ".ts");
});
var tslintRulesOutFiles = tslintRules.map(function (p) {
- return path.join(builtLocalDirectory, "tslint", p + ".js");
+ return path.join(builtLocalDirectory, "tslint/rules", p + ".js");
+});
+var tslintFormattersDir = "scripts/tslint/formatters";
+var tslintFormatters = [
+ "autolinkableStylishFormatter",
+];
+var tslintFormatterFiles = tslintFormatters.map(function (p) {
+ return path.join(tslintFormattersDir, p + ".ts");
+});
+var tslintFormattersOutFiles = tslintFormatters.map(function (p) {
+ return path.join(builtLocalDirectory, "tslint/formatters", p + ".js");
});
desc("Compiles tslint rules to js");
-task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(["build-rules-end"]));
+task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(tslintFormattersOutFiles).concat(["build-rules-end"]));
tslintRulesFiles.forEach(function (ruleFile, i) {
compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false,
- { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint"), lib: "es6" });
+ { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint/rules"), lib: "es6" });
+});
+tslintFormatterFiles.forEach(function (ruleFile, i) {
+ compileFile(tslintFormattersOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false,
+ { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint/formatters"), lib: "es6" });
});
desc("Emit the start of the build-rules fold");
@@ -1208,8 +1225,8 @@ task("lint", ["build-rules"], () => {
const fileMatcher = process.env.f || process.env.file || process.env.files;
const files = fileMatcher
? `src/**/${fileMatcher}`
- : "Gulpfile.ts 'scripts/tslint/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
- const cmd = `node node_modules/tslint/bin/tslint ${files} --format stylish`;
+ : "Gulpfile.ts 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
+ const cmd = `node node_modules/tslint/bin/tslint ${files} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`;
console.log("Linting: " + cmd);
jake.exec([cmd], { interactive: true }, () => {
if (fold.isTravis()) console.log(fold.end("lint"));
diff --git a/README.md b/README.md
index 23829a74d39..fd9926d2dfa 100644
--- a/README.md
+++ b/README.md
@@ -12,13 +12,13 @@
For the latest stable version:
-```
+```bash
npm install -g typescript
```
For our nightly builds:
-```
+```bash
npm install -g typescript@next
```
@@ -50,19 +50,19 @@ In order to build the TypeScript compiler, ensure that you have [Git](https://gi
Clone a copy of the repo:
-```
+```bash
git clone https://github.com/Microsoft/TypeScript.git
```
Change to the TypeScript directory:
-```
+```bash
cd TypeScript
```
Install Gulp tools and dev dependencies:
-```
+```bash
npm install -g gulp
npm install
```
@@ -88,7 +88,7 @@ gulp help # List the above commands.
## Usage
-```shell
+```bash
node built/local/tsc.js hello.ts
```
diff --git a/package.json b/package.json
index 9e4b3234770..ca7d48c7774 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
"devDependencies": {
"@types/browserify": "latest",
"@types/chai": "latest",
+ "@types/colors": "latest",
"@types/convert-source-map": "latest",
"@types/del": "latest",
"@types/glob": "latest",
@@ -48,8 +49,8 @@
"@types/q": "latest",
"@types/run-sequence": "latest",
"@types/through2": "latest",
- "browserify": "latest",
"browser-resolve": "^1.11.2",
+ "browserify": "latest",
"chai": "latest",
"convert-source-map": "latest",
"del": "latest",
@@ -75,6 +76,7 @@
"travis-fold": "latest",
"ts-node": "latest",
"tslint": "latest",
+ "colors": "latest",
"typescript": "next"
},
"scripts": {
diff --git a/scripts/bisect-test.ts b/scripts/bisect-test.ts
index 93a516bc899..948b272470f 100644
--- a/scripts/bisect-test.ts
+++ b/scripts/bisect-test.ts
@@ -1,5 +1,7 @@
-///
-
+/**
+ * You should have ts-node installed globally before executing this, probably!
+ * Otherwise you'll need to compile this script before you start bisecting!
+ */
import cp = require('child_process');
import fs = require('fs');
@@ -42,8 +44,8 @@ jake.on('close', jakeExitCode => {
});
} else {
console.log('Unknown command line arguments.');
- console.log('Usage (compile errors): git bisect run scripts\bisect.js "foo.ts --module amd" compiles');
- console.log('Usage (emit check): git bisect run scripts\bisect.js bar.ts emits bar.js "_this = this"');
+ console.log('Usage (compile errors): git bisect run ts-node scripts\bisect-test.ts "../failure.ts --module amd" !compiles');
+ console.log('Usage (emit check): git bisect run ts-node scripts\bisect-test.ts bar.ts emits bar.js "_this = this"');
// Aborts the 'git bisect run' process
process.exit(-1);
}
diff --git a/scripts/bisect.cmd b/scripts/bisect.cmd
deleted file mode 100644
index 148722665d4..00000000000
--- a/scripts/bisect.cmd
+++ /dev/null
@@ -1,30 +0,0 @@
-echo off
-IF NOT EXIST scripts\bisect.cmd GOTO :wrongdir
-IF "%1" == "" GOTO :usage
-IF "%1" == "GO" GOTO :run
-GOTO :copy
-
-:usage
-echo Usage: bisect GoodCommit BadCommit test.ts compiles
-echo Usage: bisect GoodCommit BadCommit test.ts emits test.js "var x = 3"
-GOTO :eof
-
-:copy
-copy scripts\bisect.cmd scripts\bisect-fresh.cmd
-scripts\bisect-fresh GO %*
-GOTO :eof
-
-:run
-call jake local
-node built/local/tsc.js scripts/bisect-test.ts --module commonjs
-git bisect start %2 %3
-git bisect run node scripts/bisect-test.js %4 %5 %6 %7
-del scripts\bisect-test.js
-del scripts\bisect-fresh.cmd
-GOTO :eof
-
-:wrongdir
-@echo Run this file from the repo folder, not the scripts folder
-GOTO :eof
-
-:eof
\ No newline at end of file
diff --git a/scripts/mocha-none-reporter.js b/scripts/mocha-none-reporter.js
deleted file mode 100644
index 5787b0c042e..00000000000
--- a/scripts/mocha-none-reporter.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Module dependencies.
- */
-
-var Base = require('mocha').reporters.Base;
-
-/**
- * Expose `None`.
- */
-
-exports = module.exports = None;
-
-/**
- * Initialize a new `None` test reporter.
- *
- * @api public
- * @param {Runner} runner
- */
-function None(runner) {
- Base.call(this);
-}
-
-/**
- * Inherit from `Base.prototype`.
- */
-None.prototype.__proto__ = Base.prototype;
diff --git a/scripts/mocha-parallel.js b/scripts/mocha-parallel.js
deleted file mode 100644
index 6a54c018e9a..00000000000
--- a/scripts/mocha-parallel.js
+++ /dev/null
@@ -1,405 +0,0 @@
-var tty = require("tty")
- , readline = require("readline")
- , fs = require("fs")
- , path = require("path")
- , child_process = require("child_process")
- , os = require("os")
- , mocha = require("mocha")
- , Base = mocha.reporters.Base
- , color = Base.color
- , cursor = Base.cursor
- , ms = require("mocha/lib/ms");
-
-var isatty = tty.isatty(1) && tty.isatty(2);
-var tapRangePattern = /^(\d+)\.\.(\d+)(?:$|\r\n?|\n)/;
-var tapTestPattern = /^(not\sok|ok)\s+(\d+)\s+(?:-\s+)?(.*)$/;
-var tapCommentPattern = /^#(?: (tests|pass|fail) (\d+)$)?/;
-
-exports.runTestsInParallel = runTestsInParallel;
-exports.ProgressBars = ProgressBars;
-
-function runTestsInParallel(taskConfigsFolder, run, options, cb) {
- if (options === undefined) options = { };
-
- return discoverTests(run, options, function (error) {
- if (error) {
- return cb(error);
- }
-
- return runTests(taskConfigsFolder, run, options, cb);
- });
-}
-
-function discoverTests(run, options, cb) {
- console.log("Discovering tests...");
-
- var cmd = "mocha -R " + require.resolve("./mocha-none-reporter.js") + " " + run;
- var p = spawnProcess(cmd);
- p.on("exit", function (status) {
- if (status) {
- cb(new Error("Process exited with code " + status));
- }
- else {
- cb();
- }
- });
-}
-
-function runTests(taskConfigsFolder, run, options, cb) {
- var configFiles = fs.readdirSync(taskConfigsFolder);
- var numPartitions = configFiles.length;
- if (numPartitions <= 0) {
- cb();
- return;
- }
-
- console.log("Running tests on " + numPartitions + " threads...");
-
- var partitions = Array(numPartitions);
- var progressBars = new ProgressBars();
- progressBars.enable();
-
- var counter = numPartitions;
- configFiles.forEach(runTestsInPartition);
-
- function runTestsInPartition(file, index) {
- var partition = {
- file: path.join(taskConfigsFolder, file),
- tests: 0,
- passed: 0,
- failed: 0,
- completed: 0,
- current: undefined,
- start: undefined,
- end: undefined,
- catastrophicError: "",
- failures: []
- };
- partitions[index] = partition;
-
- // Set up the progress bar.
- updateProgress(0);
-
- // Start the background process.
- var cmd = "mocha -t " + (options.testTimeout || 20000) + " -R tap --no-colors " + run + " --config='" + partition.file + "'";
- var p = spawnProcess(cmd);
- var rl = readline.createInterface({
- input: p.stdout,
- terminal: false
- });
-
- var rlError = readline.createInterface({
- input: p.stderr,
- terminal: false
- });
-
- rl.on("line", onmessage);
- rlError.on("line", onErrorMessage);
- p.on("exit", onexit)
-
- function onErrorMessage(line) {
- partition.catastrophicError += line + os.EOL;
- }
-
- function onmessage(line) {
- if (partition.start === undefined) {
- partition.start = Date.now();
- }
-
- var rangeMatch = tapRangePattern.exec(line);
- if (rangeMatch) {
- partition.tests = parseInt(rangeMatch[2]);
- return;
- }
-
- var testMatch = tapTestPattern.exec(line);
- if (testMatch) {
- var test = {
- result: testMatch[1],
- id: parseInt(testMatch[2]),
- name: testMatch[3],
- output: []
- };
-
- partition.current = test;
- partition.completed++;
-
- if (test.result === "ok") {
- partition.passed++;
- }
- else {
- partition.failed++;
- partition.failures.push(test);
- }
-
- var progress = partition.completed / partition.tests;
- if (progress < 1) {
- updateProgress(progress);
- }
-
- return;
- }
-
- var commentMatch = tapCommentPattern.exec(line);
- if (commentMatch) {
- switch (commentMatch[1]) {
- case "tests":
- partition.current = undefined;
- partition.tests = parseInt(commentMatch[2]);
- break;
-
- case "pass":
- partition.passed = parseInt(commentMatch[2]);
- break;
-
- case "fail":
- partition.failed = parseInt(commentMatch[2]);
- break;
- }
-
- return;
- }
-
- if (partition.current) {
- partition.current.output.push(line);
- }
- }
-
- function onexit(code) {
- if (partition.end === undefined) {
- partition.end = Date.now();
- }
-
- partition.duration = partition.end - partition.start;
- var isPartitionFail = partition.failed || code !== 0;
- var summaryColor = isPartitionFail ? "fail" : "green";
- var summarySymbol = isPartitionFail ? Base.symbols.err : Base.symbols.ok;
-
- var summaryTests = (isPartitionFail ? partition.passed + "/" + partition.tests : partition.passed) + " passing";
- var summaryDuration = "(" + ms(partition.duration) + ")";
- var savedUseColors = Base.useColors;
- Base.useColors = !options.noColors;
-
- var summary = color(summaryColor, summarySymbol + " " + summaryTests) + " " + color("light", summaryDuration);
- Base.useColors = savedUseColors;
-
- updateProgress(1, summary);
-
- signal();
- }
-
- function updateProgress(percentComplete, title) {
- var progressColor = "pending";
- if (partition.failed) {
- progressColor = "fail";
- }
-
- progressBars.update(
- index,
- percentComplete,
- progressColor,
- title
- );
- }
- }
-
- function signal() {
- counter--;
-
- if (counter <= 0) {
- var reporter = new Base(),
- stats = reporter.stats,
- failures = reporter.failures;
-
- var duration = 0;
- var catastrophicError = "";
- for (var i = 0; i < numPartitions; i++) {
- var partition = partitions[i];
- stats.passes += partition.passed;
- stats.failures += partition.failed;
- stats.tests += partition.tests;
- duration += partition.duration;
- if (partition.catastrophicError !== "") {
- // Partition is written out to a temporary file as a JSON object.
- // Below is an example of how the partition JSON object looks like
- // {
- // "light":false,
- // "tasks":[
- // {
- // "runner":"compiler",
- // "files":["tests/cases/compiler/es6ImportNamedImportParsingError.ts"]
- // }
- // ],
- // "runUnitTests":false
- // }
- var jsonText = fs.readFileSync(partition.file);
- var configObj = JSON.parse(jsonText);
- if (configObj.tasks && configObj.tasks[0]) {
- catastrophicError += "Error from one or more of these files: " + configObj.tasks[0].files + os.EOL;
- catastrophicError += partition.catastrophicError;
- catastrophicError += os.EOL;
- }
- }
- for (var j = 0; j < partition.failures.length; j++) {
- var failure = partition.failures[j];
- failures.push(makeMochaTest(failure));
- }
- }
-
- stats.duration = duration;
- progressBars.disable();
-
- if (options.noColors) {
- var savedUseColors = Base.useColors;
- Base.useColors = false;
- reporter.epilogue();
- Base.useColors = savedUseColors;
- }
- else {
- reporter.epilogue();
- }
-
- if (catastrophicError !== "") {
- return cb(new Error(catastrophicError));
- }
- if (stats.failures) {
- return cb(new Error("Test failures reported: " + stats.failures));
- }
- else {
- return cb();
- }
- }
- }
-
- function makeMochaTest(test) {
- return {
- fullTitle: function() {
- return test.name;
- },
- err: {
- message: test.output[0],
- stack: test.output.join(os.EOL)
- }
- };
- }
-}
-
-var nodeModulesPathPrefix = path.resolve("./node_modules/.bin/") + path.delimiter;
-if (process.env.path !== undefined) {
- process.env.path = nodeModulesPathPrefix + process.env.path;
-} else if (process.env.PATH !== undefined) {
- process.env.PATH = nodeModulesPathPrefix + process.env.PATH;
-}
-
-function spawnProcess(cmd, options) {
- var shell = process.platform === "win32" ? "cmd" : "/bin/sh";
- var prefix = process.platform === "win32" ? "/c" : "-c";
- return child_process.spawn(shell, [prefix, cmd], { windowsVerbatimArguments: true });
-}
-
-function ProgressBars(options) {
- if (!options) options = {};
- var open = options.open || '[';
- var close = options.close || ']';
- var complete = options.complete || '▬';
- var incomplete = options.incomplete || Base.symbols.dot;
- var maxWidth = Math.floor(Base.window.width * .30) - open.length - close.length - 2;
- var width = minMax(options.width || maxWidth, 10, maxWidth);
- this._options = {
- open: open,
- complete: complete,
- incomplete: incomplete,
- close: close,
- width: width
- };
-
- this._progressBars = [];
- this._lineCount = 0;
- this._enabled = false;
-}
-ProgressBars.prototype = {
- enable: function () {
- if (!this._enabled) {
- process.stdout.write(os.EOL);
- this._enabled = true;
- }
- },
- disable: function () {
- if (this._enabled) {
- process.stdout.write(os.EOL);
- this._enabled = false;
- }
- },
- update: function (index, percentComplete, color, title) {
- percentComplete = minMax(percentComplete, 0, 1);
-
- var progressBar = this._progressBars[index] || (this._progressBars[index] = { });
- var width = this._options.width;
- var n = Math.floor(width * percentComplete);
- var i = width - n;
- if (n === progressBar.lastN && title === progressBar.title && color === progressBar.progressColor) {
- return;
- }
-
- progressBar.lastN = n;
- progressBar.title = title;
- progressBar.progressColor = color;
-
- var progress = " ";
- progress += this._color('progress', this._options.open);
- progress += this._color(color, fill(this._options.complete, n));
- progress += this._color('progress', fill(this._options.incomplete, i));
- progress += this._color('progress', this._options.close);
-
- if (title) {
- progress += this._color('progress', ' ' + title);
- }
-
- if (progressBar.text !== progress) {
- progressBar.text = progress;
- this._render(index);
- }
- },
- _render: function (index) {
- if (!this._enabled || !isatty) {
- return;
- }
-
- cursor.hide();
- readline.moveCursor(process.stdout, -process.stdout.columns, -this._lineCount);
- var lineCount = 0;
- var numProgressBars = this._progressBars.length;
- for (var i = 0; i < numProgressBars; i++) {
- if (i === index) {
- readline.clearLine(process.stdout, 1);
- process.stdout.write(this._progressBars[i].text + os.EOL);
- }
- else {
- readline.moveCursor(process.stdout, -process.stdout.columns, +1);
- }
-
- lineCount++;
- }
-
- this._lineCount = lineCount;
- cursor.show();
- },
- _color: function (type, text) {
- return type && !this._options.noColors ? color(type, text) : text;
- }
-};
-
-function fill(ch, size) {
- var s = "";
- while (s.length < size) {
- s += ch;
- }
-
- return s.length > size ? s.substr(0, size) : s;
-}
-
-function minMax(value, min, max) {
- if (value < min) return min;
- if (value > max) return max;
- return value;
-}
\ No newline at end of file
diff --git a/scripts/tslint/formatters/autolinkableStylishFormatter.ts b/scripts/tslint/formatters/autolinkableStylishFormatter.ts
new file mode 100644
index 00000000000..6a02ec24f05
--- /dev/null
+++ b/scripts/tslint/formatters/autolinkableStylishFormatter.ts
@@ -0,0 +1,97 @@
+import * as Lint from "tslint";
+import * as colors from "colors";
+import { sep } from "path";
+function groupBy(array: ReadonlyArray | undefined, getGroupId: (elem: T, index: number) => number | string): T[][] {
+ if (!array) {
+ return [];
+ }
+
+ const groupIdToGroup: { [index: string]: T[] } = {};
+ let result: T[][] | undefined; // Compacted array for return value
+ for (let index = 0; index < array.length; index++) {
+ const value = array[index];
+ const key = getGroupId(value, index);
+ if (groupIdToGroup[key]) {
+ groupIdToGroup[key].push(value);
+ }
+ else {
+ const newGroup = [value];
+ groupIdToGroup[key] = newGroup;
+ if (!result) {
+ result = [newGroup];
+ }
+ else {
+ result.push(newGroup);
+ }
+ }
+ }
+
+ return result || [];
+}
+
+function max(array: ReadonlyArray | undefined, selector: (elem: T) => number): number {
+ if (!array) {
+ return 0;
+ }
+
+ let max = 0;
+ for (const item of array) {
+ const scalar = selector(item);
+ if (scalar > max) {
+ max = scalar;
+ }
+ }
+ return max;
+}
+
+function getLink(failure: Lint.RuleFailure, color: boolean): string {
+ const lineAndCharacter = failure.getStartPosition().getLineAndCharacter();
+ const sev = failure.getRuleSeverity().toUpperCase();
+ let path = failure.getFileName();
+ // Most autolinks only become clickable if they contain a slash in some way; so we make a top level file into a relative path here
+ if (path.indexOf("/") === -1 && path.indexOf("\\") === -1) {
+ path = `.${sep}${path}`;
+ }
+ return `${color ? (sev === "WARNING" ? colors.blue(sev) : colors.red(sev)) : sev}: ${path}:${lineAndCharacter.line + 1}:${lineAndCharacter.character + 1}`;
+}
+
+function getLinkMaxSize(failures: Lint.RuleFailure[]): number {
+ return max(failures, f => getLink(f, /*color*/ false).length);
+}
+
+function getNameMaxSize(failures: Lint.RuleFailure[]): number {
+ return max(failures, f => f.getRuleName().length);
+}
+
+function pad(str: string, visiblelen: number, len: number) {
+ if (visiblelen >= len) return str;
+ const count = len - visiblelen;
+ for (let i = 0; i < count; i++) {
+ str += " ";
+ }
+ return str;
+}
+
+export class Formatter extends Lint.Formatters.AbstractFormatter {
+ public static metadata: Lint.IFormatterMetadata = {
+ formatterName: "autolinkableStylish",
+ description: "Human-readable formatter which creates stylish messages with autolinkable filepaths.",
+ descriptionDetails: Lint.Utils.dedent`
+ Colorized output grouped by file, with autolinkable filepaths containing line and column information
+ `,
+ sample: Lint.Utils.dedent`
+ src/myFile.ts
+ ERROR: src/myFile.ts:1:14 semicolon Missing semicolon`,
+ consumer: "human"
+ };
+ public format(failures: Lint.RuleFailure[]): string {
+ return groupBy(failures, f => f.getFileName()).map(group => {
+ const currentFile = group[0].getFileName();
+ const linkMaxSize = getLinkMaxSize(group);
+ const nameMaxSize = getNameMaxSize(group);
+ return `
+${currentFile}
+${group.map(f => `${pad(getLink(f, /*color*/ true), getLink(f, /*color*/ false).length, linkMaxSize)} ${colors.grey(pad(f.getRuleName(), f.getRuleName().length, nameMaxSize))} ${colors.yellow(f.getFailure())}`).join("\n")}`;
+ }).join("\n");
+ }
+}
\ No newline at end of file
diff --git a/scripts/tslint/booleanTriviaRule.ts b/scripts/tslint/rules/booleanTriviaRule.ts
similarity index 100%
rename from scripts/tslint/booleanTriviaRule.ts
rename to scripts/tslint/rules/booleanTriviaRule.ts
diff --git a/scripts/tslint/debugAssertRule.ts b/scripts/tslint/rules/debugAssertRule.ts
similarity index 100%
rename from scripts/tslint/debugAssertRule.ts
rename to scripts/tslint/rules/debugAssertRule.ts
diff --git a/scripts/tslint/nextLineRule.ts b/scripts/tslint/rules/nextLineRule.ts
similarity index 100%
rename from scripts/tslint/nextLineRule.ts
rename to scripts/tslint/rules/nextLineRule.ts
diff --git a/scripts/tslint/noBomRule.ts b/scripts/tslint/rules/noBomRule.ts
similarity index 100%
rename from scripts/tslint/noBomRule.ts
rename to scripts/tslint/rules/noBomRule.ts
diff --git a/scripts/tslint/noInOperatorRule.ts b/scripts/tslint/rules/noInOperatorRule.ts
similarity index 100%
rename from scripts/tslint/noInOperatorRule.ts
rename to scripts/tslint/rules/noInOperatorRule.ts
diff --git a/scripts/tslint/noIncrementDecrementRule.ts b/scripts/tslint/rules/noIncrementDecrementRule.ts
similarity index 100%
rename from scripts/tslint/noIncrementDecrementRule.ts
rename to scripts/tslint/rules/noIncrementDecrementRule.ts
diff --git a/scripts/tslint/noTypeAssertionWhitespaceRule.ts b/scripts/tslint/rules/noTypeAssertionWhitespaceRule.ts
similarity index 100%
rename from scripts/tslint/noTypeAssertionWhitespaceRule.ts
rename to scripts/tslint/rules/noTypeAssertionWhitespaceRule.ts
diff --git a/scripts/tslint/objectLiteralSurroundingSpaceRule.ts b/scripts/tslint/rules/objectLiteralSurroundingSpaceRule.ts
similarity index 100%
rename from scripts/tslint/objectLiteralSurroundingSpaceRule.ts
rename to scripts/tslint/rules/objectLiteralSurroundingSpaceRule.ts
diff --git a/scripts/tslint/typeOperatorSpacingRule.ts b/scripts/tslint/rules/typeOperatorSpacingRule.ts
similarity index 100%
rename from scripts/tslint/typeOperatorSpacingRule.ts
rename to scripts/tslint/rules/typeOperatorSpacingRule.ts
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index a7e94da09d9..97e60777159 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -203,9 +203,11 @@ namespace ts {
node.symbol = symbol;
if (!symbol.declarations) {
- symbol.declarations = [];
+ symbol.declarations = [node];
+ }
+ else {
+ symbol.declarations.push(node);
}
- symbol.declarations.push(node);
if (symbolFlags & SymbolFlags.HasExports && !symbol.exports) {
symbol.exports = createSymbolTable();
@@ -282,17 +284,8 @@ namespace ts {
const index = indexOf(functionType.parameters, node);
return "arg" + index as __String;
case SyntaxKind.JSDocTypedefTag:
- const parentNode = node.parent && node.parent.parent;
- let nameFromParentNode: __String;
- if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) {
- if ((parentNode).declarationList.declarations.length > 0) {
- const nameIdentifier = (parentNode).declarationList.declarations[0].name;
- if (isIdentifier(nameIdentifier)) {
- nameFromParentNode = nameIdentifier.escapedText;
- }
- }
- }
- return nameFromParentNode;
+ const name = getNameOfJSDocTypedef(node as JSDocTypedefTag);
+ return typeof name !== "undefined" ? name.escapedText : undefined;
}
}
@@ -598,7 +591,7 @@ namespace ts {
// Binding of JsDocComment should be done before the current block scope container changes.
// because the scope of JsDocComment should not be affected by whether the current node is a
// container or not.
- if (node.jsDoc) {
+ if (hasJSDocNodes(node)) {
if (isInJavaScriptFile(node)) {
for (const j of node.jsDoc) {
bind(j);
@@ -1931,7 +1924,7 @@ namespace ts {
}
function bindJSDocTypedefTagIfAny(node: Node) {
- if (!node.jsDoc) {
+ if (!hasJSDocNodes(node)) {
return;
}
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index e8c47a468d8..bff534438bf 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -58,6 +58,7 @@ namespace ts {
let symbolInstantiationDepth = 0;
const emptySymbols = createSymbolTable();
+ const identityMapper: (type: Type) => Type = identity;
const compilerOptions = host.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
@@ -224,12 +225,13 @@ namespace ts {
return tryFindAmbientModule(moduleName, /*withAugmentations*/ false);
},
getApparentType,
- getAllPossiblePropertiesOfType,
+ isArrayLikeType,
+ getAllPossiblePropertiesOfTypes,
getSuggestionForNonexistentProperty: (node, type) => unescapeLeadingUnderscores(getSuggestionForNonexistentProperty(node, type)),
getSuggestionForNonexistentSymbol: (location, name, meaning) => unescapeLeadingUnderscores(getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning)),
getBaseConstraintOfType,
resolveName(name, location, meaning) {
- return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
+ return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false);
},
getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()),
};
@@ -333,7 +335,8 @@ namespace ts {
let flowLoopStart = 0;
let flowLoopCount = 0;
- let visitedFlowCount = 0;
+ let sharedFlowCount = 0;
+ let flowAnalysisDisabled = false;
const emptyStringType = getLiteralType("");
const zeroType = getLiteralType(0);
@@ -351,8 +354,8 @@ namespace ts {
const flowLoopNodes: FlowNode[] = [];
const flowLoopKeys: string[] = [];
const flowLoopTypes: Type[][] = [];
- const visitedFlowNodes: FlowNode[] = [];
- const visitedFlowTypes: FlowType[] = [];
+ const sharedFlowNodes: FlowNode[] = [];
+ const sharedFlowTypes: FlowType[] = [];
const potentialThisCollisions: Node[] = [];
const potentialNewTargetCollisions: Node[] = [];
const awaitedTypeStack: number[] = [];
@@ -575,7 +578,7 @@ namespace ts {
function cloneSymbol(symbol: Symbol): Symbol {
const result = createSymbol(symbol.flags, symbol.escapedName);
- result.declarations = symbol.declarations.slice(0);
+ result.declarations = symbol.declarations ? symbol.declarations.slice() : [];
result.parent = symbol.parent;
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true;
@@ -863,17 +866,22 @@ namespace ts {
}
}
- // 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.
+ /**
+ * Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
+ * the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with
+ * the given name can be found.
+ *
+ * @param isUse If true, this will count towards --noUnusedLocals / --noUnusedParameters.
+ */
function resolveName(
location: Node | undefined,
name: __String,
meaning: SymbolFlags,
nameNotFoundMessage: DiagnosticMessage | undefined,
nameArg: __String | Identifier,
+ isUse: boolean,
suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol {
- return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, getSymbol, suggestedNameNotFoundMessage);
+ return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, getSymbol, suggestedNameNotFoundMessage);
}
function resolveNameHelper(
@@ -882,6 +890,7 @@ namespace ts {
meaning: SymbolFlags,
nameNotFoundMessage: DiagnosticMessage,
nameArg: __String | Identifier,
+ isUse: boolean,
lookup: typeof getSymbol,
suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol {
const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location
@@ -1112,11 +1121,18 @@ namespace ts {
// We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`.
// If `result === lastLocation.symbol`, that means that we are somewhere inside `lastLocation` 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 (result && nameNotFoundMessage && noUnusedIdentifiers && result !== lastLocation.symbol) {
+ if (isUse && result && nameNotFoundMessage && noUnusedIdentifiers && result !== lastLocation.symbol) {
result.isReferenced = true;
}
if (!result) {
+ if (lastLocation) {
+ Debug.assert(lastLocation.kind === SyntaxKind.SourceFile);
+ if ((lastLocation as SourceFile).commonJsModuleIndicator && name === "exports") {
+ return lastLocation.symbol;
+ }
+ }
+
result = lookup(globals, name, meaning);
}
@@ -1265,7 +1281,7 @@ namespace ts {
function checkAndReportErrorForUsingTypeAsNamespace(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
if (meaning === SymbolFlags.Namespace) {
- const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
+ const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
const parent = errorLocation.parent;
if (symbol) {
if (isQualifiedName(parent)) {
@@ -1296,7 +1312,7 @@ namespace ts {
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));
+ const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) {
error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, unescapeLeadingUnderscores(name));
return true;
@@ -1307,14 +1323,14 @@ namespace ts {
function checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Type)) {
- const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
+ const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
if (symbol) {
error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_value, unescapeLeadingUnderscores(name));
return true;
}
}
else if (meaning & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Value)) {
- const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
+ const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
if (symbol) {
error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, unescapeLeadingUnderscores(name));
return true;
@@ -1638,7 +1654,7 @@ namespace ts {
if (name.kind === SyntaxKind.Identifier) {
const message = meaning === SymbolFlags.Namespace ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
- symbol = resolveName(location || name, name.escapedText, meaning, ignoreErrors ? undefined : message, name);
+ symbol = resolveName(location || name, name.escapedText, meaning, ignoreErrors ? undefined : message, name, /*isUse*/ true);
if (!symbol) {
return undefined;
}
@@ -1686,7 +1702,7 @@ namespace ts {
undefined;
}
else {
- Debug.fail("Unknown entity name kind.");
+ Debug.assertNever(name, "Unknown entity name kind.");
}
Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol);
@@ -1743,13 +1759,13 @@ namespace ts {
}
// May be an untyped module. If so, ignore resolutionDiagnostic.
- if (resolvedModule && resolvedModule.isExternalLibraryImport && !extensionIsTypeScript(resolvedModule.extension)) {
+ if (resolvedModule && !extensionIsTypeScript(resolvedModule.extension) && resolutionDiagnostic === undefined || resolutionDiagnostic === Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type) {
if (isForAugmentation) {
const diag = Diagnostics.Invalid_module_name_in_augmentation_Module_0_resolves_to_an_untyped_module_at_1_which_cannot_be_augmented;
error(errorNode, diag, moduleReference, resolvedModule.resolvedFileName);
}
else if (noImplicitAny && moduleNotFoundError) {
- let errorInfo = chainDiagnosticMessages(/*details*/ undefined,
+ let errorInfo = !resolvedModule.isExternalLibraryImport ? undefined : chainDiagnosticMessages(/*details*/ undefined,
Diagnostics.Try_npm_install_types_Slash_0_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0,
moduleReference);
errorInfo = chainDiagnosticMessages(errorInfo,
@@ -2061,7 +2077,7 @@ namespace ts {
return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace;
}
- function getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] | undefined {
+ function getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] | undefined {
if (!(symbol && !isPropertyOrMethodDeclarationSymbol(symbol))) {
return undefined;
}
@@ -2095,6 +2111,10 @@ namespace ts {
canQualifySymbol(symbolFromSymbolTable, meaning);
}
+ function isUMDExportSymbol(symbol: Symbol) {
+ return symbol && symbol.declarations && symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]);
+ }
+
function trySymbolTable(symbols: SymbolTable) {
// If symbol is directly available by its name in the symbol table
if (isAccessible(symbols.get(symbol.escapedName))) {
@@ -2106,6 +2126,7 @@ namespace ts {
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
&& symbolFromSymbolTable.escapedName !== "export="
&& !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)
+ && !(isUMDExportSymbol(symbolFromSymbolTable) && enclosingDeclaration && isExternalModule(getSourceFileOfNode(enclosingDeclaration)))
// If `!useOnlyExternalAliasing`, we can use any type of alias to get the name
&& (!useOnlyExternalAliasing || some(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration))) {
@@ -2125,7 +2146,7 @@ namespace ts {
}
}
- function needsQualification(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags) {
+ function needsQualification(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags) {
let qualify = false;
forEachSymbolTableInScope(enclosingDeclaration, symbolTable => {
// If symbol of this name is not available in the symbol table we are ok
@@ -2307,7 +2328,7 @@ namespace ts {
}
const firstIdentifier = getFirstIdentifier(entityName);
- const symbol = resolveName(enclosingDeclaration, firstIdentifier.escapedText, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
+ const symbol = resolveName(enclosingDeclaration, firstIdentifier.escapedText, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false);
// Verify if the symbol is accessible
return (symbol && hasVisibleDeclarations(symbol, /*shouldComputeAliasToMakeVisible*/ true)) || {
@@ -3964,7 +3985,7 @@ namespace ts {
function collectLinkedAliases(node: Identifier): Node[] {
let exportSymbol: Symbol;
if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) {
- exportSymbol = resolveName(node.parent, node.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, Diagnostics.Cannot_find_name_0, node);
+ exportSymbol = resolveName(node.parent, node.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, Diagnostics.Cannot_find_name_0, node, /*isUse*/ false);
}
else if (node.parent.kind === SyntaxKind.ExportSpecifier) {
exportSymbol = getTargetOfExportSpecifier(node.parent, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
@@ -3986,7 +4007,7 @@ namespace ts {
const internalModuleReference = (declaration).moduleReference;
const firstIdentifier = getFirstIdentifier(internalModuleReference);
const importSymbol = resolveName(declaration, firstIdentifier.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace,
- undefined, undefined);
+ undefined, undefined, /*isUse*/ false);
if (importSymbol) {
buildVisibleNodeList(importSymbol.declarations);
}
@@ -4794,22 +4815,39 @@ namespace ts {
return typeParameters;
}
- // Appends the outer type parameters of a node to a set of type parameters and returns the resulting set. The function
- // allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set in-place and
- // returns the same array.
- function appendOuterTypeParameters(typeParameters: TypeParameter[], node: Node): TypeParameter[] {
+ // Return the outer type parameters of a node or undefined if the node has no outer type parameters.
+ function getOuterTypeParameters(node: Node, includeThisTypes?: boolean): TypeParameter[] {
while (true) {
node = node.parent;
if (!node) {
- return typeParameters;
+ return undefined;
}
- if (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression ||
- node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression ||
- node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.ArrowFunction) {
- const declarations = (node).typeParameters;
- if (declarations) {
- return appendTypeParameters(appendOuterTypeParameters(typeParameters, node), declarations);
- }
+ switch (node.kind) {
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.ClassExpression:
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.CallSignature:
+ case SyntaxKind.ConstructSignature:
+ case SyntaxKind.MethodSignature:
+ case SyntaxKind.FunctionType:
+ case SyntaxKind.ConstructorType:
+ case SyntaxKind.JSDocFunctionType:
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.FunctionExpression:
+ case SyntaxKind.ArrowFunction:
+ case SyntaxKind.TypeAliasDeclaration:
+ case SyntaxKind.JSDocTemplateTag:
+ case SyntaxKind.MappedType:
+ const outerTypeParameters = getOuterTypeParameters(node, includeThisTypes);
+ if (node.kind === SyntaxKind.MappedType) {
+ return append(outerTypeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfNode((node).typeParameter)));
+ }
+ const outerAndOwnTypeParameters = appendTypeParameters(outerTypeParameters, getEffectiveTypeParameterDeclarations(node) || emptyArray);
+ const thisType = includeThisTypes &&
+ (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.InterfaceDeclaration) &&
+ getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node)).thisType;
+ return thisType ? append(outerAndOwnTypeParameters, thisType) : outerAndOwnTypeParameters;
}
}
}
@@ -4817,7 +4855,7 @@ namespace ts {
// The outer type parameters are those defined by enclosing generic classes, methods, or functions.
function getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
const declaration = symbol.flags & SymbolFlags.Class ? symbol.valueDeclaration : getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration);
- return appendOuterTypeParameters(/*typeParameters*/ undefined, declaration);
+ return getOuterTypeParameters(declaration);
}
// The local type parameters are the combined set of type parameters from all declarations of the class,
@@ -4878,7 +4916,7 @@ namespace ts {
function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: ReadonlyArray, location: Node): Signature[] {
const signatures = getConstructorsForTypeArguments(type, typeArgumentNodes, location);
const typeArguments = map(typeArgumentNodes, getTypeFromTypeNode);
- return sameMap(signatures, sig => some(sig.typeParameters) ? getSignatureInstantiation(sig, typeArguments) : sig);
+ return sameMap(signatures, sig => some(sig.typeParameters) ? getSignatureInstantiation(sig, typeArguments, isInJavaScriptFile(location)) : sig);
}
/**
@@ -5474,7 +5512,7 @@ namespace ts {
const minTypeArgumentCount = getMinTypeArgumentCount(baseSig.typeParameters);
const typeParamCount = length(baseSig.typeParameters);
if ((isJavaScript || typeArgCount >= minTypeArgumentCount) && typeArgCount <= typeParamCount) {
- const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, baseTypeNode)) : cloneSignature(baseSig);
+ const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, isJavaScript)) : cloneSignature(baseSig);
sig.typeParameters = classType.localTypeParameters;
sig.resolvedReturnType = classType;
result.push(sig);
@@ -5888,25 +5926,21 @@ namespace ts {
getPropertiesOfObjectType(type);
}
- function getAllPossiblePropertiesOfType(type: Type): Symbol[] {
- if (type.flags & TypeFlags.Union) {
- const props = createSymbolTable();
- for (const memberType of (type as UnionType).types) {
- if (memberType.flags & TypeFlags.Primitive) {
- continue;
- }
+ function getAllPossiblePropertiesOfTypes(types: Type[]): Symbol[] {
+ const unionType = getUnionType(types);
+ if (!(unionType.flags & TypeFlags.Union)) {
+ return getPropertiesOfType(unionType);
+ }
- for (const { escapedName } of getPropertiesOfType(memberType)) {
- if (!props.has(escapedName)) {
- props.set(escapedName, createUnionOrIntersectionProperty(type as UnionType, escapedName));
- }
+ const props = createSymbolTable();
+ for (const memberType of types) {
+ for (const { escapedName } of getPropertiesOfType(memberType)) {
+ if (!props.has(escapedName)) {
+ props.set(escapedName, createUnionOrIntersectionProperty(unionType as UnionType, escapedName));
}
}
- return arrayFrom(props.values());
- }
- else {
- return getPropertiesOfType(type);
}
+ return arrayFrom(props.values());
}
function getConstraintOfType(type: TypeVariable | UnionOrIntersectionType): Type {
@@ -6337,11 +6371,10 @@ namespace ts {
* @param typeParameters The requested type parameters.
* @param minTypeArgumentCount The minimum number of required type arguments.
*/
- function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number, location?: Node) {
+ function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScript: boolean) {
const numTypeParameters = length(typeParameters);
if (numTypeParameters) {
const numTypeArguments = length(typeArguments);
- const isJavaScript = isInJavaScriptFile(location);
if ((isJavaScript || numTypeArguments >= minTypeArgumentCount) && numTypeArguments <= numTypeParameters) {
if (!typeArguments) {
typeArguments = [];
@@ -6384,7 +6417,7 @@ namespace ts {
let paramSymbol = param.symbol;
// Include parameter symbol instead of property symbol in the signature
if (paramSymbol && !!(paramSymbol.flags & SymbolFlags.Property) && !isBindingPattern(param.name)) {
- const resolvedSymbol = resolveName(param, paramSymbol.escapedName, SymbolFlags.Value, undefined, undefined);
+ const resolvedSymbol = resolveName(param, paramSymbol.escapedName, SymbolFlags.Value, undefined, undefined, /*isUse*/ false);
paramSymbol = resolvedSymbol;
}
if (i === 0 && paramSymbol.escapedName === "this") {
@@ -6599,8 +6632,8 @@ namespace ts {
return anyType;
}
- function getSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature {
- typeArguments = fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters));
+ function getSignatureInstantiation(signature: Signature, typeArguments: Type[], isJavascript: boolean): Signature {
+ typeArguments = fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters), isJavascript);
const instantiations = signature.instantiations || (signature.instantiations = createMap());
const id = getTypeListId(typeArguments);
let instantiation = instantiations.get(id);
@@ -6615,11 +6648,33 @@ namespace ts {
}
function getErasedSignature(signature: Signature): Signature {
- if (!signature.typeParameters) return signature;
- if (!signature.erasedSignatureCache) {
- signature.erasedSignatureCache = instantiateSignature(signature, createTypeEraser(signature.typeParameters), /*eraseTypeParameters*/ true);
- }
- return signature.erasedSignatureCache;
+ return signature.typeParameters ?
+ signature.erasedSignatureCache || (signature.erasedSignatureCache = createErasedSignature(signature)) :
+ signature;
+ }
+
+ function createErasedSignature(signature: Signature) {
+ // Create an instantiation of the signature where all type arguments are the any type.
+ return instantiateSignature(signature, createTypeEraser(signature.typeParameters), /*eraseTypeParameters*/ true);
+ }
+
+ function getCanonicalSignature(signature: Signature): Signature {
+ return signature.typeParameters ?
+ signature.canonicalSignatureCache || (signature.canonicalSignatureCache = createCanonicalSignature(signature)) :
+ signature;
+ }
+
+ function createCanonicalSignature(signature: Signature) {
+ // Create an instantiation of the signature where each unconstrained type parameter is replaced with
+ // its original. When a generic class or interface is instantiated, each generic method in the class or
+ // interface is instantiated with a fresh set of cloned type parameters (which we need to handle scenarios
+ // where different generations of the same type parameter are in scope). This leads to a lot of new type
+ // identities, and potentially a lot of work comparing those identities, so here we create an instantiation
+ // that uses the original type identities for all unconstrained type parameters.
+ return getSignatureInstantiation(
+ signature,
+ map(signature.typeParameters, tp => tp.target && !getConstraintOfTypeParameter(tp.target) ? tp.target : tp),
+ isInJavaScriptFile(signature.declaration));
}
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
@@ -6770,7 +6825,8 @@ namespace ts {
if (typeParameters) {
const numTypeArguments = length(node.typeArguments);
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
- if (!isInJavaScriptFile(node) && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) {
+ const isJavascript = isInJavaScriptFile(node);
+ if (!isJavascript && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) {
error(node,
minTypeArgumentCount === typeParameters.length
? Diagnostics.Generic_type_0_requires_1_type_argument_s
@@ -6783,7 +6839,7 @@ namespace ts {
// In a type reference, the outer type parameters of the referenced class or interface are automatically
// supplied as type arguments and the type reference only specifies arguments for the local type parameters
// of the class or interface.
- const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, node));
+ const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, isJavascript));
return createTypeReference(type, typeArguments);
}
if (node.typeArguments) {
@@ -6800,7 +6856,7 @@ namespace ts {
const id = getTypeListId(typeArguments);
let instantiation = links.instantiations.get(id);
if (!instantiation) {
- links.instantiations.set(id, instantiation = instantiateTypeNoAlias(type, createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters)))));
+ links.instantiations.set(id, instantiation = instantiateType(type, createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters), isInJavaScriptFile(symbol.valueDeclaration)))));
}
return instantiation;
}
@@ -7042,7 +7098,8 @@ namespace ts {
}
function getGlobalSymbol(name: __String, meaning: SymbolFlags, diagnostic: DiagnosticMessage): Symbol {
- return resolveName(undefined, name, meaning, diagnostic, name);
+ // Don't track references for global symbols anyway, so value if `isReference` is arbitrary
+ return resolveName(undefined, name, meaning, diagnostic, name, /*isUse*/ false);
}
function getGlobalType(name: __String, arity: 0, reportErrors: boolean): ObjectType;
@@ -7248,6 +7305,22 @@ namespace ts {
return binarySearchTypes(types, type) >= 0;
}
+ // Return true if the given intersection type contains (a) more than one unit type or (b) an object
+ // type and a nullable type (null or undefined).
+ function isEmptyIntersectionType(type: IntersectionType) {
+ let combined: TypeFlags = 0;
+ for (const t of type.types) {
+ if (t.flags & TypeFlags.Unit && combined & TypeFlags.Unit) {
+ return true;
+ }
+ combined |= t.flags;
+ if (combined & TypeFlags.Nullable && combined & (TypeFlags.Object | TypeFlags.NonPrimitive)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
function addTypeToUnion(typeSet: TypeSet, type: Type) {
const flags = type.flags;
if (flags & TypeFlags.Union) {
@@ -7261,7 +7334,11 @@ namespace ts {
if (flags & TypeFlags.Null) typeSet.containsNull = true;
if (!(flags & TypeFlags.ContainsWideningType)) typeSet.containsNonWideningType = true;
}
- else if (!(flags & TypeFlags.Never)) {
+ else if (!(flags & TypeFlags.Never || flags & TypeFlags.Intersection && isEmptyIntersectionType(type))) {
+ // We ignore 'never' types in unions. Likewise, we ignore intersections of unit types as they are
+ // another form of 'never' (in that they have an empty value domain). We could in theory turn
+ // intersections of unit types into 'never' upon construction, but deferring the reduction makes it
+ // easier to reason about their origin.
if (flags & TypeFlags.String) typeSet.containsString = true;
if (flags & TypeFlags.Number) typeSet.containsNumber = true;
if (flags & TypeFlags.StringOrNumberLiteral) typeSet.containsStringOrNumberLiteral = true;
@@ -7394,6 +7471,12 @@ namespace ts {
type = createType(TypeFlags.Union | propagatedFlags);
unionTypes.set(id, type);
type.types = types;
+ /*
+ Note: This is the alias symbol (or lack thereof) that we see when we first encounter this union type.
+ For aliases of identical unions, eg `type T = A | B; type U = A | B`, the symbol of the first alias encountered is the aliasSymbol.
+ (In the language service, the order may depend on the order in which a user takes actions, such as hovering over symbols.)
+ It's important that we create equivalent union types only once, so that's an unfortunate side effect.
+ */
type.aliasSymbol = aliasSymbol;
type.aliasTypeArguments = aliasTypeArguments;
}
@@ -7487,7 +7570,7 @@ namespace ts {
type = createType(TypeFlags.Intersection | propagatedFlags);
intersectionTypes.set(id, type);
type.types = typeSet;
- type.aliasSymbol = aliasSymbol;
+ type.aliasSymbol = aliasSymbol; // See comment in `getUnionTypeFromSortedList`.
type.aliasTypeArguments = aliasTypeArguments;
}
return type;
@@ -7782,7 +7865,10 @@ namespace ts {
return mapType(right, t => getSpreadType(left, t));
}
if (right.flags & TypeFlags.NonPrimitive) {
- return emptyObjectType;
+ return nonPrimitiveType;
+ }
+ if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.EnumLike)) {
+ return left;
}
const members = createSymbolTable();
@@ -7809,6 +7895,7 @@ namespace ts {
members.set(rightProp.escapedName, getNonReadonlySymbol(rightProp));
}
}
+
for (const leftProp of getPropertiesOfType(left)) {
if (leftProp.flags & SymbolFlags.SetAccessor && !(leftProp.flags & SymbolFlags.GetAccessor)
|| skippedPrivateMembers.has(leftProp.escapedName)
@@ -7920,7 +8007,7 @@ namespace ts {
return unknownType;
}
- function getTypeFromThisTypeNode(node: TypeNode): Type {
+ function getTypeFromThisTypeNode(node: ThisExpression | ThisTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getThisType(node);
@@ -7954,7 +8041,7 @@ namespace ts {
return node.flags & NodeFlags.JavaScriptFile ? anyType : nonPrimitiveType;
case SyntaxKind.ThisType:
case SyntaxKind.ThisKeyword:
- return getTypeFromThisTypeNode(node);
+ return getTypeFromThisTypeNode(node as ThisExpression | ThisTypeNode);
case SyntaxKind.LiteralType:
return getTypeFromLiteralTypeNode(node);
case SyntaxKind.TypeReference:
@@ -8024,11 +8111,6 @@ namespace ts {
return instantiateList(signatures, mapper, instantiateSignature);
}
- function instantiateCached(type: T, mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T {
- const instantiations = mapper.instantiations || (mapper.instantiations = []);
- return instantiations[type.id] || (instantiations[type.id] = instantiator(type, mapper));
- }
-
function makeUnaryTypeMapper(source: Type, target: Type) {
return (t: Type) => t === source ? target : t;
}
@@ -8050,11 +8132,9 @@ namespace ts {
function createTypeMapper(sources: TypeParameter[], targets: Type[]): TypeMapper {
Debug.assert(targets === undefined || sources.length === targets.length);
- const mapper: TypeMapper = sources.length === 1 ? makeUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) :
+ return sources.length === 1 ? makeUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) :
sources.length === 2 ? makeBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) :
- makeArrayTypeMapper(sources, targets);
- mapper.mappedTypes = sources;
- return mapper;
+ makeArrayTypeMapper(sources, targets);
}
function createTypeEraser(sources: TypeParameter[]): TypeMapper {
@@ -8065,10 +8145,8 @@ namespace ts {
* Maps forward-references to later types parameters to the empty object type.
* This is used during inference when instantiating type parameter defaults.
*/
- function createBackreferenceMapper(typeParameters: TypeParameter[], index: number) {
- const mapper: TypeMapper = t => indexOf(typeParameters, t) >= index ? emptyObjectType : t;
- mapper.mappedTypes = typeParameters;
- return mapper;
+ function createBackreferenceMapper(typeParameters: TypeParameter[], index: number): TypeMapper {
+ return t => indexOf(typeParameters, t) >= index ? emptyObjectType : t;
}
function isInferenceContext(mapper: TypeMapper): mapper is InferenceContext {
@@ -8081,20 +8159,12 @@ namespace ts {
mapper;
}
- function identityMapper(type: Type): Type {
- return type;
- }
-
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
- const mapper: TypeMapper = t => instantiateType(mapper1(t), mapper2);
- mapper.mappedTypes = concatenate(mapper1.mappedTypes, mapper2.mappedTypes);
- return mapper;
+ return t => instantiateType(mapper1(t), mapper2);
}
- function createReplacementMapper(source: Type, target: Type, baseMapper: TypeMapper) {
- const mapper: TypeMapper = t => t === source ? target : baseMapper(t);
- mapper.mappedTypes = baseMapper.mappedTypes;
- return mapper;
+ function createReplacementMapper(source: Type, target: Type, baseMapper: TypeMapper): TypeMapper {
+ return t => t === source ? target : baseMapper(t);
}
function cloneTypeParameter(typeParameter: TypeParameter): TypeParameter {
@@ -8174,13 +8244,53 @@ namespace ts {
return result;
}
- function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType {
- const result = createObjectType(ObjectFlags.Anonymous | ObjectFlags.Instantiated, type.symbol);
- result.target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type;
- result.mapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper;
- result.aliasSymbol = type.aliasSymbol;
- result.aliasTypeArguments = instantiateTypes(type.aliasTypeArguments, mapper);
- return result;
+ function getAnonymousTypeInstantiation(type: AnonymousType, mapper: TypeMapper) {
+ const target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type;
+ const symbol = target.symbol;
+ const links = getSymbolLinks(symbol);
+ let typeParameters = links.typeParameters;
+ if (!typeParameters) {
+ // The first time an anonymous type is instantiated we compute and store a list of the type
+ // parameters that are in scope (and therefore potentially referenced). For type literals that
+ // aren't the right hand side of a generic type alias declaration we optimize by reducing the
+ // set of type parameters to those that are actually referenced somewhere in the literal.
+ const declaration = symbol.declarations[0];
+ const outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true) || emptyArray;
+ typeParameters = symbol.flags & SymbolFlags.TypeLiteral && !target.aliasTypeArguments ?
+ filter(outerTypeParameters, tp => isTypeParameterReferencedWithin(tp, declaration)) :
+ outerTypeParameters;
+ links.typeParameters = typeParameters;
+ if (typeParameters.length) {
+ links.instantiations = createMap();
+ links.instantiations.set(getTypeListId(typeParameters), target);
+ }
+ }
+ if (typeParameters.length) {
+ // We are instantiating an anonymous type that has one or more type parameters in scope. Apply the
+ // mapper to the type parameters to produce the effective list of type arguments, and compute the
+ // instantiation cache key from the type IDs of the type arguments.
+ const combinedMapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper;
+ const typeArguments = map(typeParameters, combinedMapper);
+ const id = getTypeListId(typeArguments);
+ let result = links.instantiations.get(id);
+ if (!result) {
+ const newMapper = createTypeMapper(typeParameters, typeArguments);
+ result = target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(target, newMapper) : instantiateAnonymousType(target, newMapper);
+ links.instantiations.set(id, result);
+ }
+ return result;
+ }
+ return type;
+ }
+
+ function isTypeParameterReferencedWithin(tp: TypeParameter, node: Node) {
+ return tp.isThisType ? forEachChild(node, checkThis) : forEachChild(node, checkIdentifier);
+ function checkThis(node: Node): boolean {
+ return node.kind === SyntaxKind.ThisType || forEachChild(node, checkThis);
+ }
+ function checkIdentifier(node: Node): boolean {
+ return node.kind === SyntaxKind.Identifier && isPartOfTypeNode(node) && getTypeFromTypeNode(node) === tp || forEachChild(node, checkIdentifier);
+ }
}
function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type {
@@ -8197,164 +8307,64 @@ namespace ts {
if (typeVariable !== mappedTypeVariable) {
return mapType(mappedTypeVariable, t => {
if (isMappableType(t)) {
- return instantiateMappedObjectType(type, createReplacementMapper(typeVariable, t, mapper));
+ return instantiateAnonymousType(type, createReplacementMapper(typeVariable, t, mapper));
}
return t;
});
}
}
}
- return instantiateMappedObjectType(type, mapper);
+ return instantiateAnonymousType(type, mapper);
}
function isMappableType(type: Type) {
return type.flags & (TypeFlags.TypeParameter | TypeFlags.Object | TypeFlags.Intersection | TypeFlags.IndexedAccess);
}
- function instantiateMappedObjectType(type: MappedType, mapper: TypeMapper): Type {
- const result = createObjectType(ObjectFlags.Mapped | ObjectFlags.Instantiated, type.symbol);
- result.declaration = type.declaration;
- result.mapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
+ function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType {
+ const result = createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol);
+ if (type.objectFlags & ObjectFlags.Mapped) {
+ (result).declaration = (type).declaration;
+ }
+ result.target = type;
+ result.mapper = mapper;
result.aliasSymbol = type.aliasSymbol;
result.aliasTypeArguments = instantiateTypes(type.aliasTypeArguments, mapper);
return result;
}
- function isSymbolInScopeOfMappedTypeParameter(symbol: Symbol, mapper: TypeMapper) {
- if (!(symbol.declarations && symbol.declarations.length)) {
- return false;
- }
- const mappedTypes = mapper.mappedTypes;
- // Starting with the parent of the symbol's declaration, check if the mapper maps any of
- // the type parameters introduced by enclosing declarations. We just pick the first
- // declaration since multiple declarations will all have the same parent anyway.
- return !!findAncestor(symbol.declarations[0], node => {
- if (node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.SourceFile) {
- return "quit";
- }
- switch (node.kind) {
- case SyntaxKind.FunctionType:
- case SyntaxKind.ConstructorType:
- case SyntaxKind.FunctionDeclaration:
- case SyntaxKind.MethodDeclaration:
- case SyntaxKind.MethodSignature:
- case SyntaxKind.Constructor:
- case SyntaxKind.CallSignature:
- case SyntaxKind.ConstructSignature:
- case SyntaxKind.IndexSignature:
- case SyntaxKind.GetAccessor:
- case SyntaxKind.SetAccessor:
- case SyntaxKind.FunctionExpression:
- case SyntaxKind.ArrowFunction:
- case SyntaxKind.ClassDeclaration:
- case SyntaxKind.ClassExpression:
- case SyntaxKind.InterfaceDeclaration:
- case SyntaxKind.TypeAliasDeclaration:
- const typeParameters = getEffectiveTypeParameterDeclarations(node as DeclarationWithTypeParameters);
- if (typeParameters) {
- for (const d of typeParameters) {
- if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode(d)))) {
- return true;
- }
- }
- }
- if (isClassLike(node) || node.kind === SyntaxKind.InterfaceDeclaration) {
- const thisType = getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node)).thisType;
- if (thisType && contains(mappedTypes, thisType)) {
- return true;
- }
- }
- break;
- case SyntaxKind.MappedType:
- if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode((node).typeParameter)))) {
- return true;
- }
- break;
- case SyntaxKind.JSDocFunctionType:
- const func = node as JSDocFunctionType;
- for (const p of func.parameters) {
- if (contains(mappedTypes, getTypeOfNode(p))) {
- return true;
- }
- }
- break;
- }
- });
- }
-
- function isTopLevelTypeAlias(symbol: Symbol) {
- if (symbol.declarations && symbol.declarations.length) {
- const parentKind = symbol.declarations[0].parent.kind;
- return parentKind === SyntaxKind.SourceFile || parentKind === SyntaxKind.ModuleBlock;
- }
- return false;
- }
-
function instantiateType(type: Type, mapper: TypeMapper): Type {
if (type && mapper !== identityMapper) {
- // If we are instantiating a type that has a top-level type alias, obtain the instantiation through
- // the type alias instead in order to share instantiations for the same type arguments. This can
- // dramatically reduce the number of structurally identical types we generate. Note that we can only
- // perform this optimization for top-level type aliases. Consider:
- //
- // function f1(x: T) {
- // type Foo = { x: X, t: T };
- // let obj: Foo = { x: x };
- // return obj;
- // }
- // function f2(x: U) { return f1(x); }
- // let z = f2(42);
- //
- // Above, the declaration of f2 has an inferred return type that is an instantiation of f1's Foo
- // equivalent to { x: U, t: U }. When instantiating this return type, we can't go back to Foo's
- // cache because all cached instantiations are of the form { x: ???, t: T }, i.e. they have not been
- // instantiated for T. Instead, we need to further instantiate the { x: U, t: U } form.
- if (type.aliasSymbol && isTopLevelTypeAlias(type.aliasSymbol)) {
- if (type.aliasTypeArguments) {
- return getTypeAliasInstantiation(type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
+ if (type.flags & TypeFlags.TypeParameter) {
+ return mapper(type);
+ }
+ if (type.flags & TypeFlags.Object) {
+ if ((type).objectFlags & ObjectFlags.Anonymous) {
+ // If the anonymous type originates in a declaration of a function, method, class, or
+ // interface, in an object type literal, or in an object literal expression, we may need
+ // to instantiate the type because it might reference a type parameter.
+ return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ?
+ getAnonymousTypeInstantiation(type, mapper) : type;
+ }
+ if ((type).objectFlags & ObjectFlags.Mapped) {
+ return getAnonymousTypeInstantiation(type, mapper);
+ }
+ if ((type).objectFlags & ObjectFlags.Reference) {
+ return createTypeReference((type).target, instantiateTypes((type).typeArguments, mapper));
}
- return type;
}
- return instantiateTypeNoAlias(type, mapper);
- }
- return type;
- }
-
- function instantiateTypeNoAlias(type: Type, mapper: TypeMapper): Type {
- if (type.flags & TypeFlags.TypeParameter) {
- return mapper(type);
- }
- if (type.flags & TypeFlags.Object) {
- if ((type).objectFlags & ObjectFlags.Anonymous) {
- // If the anonymous type originates in a declaration of a function, method, class, or
- // interface, in an object type literal, or in an object literal expression, we may need
- // to instantiate the type because it might reference a type parameter. We skip instantiation
- // if none of the type parameters that are in scope in the type's declaration are mapped by
- // the given mapper, however we can only do that analysis if the type isn't itself an
- // instantiation.
- return type.symbol &&
- type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) &&
- ((type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ?
- instantiateCached(type, mapper, instantiateAnonymousType) : type;
+ if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) {
+ return getUnionType(instantiateTypes((type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
}
- if ((type).objectFlags & ObjectFlags.Mapped) {
- return instantiateCached(type, mapper, instantiateMappedType);
+ if (type.flags & TypeFlags.Intersection) {
+ return getIntersectionType(instantiateTypes((type).types, mapper), type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
}
- if ((type).objectFlags & ObjectFlags.Reference) {
- return createTypeReference((type).target, instantiateTypes((type).typeArguments, mapper));
+ if (type.flags & TypeFlags.Index) {
+ return getIndexType(instantiateType((type).type, mapper));
+ }
+ if (type.flags & TypeFlags.IndexedAccess) {
+ return getIndexedAccessType(instantiateType((type).objectType, mapper), instantiateType((type).indexType, mapper));
}
- }
- if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) {
- return getUnionType(instantiateTypes((type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
- }
- if (type.flags & TypeFlags.Intersection) {
- return getIntersectionType(instantiateTypes((type).types, mapper), type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
- }
- if (type.flags & TypeFlags.Index) {
- return getIndexType(instantiateType((type).type, mapper));
- }
- if (type.flags & TypeFlags.IndexedAccess) {
- return getIndexedAccessType(instantiateType((type).objectType, mapper), instantiateType((type).indexType, mapper));
}
return type;
}
@@ -8529,7 +8539,8 @@ namespace ts {
return Ternary.False;
}
- if (source.typeParameters) {
+ if (source.typeParameters && source.typeParameters !== target.typeParameters) {
+ target = getCanonicalSignature(target);
source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes);
}
@@ -9781,15 +9792,15 @@ namespace ts {
return type.flags & TypeFlags.TypeParameter && !getConstraintFromTypeParameter(type);
}
- function isTypeReferenceWithGenericArguments(type: Type) {
- return getObjectFlags(type) & ObjectFlags.Reference && some((type).typeArguments, isUnconstrainedTypeParameter);
+ function isTypeReferenceWithGenericArguments(type: Type): boolean {
+ return getObjectFlags(type) & ObjectFlags.Reference && some((type).typeArguments, t => isUnconstrainedTypeParameter(t) || isTypeReferenceWithGenericArguments(t));
}
/**
* getTypeReferenceId(A) returns "111=0-12=1"
* where A.id=111 and number.id=12
*/
- function getTypeReferenceId(type: TypeReference, typeParameters: Type[]) {
+ function getTypeReferenceId(type: TypeReference, typeParameters: Type[], depth = 0) {
let result = "" + type.target.id;
for (const t of type.typeArguments) {
if (isUnconstrainedTypeParameter(t)) {
@@ -9800,6 +9811,9 @@ namespace ts {
}
result += "=" + index;
}
+ else if (depth < 4 && isTypeReferenceWithGenericArguments(t)) {
+ result += "<" + getTypeReferenceId(t as TypeReference, typeParameters, depth + 1) + ">";
+ }
else {
result += "-" + t.id;
}
@@ -10065,7 +10079,7 @@ namespace ts {
}
function isUnitType(type: Type): boolean {
- return (type.flags & (TypeFlags.Literal | TypeFlags.Undefined | TypeFlags.Null)) !== 0;
+ return !!(type.flags & TypeFlags.Unit);
}
function isLiteralType(type: Type): boolean {
@@ -10367,7 +10381,6 @@ namespace ts {
function createInferenceContext(signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext {
const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(signature.typeParameters, createInferenceInfo);
const context = mapper as InferenceContext;
- context.mappedTypes = signature.typeParameters;
context.signature = signature;
context.inferences = inferences;
context.flags = flags;
@@ -10412,7 +10425,7 @@ namespace ts {
// results for union and intersection types for performance reasons.
function couldContainTypeVariables(type: Type): boolean {
const objectFlags = getObjectFlags(type);
- return !!(type.flags & TypeFlags.TypeVariable ||
+ return !!(type.flags & (TypeFlags.TypeVariable | TypeFlags.Index) ||
objectFlags & ObjectFlags.Reference && forEach((type).typeArguments, couldContainTypeVariables) ||
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) ||
objectFlags & ObjectFlags.Mapped ||
@@ -10580,6 +10593,13 @@ namespace ts {
inferFromTypes(sourceTypes[i], targetTypes[i]);
}
}
+ else if (source.flags & TypeFlags.Index && target.flags & TypeFlags.Index) {
+ inferFromTypes((source).type, (target).type);
+ }
+ else if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) {
+ inferFromTypes((source).objectType, (target).objectType);
+ inferFromTypes((source).indexType, (target).indexType);
+ }
else if (target.flags & TypeFlags.UnionOrIntersection) {
const targetTypes = (target).types;
let typeVariableCount = 0;
@@ -10604,7 +10624,7 @@ namespace ts {
priority = savePriority;
}
}
- else if (source.flags & TypeFlags.UnionOrIntersection) {
+ else if (source.flags & TypeFlags.Union) {
// Source is a union or intersection type, infer from each constituent type
const sourceTypes = (source).types;
for (const sourceType of sourceTypes) {
@@ -10613,7 +10633,7 @@ namespace ts {
}
else {
source = getApparentType(source);
- if (source.flags & TypeFlags.Object) {
+ if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
const key = source.id + "," + target.id;
if (visited && visited.get(key)) {
return;
@@ -10653,6 +10673,12 @@ namespace ts {
}
function inferFromObjectTypes(source: Type, target: Type) {
+ if (isGenericMappedType(source) && isGenericMappedType(target)) {
+ // The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer
+ // from S to T and from X to Y.
+ inferFromTypes(getConstraintTypeFromMappedType(source), getConstraintTypeFromMappedType(target));
+ inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target));
+ }
if (getObjectFlags(target) & ObjectFlags.Mapped) {
const constraintType = getConstraintTypeFromMappedType(target);
if (constraintType.flags & TypeFlags.Index) {
@@ -10693,7 +10719,7 @@ namespace ts {
function inferFromProperties(source: Type, target: Type) {
const properties = getPropertiesOfObjectType(target);
for (const targetProp of properties) {
- const sourceProp = getPropertyOfObjectType(source, targetProp.escapedName);
+ const sourceProp = getPropertyOfType(source, targetProp.escapedName);
if (sourceProp) {
inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
}
@@ -10844,7 +10870,15 @@ namespace ts {
function getResolvedSymbol(node: Identifier): Symbol {
const links = getNodeLinks(node);
if (!links.resolvedSymbol) {
- links.resolvedSymbol = !nodeIsMissing(node) && resolveName(node, node.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, node, Diagnostics.Cannot_find_name_0_Did_you_mean_1) || unknownSymbol;
+ links.resolvedSymbol = !nodeIsMissing(node) &&
+ resolveName(
+ node,
+ node.escapedText,
+ SymbolFlags.Value | SymbolFlags.ExportValue,
+ Diagnostics.Cannot_find_name_0,
+ node,
+ !isWriteOnlyAccess(node),
+ Diagnostics.Cannot_find_name_0_Did_you_mean_1) || unknownSymbol;
}
return links.resolvedSymbol;
}
@@ -11485,14 +11519,25 @@ namespace ts {
return false;
}
+ function reportFlowControlError(node: Node) {
+ const block = findAncestor(node, isFunctionOrModuleBlock);
+ const sourceFile = getSourceFileOfNode(node);
+ const span = getSpanOfTokenAtPosition(sourceFile, block.statements.pos);
+ diagnostics.add(createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.The_containing_function_or_module_body_is_too_large_for_control_flow_analysis));
+ }
+
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
let key: string;
+ let flowDepth = 0;
+ if (flowAnalysisDisabled) {
+ return unknownType;
+ }
if (!reference.flowNode || !couldBeUninitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
return declaredType;
}
- const visitedFlowStart = visitedFlowCount;
+ const sharedFlowStart = sharedFlowCount;
const evolvedType = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode));
- visitedFlowCount = visitedFlowStart;
+ sharedFlowCount = sharedFlowStart;
// When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation,
// we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations
// on empty arrays are possible without implicit any errors and new element types can be inferred without
@@ -11504,60 +11549,70 @@ namespace ts {
return resultType;
function getTypeAtFlowNode(flow: FlowNode): FlowType {
+ if (flowDepth === 2500) {
+ // We have made 2500 recursive invocations. To avoid overflowing the call stack we report an error
+ // and disable further control flow analysis in the containing function or module body.
+ flowAnalysisDisabled = true;
+ reportFlowControlError(reference);
+ return unknownType;
+ }
+ flowDepth++;
while (true) {
- if (flow.flags & FlowFlags.Shared) {
+ const flags = flow.flags;
+ if (flags & FlowFlags.Shared) {
// We cache results of flow type resolution for shared nodes that were previously visited in
// the same getFlowTypeOfReference invocation. A node is considered shared when it is the
// antecedent of more than one node.
- for (let i = visitedFlowStart; i < visitedFlowCount; i++) {
- if (visitedFlowNodes[i] === flow) {
- return visitedFlowTypes[i];
+ for (let i = sharedFlowStart; i < sharedFlowCount; i++) {
+ if (sharedFlowNodes[i] === flow) {
+ flowDepth--;
+ return sharedFlowTypes[i];
}
}
}
let type: FlowType;
- if (flow.flags & FlowFlags.AfterFinally) {
+ if (flags & FlowFlags.AfterFinally) {
// block flow edge: finally -> pre-try (for larger explanation check comment in binder.ts - bindTryStatement
(flow).locked = true;
type = getTypeAtFlowNode((flow).antecedent);
(flow).locked = false;
}
- else if (flow.flags & FlowFlags.PreFinally) {
+ else if (flags & FlowFlags.PreFinally) {
// locked pre-finally flows are filtered out in getTypeAtFlowBranchLabel
// so here just redirect to antecedent
flow = (flow).antecedent;
continue;
}
- else if (flow.flags & FlowFlags.Assignment) {
+ else if (flags & FlowFlags.Assignment) {
type = getTypeAtFlowAssignment(flow);
if (!type) {
flow = (flow).antecedent;
continue;
}
}
- else if (flow.flags & FlowFlags.Condition) {
+ else if (flags & FlowFlags.Condition) {
type = getTypeAtFlowCondition(flow);
}
- else if (flow.flags & FlowFlags.SwitchClause) {
+ else if (flags & FlowFlags.SwitchClause) {
type = getTypeAtSwitchClause(flow);
}
- else if (flow.flags & FlowFlags.Label) {
+ else if (flags & FlowFlags.Label) {
if ((flow).antecedents.length === 1) {
flow = (flow).antecedents[0];
continue;
}
- type = flow.flags & FlowFlags.BranchLabel ?
+ type = flags & FlowFlags.BranchLabel ?
getTypeAtFlowBranchLabel(flow) :
getTypeAtFlowLoopLabel(flow);
}
- else if (flow.flags & FlowFlags.ArrayMutation) {
+ else if (flags & FlowFlags.ArrayMutation) {
type = getTypeAtFlowArrayMutation(flow);
if (!type) {
flow = (flow).antecedent;
continue;
}
}
- else if (flow.flags & FlowFlags.Start) {
+ else if (flags & FlowFlags.Start) {
// Check if we should continue with the control flow of the containing function.
const container = (flow).container;
if (container && container !== flowContainer && reference.kind !== SyntaxKind.PropertyAccessExpression && reference.kind !== SyntaxKind.ThisKeyword) {
@@ -11572,12 +11627,13 @@ namespace ts {
// simply return the non-auto declared type to reduce follow-on errors.
type = convertAutoToAny(declaredType);
}
- if (flow.flags & FlowFlags.Shared) {
+ if (flags & FlowFlags.Shared) {
// Record visited node and the associated type in the cache.
- visitedFlowNodes[visitedFlowCount] = flow;
- visitedFlowTypes[visitedFlowCount] = type;
- visitedFlowCount++;
+ sharedFlowNodes[sharedFlowCount] = flow;
+ sharedFlowTypes[sharedFlowCount] = type;
+ sharedFlowCount++;
}
+ flowDepth--;
return type;
}
}
@@ -11615,29 +11671,31 @@ namespace ts {
}
function getTypeAtFlowArrayMutation(flow: FlowArrayMutation): FlowType {
- const node = flow.node;
- const expr = node.kind === SyntaxKind.CallExpression ?
- ((node).expression).expression :
- ((node).left).expression;
- if (isMatchingReference(reference, getReferenceCandidate(expr))) {
- const flowType = getTypeAtFlowNode(flow.antecedent);
- const type = getTypeFromFlowType(flowType);
- if (getObjectFlags(type) & ObjectFlags.EvolvingArray) {
- let evolvedType = type;
- if (node.kind === SyntaxKind.CallExpression) {
- for (const arg of (node).arguments) {
- evolvedType = addEvolvingArrayElementType(evolvedType, arg);
+ if (declaredType === autoType || declaredType === autoArrayType) {
+ const node = flow.node;
+ const expr = node.kind === SyntaxKind.CallExpression ?
+ ((node).expression).expression :
+ ((node).left).expression;
+ if (isMatchingReference(reference, getReferenceCandidate(expr))) {
+ const flowType = getTypeAtFlowNode(flow.antecedent);
+ const type = getTypeFromFlowType(flowType);
+ if (getObjectFlags(type) & ObjectFlags.EvolvingArray) {
+ let evolvedType = type;
+ if (node.kind === SyntaxKind.CallExpression) {
+ for (const arg of (node).arguments) {
+ evolvedType = addEvolvingArrayElementType(evolvedType, arg);
+ }
}
- }
- else {
- const indexType = getTypeOfExpression(((node).left).argumentExpression);
- if (isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) {
- evolvedType = addEvolvingArrayElementType(evolvedType, (node).right);
+ else {
+ const indexType = getTypeOfExpression(((node).left).argumentExpression);
+ if (isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) {
+ evolvedType = addEvolvingArrayElementType(evolvedType, (node).right);
+ }
}
+ return evolvedType === type ? flowType : createFlowType(evolvedType, isIncomplete(flowType));
}
- return evolvedType === type ? flowType : createFlowType(evolvedType, isIncomplete(flowType));
+ return flowType;
}
- return flowType;
}
return undefined;
}
@@ -12838,7 +12896,8 @@ namespace ts {
}
}
}
- if (noImplicitThis || isInJavaScriptFile(func)) {
+ const inJs = isInJavaScriptFile(func);
+ if (noImplicitThis || inJs) {
const containingLiteral = getContainingObjectLiteral(func);
if (containingLiteral) {
// We have an object literal method. Check if the containing object literal has a contextual type
@@ -12865,10 +12924,20 @@ namespace ts {
}
// In an assignment of the form 'obj.xxx = function(...)' or 'obj[xxx] = function(...)', the
// contextual type for 'this' is 'obj'.
- if (func.parent.kind === SyntaxKind.BinaryExpression && (func.parent).operatorToken.kind === SyntaxKind.EqualsToken) {
- const target = (func.parent).left;
+ const { parent } = func;
+ if (parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.EqualsToken) {
+ const target = (parent).left;
if (target.kind === SyntaxKind.PropertyAccessExpression || target.kind === SyntaxKind.ElementAccessExpression) {
- return checkExpressionCached((target).expression);
+ const { expression } = target as PropertyAccessExpression | ElementAccessExpression;
+ // Don't contextually type `this` as `exports` in `exports.Point = function(x, y) { this.x = x; this.y = y; }`
+ if (inJs && isIdentifier(expression)) {
+ const sourceFile = getSourceFileOfNode(parent);
+ if (sourceFile.commonJsModuleIndicator && getResolvedSymbol(expression) === sourceFile.symbol) {
+ return undefined;
+ }
+ }
+
+ return checkExpressionCached(expression);
}
}
}
@@ -13555,6 +13624,7 @@ namespace ts {
for (let i = 0; i < node.properties.length; i++) {
const memberDecl = node.properties[i];
let member = memberDecl.symbol;
+ let literalName: __String | undefined;
if (memberDecl.kind === SyntaxKind.PropertyAssignment ||
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ||
isObjectLiteralMethod(memberDecl)) {
@@ -13565,6 +13635,12 @@ namespace ts {
let type: Type;
if (memberDecl.kind === SyntaxKind.PropertyAssignment) {
+ if (memberDecl.name.kind === SyntaxKind.ComputedPropertyName) {
+ const t = checkComputedPropertyName(memberDecl.name);
+ if (t.flags & TypeFlags.Literal) {
+ literalName = escapeLeadingUnderscores("" + (t as LiteralType).value);
+ }
+ }
type = checkPropertyAssignment(memberDecl, checkMode);
}
else if (memberDecl.kind === SyntaxKind.MethodDeclaration) {
@@ -13581,7 +13657,7 @@ namespace ts {
}
typeFlags |= type.flags;
- const prop = createSymbol(SymbolFlags.Property | member.flags, member.escapedName);
+ const prop = createSymbol(SymbolFlags.Property | member.flags, literalName || member.escapedName);
if (inDestructuringPattern) {
// If object literal is an assignment pattern and if the assignment pattern specifies a default value
// for the property, make the property optional.
@@ -13591,7 +13667,7 @@ namespace ts {
if (isOptional) {
prop.flags |= SymbolFlags.Optional;
}
- if (hasDynamicName(memberDecl)) {
+ if (!literalName && hasDynamicName(memberDecl)) {
patternWithComputedProperties = true;
}
}
@@ -13649,7 +13725,7 @@ namespace ts {
checkNodeDeferred(memberDecl);
}
- if (hasDynamicName(memberDecl)) {
+ if (!literalName && hasDynamicName(memberDecl)) {
if (isNumericName(memberDecl.name)) {
hasComputedNumberProperty = true;
}
@@ -13715,7 +13791,8 @@ namespace ts {
}
function isValidSpreadType(type: Type): boolean {
- return !!(type.flags & (TypeFlags.Any | TypeFlags.Null | TypeFlags.Undefined | TypeFlags.NonPrimitive) ||
+ return !!(type.flags & (TypeFlags.Any | TypeFlags.NonPrimitive) ||
+ getFalsyFlags(type) & TypeFlags.DefinitelyFalsy && isValidSpreadType(removeDefinitelyFalsyTypes(type)) ||
type.flags & TypeFlags.Object && !isGenericMappedType(type) ||
type.flags & TypeFlags.UnionOrIntersection && !forEach((type).types, t => !isValidSpreadType(t)));
}
@@ -13980,8 +14057,9 @@ namespace ts {
const instantiatedSignatures = [];
for (const signature of signatures) {
if (signature.typeParameters) {
- const typeArguments = fillMissingTypeArguments(/*typeArguments*/ undefined, signature.typeParameters, /*minTypeArgumentCount*/ 0);
- instantiatedSignatures.push(getSignatureInstantiation(signature, typeArguments));
+ const isJavascript = isInJavaScriptFile(node);
+ const typeArguments = fillMissingTypeArguments(/*typeArguments*/ undefined, signature.typeParameters, /*minTypeArgumentCount*/ 0, isJavascript);
+ instantiatedSignatures.push(getSignatureInstantiation(signature, typeArguments, isJavascript));
}
else {
instantiatedSignatures.push(signature);
@@ -14409,7 +14487,7 @@ namespace ts {
// And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error.
const reactRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
const reactNamespace = getJsxNamespace();
- const reactSym = resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace);
+ const reactSym = resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace, /*isUse*/ true);
if (reactSym) {
// Mark local symbol as referenced here because it might not have been marked
// if jsx emit was not react as there wont be error being emitted
@@ -14685,7 +14763,7 @@ namespace ts {
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
- markPropertyAsReferenced(prop);
+ markPropertyAsReferenced(prop, node);
getNodeLinks(node).resolvedSymbol = prop;
@@ -14719,7 +14797,7 @@ namespace ts {
return;
}
- if (findAncestor(node, node => node.kind === SyntaxKind.PropertyDeclaration ? true : isExpression(node) ? false : "quit") &&
+ if (isInPropertyInitializer(node) &&
!isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)
&& !isPropertyDeclaredInAncestorClass(prop)) {
error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, unescapeLeadingUnderscores(right.escapedText));
@@ -14732,6 +14810,20 @@ namespace ts {
}
}
+ function isInPropertyInitializer(node: Node): boolean {
+ return !!findAncestor(node, node => {
+ switch (node.kind) {
+ case SyntaxKind.PropertyDeclaration:
+ return true;
+ case SyntaxKind.PropertyAssignment:
+ // We might be in `a = { b: this.b }`, so keep looking. See `tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts`.
+ return false;
+ default:
+ return isPartOfExpression(node) ? false : "quit";
+ }
+ });
+ }
+
/**
* It's possible that "prop.valueDeclaration" is a local declaration, but the property was also declared in a superclass.
* In that case we won't consider it used before its declaration, because it gets its value from the superclass' declaration.
@@ -14785,7 +14877,7 @@ namespace ts {
}
function getSuggestionForNonexistentSymbol(location: Node, name: __String, meaning: SymbolFlags): __String {
- const result = resolveNameHelper(location, name, meaning, /*nameNotFoundMessage*/ undefined, name, (symbols, name, meaning) => {
+ const result = resolveNameHelper(location, name, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ false, (symbols, name, meaning) => {
const symbol = getSymbol(symbols, name, meaning);
if (symbol) {
// Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function
@@ -14865,11 +14957,12 @@ namespace ts {
return bestCandidate;
}
- function markPropertyAsReferenced(prop: Symbol) {
+ function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined) {
if (prop &&
noUnusedIdentifiers &&
(prop.flags & SymbolFlags.ClassMember) &&
- prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Private)) {
+ prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Private)
+ && !(nodeForCheckWriteOnly && isWriteOnlyAccess(nodeForCheckWriteOnly))) {
if (getCheckFlags(prop) & CheckFlags.Instantiated) {
getSymbolLinks(prop).target.isReferenced = true;
}
@@ -15134,7 +15227,6 @@ namespace ts {
let argCount: number; // Apparent number of arguments we will have in this call
let typeArguments: NodeArray; // Type arguments (undefined if none)
let callIsIncomplete: boolean; // In incomplete call we want to be lenient when we have too few arguments
- let isDecorator: boolean;
let spreadArgIndex = -1;
if (isJsxOpeningLikeElement(node)) {
@@ -15168,7 +15260,6 @@ namespace ts {
}
}
else if (node.kind === SyntaxKind.Decorator) {
- isDecorator = true;
typeArguments = undefined;
argCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
}
@@ -15238,7 +15329,7 @@ namespace ts {
if (!contextualMapper) {
inferTypes(context.inferences, getReturnTypeOfSignature(contextualSignature), getReturnTypeOfSignature(signature), InferencePriority.ReturnType);
}
- return getSignatureInstantiation(signature, getInferredTypes(context));
+ return getSignatureInstantiation(signature, getInferredTypes(context), isInJavaScriptFile(contextualSignature.declaration));
}
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray, excludeArgument: boolean[], context: InferenceContext): Type[] {
@@ -15273,7 +15364,7 @@ namespace ts {
// Above, the type of the 'value' parameter is inferred to be 'A'.
const contextualSignature = getSingleCallSignature(instantiatedType);
const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ?
- getOrCreateTypeFromSignature(getSignatureInstantiation(contextualSignature, contextualSignature.typeParameters)) :
+ getOrCreateTypeFromSignature(getSignatureInstantiation(contextualSignature, contextualSignature.typeParameters, isInJavaScriptFile(node))) :
instantiatedType;
const inferenceTargetType = getReturnTypeOfSignature(signature);
// Inferences made from return types have lower priority than all other inferences.
@@ -15989,8 +16080,9 @@ namespace ts {
candidate = originalCandidate;
if (candidate.typeParameters) {
let typeArgumentTypes: Type[];
+ const isJavascript = isInJavaScriptFile(candidate.declaration);
if (typeArguments) {
- typeArgumentTypes = fillMissingTypeArguments(map(typeArguments, getTypeFromTypeNode), candidate.typeParameters, getMinTypeArgumentCount(candidate.typeParameters));
+ typeArgumentTypes = fillMissingTypeArguments(map(typeArguments, getTypeFromTypeNode), candidate.typeParameters, getMinTypeArgumentCount(candidate.typeParameters), isJavascript);
if (!checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false)) {
candidateForTypeArgumentError = originalCandidate;
break;
@@ -15999,7 +16091,7 @@ namespace ts {
else {
typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
}
- candidate = getSignatureInstantiation(candidate, typeArgumentTypes);
+ candidate = getSignatureInstantiation(candidate, typeArgumentTypes, isJavascript);
}
if (!checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
candidateForArgumentError = candidate;
@@ -16147,16 +16239,6 @@ namespace ts {
return resolveErrorCall(node);
}
- // If the expression is a class of abstract type, then it cannot be instantiated.
- // Note, only class declarations can be declared abstract.
- // In the case of a merged class-module or class-interface declaration,
- // only the class declaration node will have the Abstract flag set.
- const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol);
- if (valueDecl && hasModifier(valueDecl, ModifierFlags.Abstract)) {
- error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(getNameOfDeclaration(valueDecl)));
- return resolveErrorCall(node);
- }
-
// TS 1.0 spec: 4.11
// If expressionType is of type Any, Args can be any argument
// list and the result of the operation is of type Any.
@@ -16176,6 +16258,16 @@ namespace ts {
if (!isConstructorAccessible(node, constructSignatures[0])) {
return resolveErrorCall(node);
}
+ // If the expression is a class of abstract type, then it cannot be instantiated.
+ // Note, only class declarations can be declared abstract.
+ // In the case of a merged class-module or class-interface declaration,
+ // only the class declaration node will have the Abstract flag set.
+ const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol);
+ if (valueDecl && hasModifier(valueDecl, ModifierFlags.Abstract)) {
+ error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(getNameOfDeclaration(valueDecl)));
+ return resolveErrorCall(node);
+ }
+
return resolveCall(node, constructSignatures, candidatesOutArray);
}
@@ -16376,7 +16468,7 @@ namespace ts {
// This code-path is called by language service
return resolveStatelessJsxOpeningLikeElement(node, checkExpression((node).tagName), candidatesOutArray);
}
- Debug.fail("Branch in 'resolveSignature' should be unreachable.");
+ Debug.assertNever(node, "Branch in 'resolveSignature' should be unreachable.");
}
/**
@@ -16563,7 +16655,7 @@ namespace ts {
}
// Make sure require is not a local function
if (!isIdentifier(node.expression)) throw Debug.fail();
- const resolvedRequire = resolveName(node.expression, node.expression.escapedText, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
+ const resolvedRequire = resolveName(node.expression, node.expression.escapedText, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true);
if (!resolvedRequire) {
// project does not contain symbol named 'require' - assume commonjs require
return true;
@@ -16687,8 +16779,9 @@ namespace ts {
}
}
if (signature.hasRestParameter && isRestParameterIndex(context, signature.parameters.length - 1)) {
+ // parameter might be a transient symbol generated by use of `arguments` in the function body.
const parameter = lastOrUndefined(signature.parameters);
- if (!getEffectiveTypeAnnotationNode(parameter.valueDeclaration)) {
+ if (isTransientSymbol(parameter) || !getEffectiveTypeAnnotationNode(parameter.valueDeclaration)) {
const contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters));
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType);
}
@@ -18054,7 +18147,7 @@ namespace ts {
function checkParenthesizedExpression(node: ParenthesizedExpression, checkMode?: CheckMode): Type {
if (isInJavaScriptFile(node) && node.jsDoc) {
- const typecasts = flatMap(node.jsDoc, doc => filter(doc.tags, tag => tag.kind === SyntaxKind.JSDocTypeTag));
+ const typecasts = flatMap(node.jsDoc, doc => filter(doc.tags, tag => tag.kind === SyntaxKind.JSDocTypeTag && !!(tag as JSDocTypeTag).typeExpression && !!(tag as JSDocTypeTag).typeExpression.type));
if (typecasts && typecasts.length) {
// We should have already issued an error if there were multiple type jsdocs
const cast = typecasts[0] as JSDocTypeTag;
@@ -18765,7 +18858,7 @@ namespace ts {
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (constraint) {
if (!typeArguments) {
- typeArguments = fillMissingTypeArguments(map(typeArgumentNodes, getTypeFromTypeNode), typeParameters, minTypeArgumentCount);
+ typeArguments = fillMissingTypeArguments(map(typeArgumentNodes, getTypeFromTypeNode), typeParameters, minTypeArgumentCount, isInJavaScriptFile(typeArgumentNodes[i]));
mapper = createTypeMapper(typeParameters, typeArguments);
}
const typeArgument = typeArguments[i];
@@ -19190,6 +19283,8 @@ namespace ts {
switch (d.kind) {
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
+ // A jsdoc typedef is, by definition, a type alias
+ case SyntaxKind.JSDocTypedefTag:
return DeclarationSpaces.ExportType;
case SyntaxKind.ModuleDeclaration:
return isAmbientModule(d) || getModuleInstanceState(d) !== ModuleInstanceState.NonInstantiated
@@ -19560,8 +19655,11 @@ namespace ts {
}
function markEntityNameOrEntityExpressionAsReference(typeName: EntityNameOrEntityNameExpression) {
- const rootName = typeName && getFirstIdentifier(typeName);
- const rootSymbol = rootName && resolveName(rootName, rootName.escapedText, (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
+ if (!typeName) return;
+
+ const rootName = getFirstIdentifier(typeName);
+ const meaning = (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias;
+ const rootSymbol = resolveName(rootName, rootName.escapedText, meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isRefernce*/ true);
if (rootSymbol
&& rootSymbol.flags & SymbolFlags.Alias
&& symbolIsValue(rootSymbol)
@@ -19855,11 +19953,11 @@ namespace ts {
!isParameterPropertyDeclaration(parameter) &&
!parameterIsThisKeyword(parameter) &&
!parameterNameStartsWithUnderscore(name)) {
- error(name, Diagnostics._0_is_declared_but_never_used, unescapeLeadingUnderscores(local.escapedName));
+ error(name, Diagnostics._0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(local.escapedName));
}
}
else if (compilerOptions.noUnusedLocals) {
- forEach(local.declarations, d => errorUnusedLocal(getNameOfDeclaration(d) || d, unescapeLeadingUnderscores(local.escapedName)));
+ forEach(local.declarations, d => errorUnusedLocal(d, unescapeLeadingUnderscores(local.escapedName)));
}
}
});
@@ -19874,16 +19972,18 @@ namespace ts {
return false;
}
- function errorUnusedLocal(node: Node, name: string) {
+ function errorUnusedLocal(declaration: Declaration, name: string) {
+ const node = getNameOfDeclaration(declaration) || declaration;
if (isIdentifierThatStartsWithUnderScore(node)) {
const declaration = getRootDeclaration(node.parent);
- if (declaration.kind === SyntaxKind.VariableDeclaration && isForInOrOfStatement(declaration.parent.parent)) {
+ if ((declaration.kind === SyntaxKind.VariableDeclaration && isForInOrOfStatement(declaration.parent.parent)) ||
+ declaration.kind === SyntaxKind.TypeParameter) {
return;
}
}
if (!isRemovedPropertyFromObjectSpread(node.kind === SyntaxKind.Identifier ? node.parent : node)) {
- error(node, Diagnostics._0_is_declared_but_never_used, name);
+ error(node, Diagnostics._0_is_declared_but_its_value_is_never_read, name);
}
}
@@ -19901,13 +20001,13 @@ namespace ts {
for (const member of node.members) {
if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.PropertyDeclaration) {
if (!member.symbol.isReferenced && hasModifier(member, ModifierFlags.Private)) {
- error(member.name, Diagnostics._0_is_declared_but_never_used, unescapeLeadingUnderscores(member.symbol.escapedName));
+ error(member.name, Diagnostics._0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(member.symbol.escapedName));
}
}
else if (member.kind === SyntaxKind.Constructor) {
for (const parameter of (member).parameters) {
if (!parameter.symbol.isReferenced && hasModifier(parameter, ModifierFlags.Private)) {
- error(parameter.name, Diagnostics.Property_0_is_declared_but_never_used, unescapeLeadingUnderscores(parameter.symbol.escapedName));
+ error(parameter.name, Diagnostics.Property_0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(parameter.symbol.escapedName));
}
}
}
@@ -19927,8 +20027,8 @@ namespace ts {
return;
}
for (const typeParameter of node.typeParameters) {
- if (!getMergedSymbol(typeParameter.symbol).isReferenced) {
- error(typeParameter.name, Diagnostics._0_is_declared_but_never_used, unescapeLeadingUnderscores(typeParameter.symbol.escapedName));
+ if (!getMergedSymbol(typeParameter.symbol).isReferenced && !isIdentifierThatStartsWithUnderScore(typeParameter.name)) {
+ error(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(typeParameter.symbol.escapedName));
}
}
}
@@ -19941,7 +20041,7 @@ namespace ts {
if (!local.isReferenced && !local.exportSymbol) {
for (const declaration of local.declarations) {
if (!isAmbientModule(declaration)) {
- errorUnusedLocal(getNameOfDeclaration(declaration), unescapeLeadingUnderscores(local.escapedName));
+ errorUnusedLocal(declaration, unescapeLeadingUnderscores(local.escapedName));
}
}
}
@@ -19954,7 +20054,14 @@ namespace ts {
if (node.kind === SyntaxKind.Block) {
checkGrammarStatementInAmbientContext(node);
}
- forEach(node.statements, checkSourceElement);
+ if (isFunctionOrModuleBlock(node)) {
+ const saveFlowAnalysisDisabled = flowAnalysisDisabled;
+ forEach(node.statements, checkSourceElement);
+ flowAnalysisDisabled = saveFlowAnalysisDisabled;
+ }
+ else {
+ forEach(node.statements, checkSourceElement);
+ }
if (node.locals) {
registerForUnusedIdentifiersCheck(node);
}
@@ -20152,7 +20259,7 @@ namespace ts {
const symbol = getSymbolOfNode(node);
if (symbol.flags & SymbolFlags.FunctionScopedVariable) {
if (!isIdentifier(node.name)) throw Debug.fail();
- const localDeclarationSymbol = resolveName(node, node.name.escapedText, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
+ const localDeclarationSymbol = resolveName(node, node.name.escapedText, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false);
if (localDeclarationSymbol &&
localDeclarationSymbol !== symbol &&
localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
@@ -20207,7 +20314,7 @@ namespace ts {
else if (n.kind === SyntaxKind.Identifier) {
// check FunctionLikeDeclaration.locals (stores parameters\function local variable)
// if it contains entry with a specified name
- const symbol = resolveName(n, (n).escapedText, SymbolFlags.Value | SymbolFlags.Alias, /*nameNotFoundMessage*/undefined, /*nameArg*/undefined);
+ const symbol = resolveName(n, (n).escapedText, SymbolFlags.Value | SymbolFlags.Alias, /*nameNotFoundMessage*/undefined, /*nameArg*/undefined, /*isUse*/ false);
if (!symbol || symbol === unknownSymbol || !symbol.valueDeclaration) {
return;
}
@@ -20291,7 +20398,7 @@ namespace ts {
const parentType = getTypeForBindingElementParent(parent);
const name = node.propertyName || node.name;
const property = getPropertyOfType(parentType, getTextOfPropertyName(name));
- markPropertyAsReferenced(property);
+ markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined); // A destructuring is never a write-only reference.
if (parent.initializer && property) {
checkPropertyAccessibility(parent, parent.initializer, parentType, property);
}
@@ -21548,7 +21655,7 @@ namespace ts {
return true;
}
- type InheritanceInfoMap = { prop: Symbol; containingType: Type };
+ interface InheritanceInfoMap { prop: Symbol; containingType: Type; }
const seen = createUnderscoreEscapedMap();
forEach(resolveDeclaredMembers(type).declaredProperties, p => { seen.set(p.escapedName, { prop: p, containingType: type }); });
let ok = true;
@@ -22168,9 +22275,9 @@ namespace ts {
}
}
else {
- if (modulekind === ModuleKind.ES2015 && !isInAmbientContext(node)) {
+ if (modulekind >= ModuleKind.ES2015 && !isInAmbientContext(node)) {
// Import equals declaration is deprecated in es6 or above
- grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_2015_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead);
+ grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead);
}
}
}
@@ -22206,7 +22313,7 @@ namespace ts {
error(node.moduleSpecifier, Diagnostics.Module_0_uses_export_and_cannot_be_used_with_export_Asterisk, symbolToString(moduleSymbol));
}
- if (modulekind !== ModuleKind.System && modulekind !== ModuleKind.ES2015) {
+ if (modulekind !== ModuleKind.System && modulekind !== ModuleKind.ES2015 && modulekind !== ModuleKind.ESNext) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.ExportStar);
}
}
@@ -22227,7 +22334,7 @@ namespace ts {
const exportedName = node.propertyName || node.name;
// find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases)
const symbol = resolveName(exportedName, exportedName.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias,
- /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
+ /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true);
if (symbol && (symbol === undefinedSymbol || isGlobalSourceFile(getDeclarationContainer(symbol.declarations[0])))) {
error(exportedName, Diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, unescapeLeadingUnderscores(exportedName.escapedText));
}
@@ -22267,10 +22374,14 @@ namespace ts {
checkExternalModuleExports(container);
+ if (isInAmbientContext(node) && !isEntityNameExpression(node.expression)) {
+ grammarErrorOnNode(node.expression, Diagnostics.The_expression_of_an_export_assignment_must_be_an_identifier_or_qualified_name_in_an_ambient_context);
+ }
+
if (node.isExportEquals && !isInAmbientContext(node)) {
- if (modulekind === ModuleKind.ES2015) {
+ if (modulekind >= ModuleKind.ES2015) {
// export assignment is not supported in es6 modules
- grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_2015_modules_Consider_using_export_default_or_another_module_format_instead);
+ grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_export_default_or_another_module_format_instead);
}
else if (modulekind === ModuleKind.System) {
// system modules does not support export assignment
@@ -22544,6 +22655,7 @@ namespace ts {
deferredNodes = [];
deferredUnusedIdentifierNodes = produceDiagnostics && noUnusedIdentifiers ? [] : undefined;
+ flowAnalysisDisabled = false;
forEach(node.statements, checkSourceElement);
@@ -22982,14 +23094,16 @@ namespace ts {
return sig.thisParameter;
}
}
+ if (isInExpressionContext(node)) {
+ return checkExpression(node as Expression).symbol;
+ }
// falls through
- case SyntaxKind.SuperKeyword:
- const type = isPartOfExpression(node) ? getTypeOfExpression(node) : getTypeFromTypeNode(node);
- return type.symbol;
-
case SyntaxKind.ThisType:
- return getTypeFromTypeNode(node).symbol;
+ return getTypeFromThisTypeNode(node as ThisExpression | ThisTypeNode).symbol;
+
+ case SyntaxKind.SuperKeyword:
+ return checkExpression(node as Expression).symbol;
case SyntaxKind.ConstructorKeyword:
// constructor keyword for an overload, should take us to the definition if it exist
@@ -23019,6 +23133,9 @@ namespace ts {
: undefined;
return objectType && getPropertyOfType(objectType, escapeLeadingUnderscores((node as StringLiteral | NumericLiteral).text));
+ case SyntaxKind.DefaultKeyword:
+ return getSymbolOfNode(node.parent);
+
default:
return undefined;
}
@@ -23328,7 +23445,7 @@ namespace ts {
const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration);
if (isStatementWithLocals(container)) {
const nodeLinks = getNodeLinks(symbol.valueDeclaration);
- if (!!resolveName(container.parent, symbol.escapedName, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)) {
+ if (resolveName(container.parent, symbol.escapedName, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false)) {
// redeclaration - always should be renamed
links.isDeclarationWithCollidingName = true;
}
@@ -23638,7 +23755,7 @@ namespace ts {
}
}
- return resolveName(location, reference.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
+ return resolveName(location, reference.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true);
}
function getReferencedValueDeclaration(reference: Identifier): Declaration {
@@ -23962,7 +24079,7 @@ namespace ts {
return quickResult;
}
- let lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node, lastAsync: Node, lastReadonly: Node;
+ let lastStatic: Node, lastDeclare: Node, lastAsync: Node, lastReadonly: Node;
let flags = ModifierFlags.None;
for (const modifier of node.modifiers) {
if (modifier.kind !== SyntaxKind.ReadonlyKeyword) {
@@ -23984,13 +24101,6 @@ namespace ts {
case SyntaxKind.PrivateKeyword:
const text = visibilityToString(modifierToFlag(modifier.kind));
- if (modifier.kind === SyntaxKind.ProtectedKeyword) {
- lastProtected = modifier;
- }
- else if (modifier.kind === SyntaxKind.PrivateKeyword) {
- lastPrivate = modifier;
- }
-
if (flags & ModifierFlags.AccessibilityModifier) {
return grammarErrorOnNode(modifier, Diagnostics.Accessibility_modifier_already_seen);
}
@@ -24552,7 +24662,7 @@ namespace ts {
currentKind = SetAccessor;
}
else {
- Debug.fail("Unexpected syntax kind:" + (prop).kind);
+ Debug.assertNever(prop, "Unexpected syntax kind:" + (prop).kind);
}
const effectiveName = getPropertyNameForPropertyNameNode(name);
@@ -24868,7 +24978,7 @@ namespace ts {
}
}
- if (compilerOptions.module !== ModuleKind.ES2015 && compilerOptions.module !== ModuleKind.System && !compilerOptions.noEmit &&
+ if (compilerOptions.module !== ModuleKind.ES2015 && compilerOptions.module !== ModuleKind.ESNext && compilerOptions.module !== ModuleKind.System && !compilerOptions.noEmit &&
!isInAmbientContext(node.parent.parent) && hasModifier(node.parent.parent, ModifierFlags.Export)) {
checkESModuleMarker(node.name);
}
diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts
index c92d147f9a9..54e5ee1d01d 100644
--- a/src/compiler/commandLineParser.ts
+++ b/src/compiler/commandLineParser.ts
@@ -1057,7 +1057,7 @@ namespace ts {
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.name, extraKeyDiagnosticMessage, keyText));
}
const value = convertPropertyValueToJson(element.initializer, option);
- if (typeof keyText !== "undefined" && typeof value !== "undefined") {
+ if (typeof keyText !== "undefined") {
result[keyText] = value;
// Notify key value set, if user asked for it
if (jsonConversionNotifier &&
@@ -1104,7 +1104,7 @@ namespace ts {
return false;
case SyntaxKind.NullKeyword:
- reportInvalidOptionValue(!!option);
+ reportInvalidOptionValue(option && option.name === "extends"); // "extends" is the only option we don't allow null/undefined for
return null; // tslint:disable-line:no-null-keyword
case SyntaxKind.StringLiteral:
@@ -1189,6 +1189,7 @@ namespace ts {
function isCompilerOptionsValue(option: CommandLineOption, value: any): value is CompilerOptionsValue {
if (option) {
+ if (isNullOrUndefined(value)) return true; // All options are undefinable/nullable
if (option.type === "list") {
return isArray(value);
}
@@ -1379,6 +1380,17 @@ namespace ts {
}
}
+ function isNullOrUndefined(x: any): x is null | undefined {
+ // tslint:disable-next-line:no-null-keyword
+ return x === undefined || x === null;
+ }
+
+ function directoryOfCombinedPath(fileName: string, basePath: string) {
+ // Use the `identity` function to avoid canonicalizing the path, as it must remain noncanonical
+ // until consistient casing errors are reported
+ return getDirectoryPath(toPath(fileName, basePath, identity));
+ }
+
/**
* Parse the contents of a config file from json or json source file (tsconfig.json).
* @param json The contents of the config file to parse
@@ -1419,7 +1431,7 @@ namespace ts {
function getFileNames(): ExpandResult {
let fileNames: ReadonlyArray;
- if (hasProperty(raw, "files")) {
+ if (hasProperty(raw, "files") && !isNullOrUndefined(raw["files"])) {
if (isArray(raw["files"])) {
fileNames = >raw["files"];
if (fileNames.length === 0) {
@@ -1432,7 +1444,7 @@ namespace ts {
}
let includeSpecs: ReadonlyArray;
- if (hasProperty(raw, "include")) {
+ if (hasProperty(raw, "include") && !isNullOrUndefined(raw["include"])) {
if (isArray(raw["include"])) {
includeSpecs = >raw["include"];
}
@@ -1442,7 +1454,7 @@ namespace ts {
}
let excludeSpecs: ReadonlyArray;
- if (hasProperty(raw, "exclude")) {
+ if (hasProperty(raw, "exclude") && !isNullOrUndefined(raw["exclude"])) {
if (isArray(raw["exclude"])) {
excludeSpecs = >raw["exclude"];
}
@@ -1461,7 +1473,7 @@ namespace ts {
includeSpecs = ["**/*"];
}
- const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors, extraFileExtensions, sourceFile);
+ const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath, options, host, errors, extraFileExtensions, sourceFile);
if (result.fileNames.length === 0 && !hasProperty(raw, "files") && resolutionStack.length === 0) {
errors.push(
@@ -1552,7 +1564,7 @@ namespace ts {
host: ParseConfigHost,
basePath: string,
getCanonicalFileName: (fileName: string) => string,
- configFileName: string,
+ configFileName: string | undefined,
errors: Push
): ParsedTsconfig {
if (hasProperty(json, "excludes")) {
@@ -1571,7 +1583,8 @@ namespace ts {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string"));
}
else {
- extendedConfigPath = getExtendsConfigPath(json.extends, host, basePath, getCanonicalFileName, errors, createCompilerDiagnostic);
+ const newBase = configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath;
+ extendedConfigPath = getExtendsConfigPath(json.extends, host, newBase, getCanonicalFileName, errors, createCompilerDiagnostic);
}
}
return { raw: json, options, typeAcquisition, extendedConfigPath };
@@ -1582,7 +1595,7 @@ namespace ts {
host: ParseConfigHost,
basePath: string,
getCanonicalFileName: (fileName: string) => string,
- configFileName: string,
+ configFileName: string | undefined,
errors: Push
): ParsedTsconfig {
const options = getDefaultCompilerOptions(configFileName);
@@ -1603,10 +1616,11 @@ namespace ts {
onSetValidOptionKeyValueInRoot(key: string, _keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression) {
switch (key) {
case "extends":
+ const newBase = configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath;
extendedConfigPath = getExtendsConfigPath(
value,
host,
- basePath,
+ newBase,
getCanonicalFileName,
errors,
(message, arg0) =>
@@ -1803,6 +1817,7 @@ namespace ts {
}
function normalizeOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue {
+ if (isNullOrUndefined(value)) return undefined;
if (option.type === "list") {
const listOption = option;
if (listOption.element.isFilePath || typeof listOption.element.type !== "string") {
@@ -1827,6 +1842,7 @@ namespace ts {
}
function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Push) {
+ if (isNullOrUndefined(value)) return undefined;
const key = value.toLowerCase();
const val = opt.type.get(key);
if (val !== undefined) {
@@ -1977,7 +1993,7 @@ namespace ts {
// remove a literal file.
if (fileNames) {
for (const fileName of fileNames) {
- const file = combinePaths(basePath, fileName);
+ const file = getNormalizedAbsolutePath(fileName, basePath);
literalFileMap.set(keyMapper(file), file);
}
}
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index 20f757c3df8..84321b4b624 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -9,6 +9,15 @@ namespace ts {
export const version = `${versionMajorMinor}.0`;
}
+namespace ts {
+ export function isExternalModuleNameRelative(moduleName: string): boolean {
+ // TypeScript 1.0 spec (April 2014): 11.2.1
+ // An external module name is "relative" if the first term is "." or "..".
+ // Update: We also consider a path like `C:\foo.ts` "relative" because we do not search for it in `node_modules` or treat it as an ambient module.
+ return pathIsRelative(moduleName) || isRootedDiskPath(moduleName);
+ }
+}
+
/* @internal */
namespace ts {
@@ -40,7 +49,6 @@ namespace ts {
return new MapCtr() as UnderscoreEscapedMap;
}
- /* @internal */
export function createSymbolTable(symbols?: ReadonlyArray): SymbolTable {
const result = createMap() as SymbolTable;
if (symbols) {
@@ -1220,6 +1228,9 @@ namespace ts {
/** Does nothing. */
export function noop(): void {}
+ /** Returns its argument. */
+ export function identity(x: T) { return x; }
+
/** Throws an error because a function is not implemented. */
export function notImplemented(): never {
throw new Error("Not implemented");
@@ -1283,7 +1294,7 @@ namespace ts {
args[i] = arguments[i];
}
- return t => reduceLeft<(t: T) => T, T>(args, (u, f) => f(u), t);
+ return t => reduceLeft(args, (u, f) => f(u), t);
}
else if (d) {
return t => d(c(b(a(t))));
@@ -1604,18 +1615,10 @@ namespace ts {
return path && !isRootedDiskPath(path) && path.indexOf("://") !== -1;
}
- /* @internal */
export function pathIsRelative(path: string): boolean {
return /^\.\.?($|[\\/])/.test(path);
}
- export function isExternalModuleNameRelative(moduleName: string): boolean {
- // TypeScript 1.0 spec (April 2014): 11.2.1
- // An external module name is "relative" if the first term is "." or "..".
- // Update: We also consider a path like `C:\foo.ts` "relative" because we do not search for it in `node_modules` or treat it as an ambient module.
- return pathIsRelative(moduleName) || isRootedDiskPath(moduleName);
- }
-
/** @deprecated Use `!isExternalModuleNameRelative(moduleName)` instead. */
export function moduleHasNonRelativeName(moduleName: string): boolean {
return !isExternalModuleNameRelative(moduleName);
@@ -1639,7 +1642,6 @@ namespace ts {
return moduleResolution;
}
- /* @internal */
export function hasZeroOrOneAsteriskCharacter(str: string): boolean {
let seenAsterisk = false;
for (let i = 0; i < str.length; i++) {
@@ -1657,7 +1659,7 @@ namespace ts {
}
export function isRootedDiskPath(path: string) {
- return getRootLength(path) !== 0;
+ return path && getRootLength(path) !== 0;
}
export function convertToRelativePath(absoluteOrRelativePath: string, basePath: string, getCanonicalFileName: (path: string) => string): string {
@@ -1864,17 +1866,14 @@ namespace ts {
return true;
}
- /* @internal */
export function startsWith(str: string, prefix: string): boolean {
return str.lastIndexOf(prefix, 0) === 0;
}
- /* @internal */
export function removePrefix(str: string, prefix: string): string {
return startsWith(str, prefix) ? str.substr(prefix.length) : str;
}
- /* @internal */
export function endsWith(str: string, suffix: string): boolean {
const expectedPos = str.length - suffix.length;
return expectedPos >= 0 && str.indexOf(suffix, expectedPos) === expectedPos;
@@ -1888,7 +1887,6 @@ namespace ts {
return path.length > extension.length && endsWith(path, extension);
}
- /* @internal */
export function fileExtensionIsOneOf(path: string, extensions: ReadonlyArray): boolean {
for (const extension of extensions) {
if (fileExtensionIs(path, extension)) {
@@ -1905,7 +1903,6 @@ namespace ts {
const reservedCharacterPattern = /[^\w\s\/]/g;
const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question];
- /* @internal */
export const commonPackageFolders: ReadonlyArray = ["node_modules", "bower_components", "jspm_packages"];
const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`;
@@ -2447,7 +2444,7 @@ namespace ts {
}
}
- export function fail(message?: string, stackCrawlMark?: Function): void {
+ export function fail(message?: string, stackCrawlMark?: Function): never {
debugger;
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
if ((Error).captureStackTrace) {
@@ -2456,6 +2453,10 @@ namespace ts {
throw e;
}
+ export function assertNever(member: never, message?: string, stackCrawlMark?: Function): never {
+ return fail(message || `Illegal value: ${member}`, stackCrawlMark || assertNever);
+ }
+
export function getFunctionName(func: Function) {
if (typeof func !== "function") {
return "";
@@ -2523,7 +2524,6 @@ namespace ts {
* Return an exact match if possible, or a pattern match, or undefined.
* (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
*/
- /* @internal */
export function matchPatternOrExact(patternStrings: ReadonlyArray, candidate: string): string | Pattern | undefined {
const patterns: Pattern[] = [];
for (const patternString of patternStrings) {
@@ -2540,7 +2540,6 @@ namespace ts {
return findBestPatternMatch(patterns, _ => _, candidate);
}
- /* @internal */
export function patternText({prefix, suffix}: Pattern): string {
return `${prefix}*${suffix}`;
}
@@ -2549,14 +2548,12 @@ namespace ts {
* Given that candidate matches pattern, returns the text matching the '*'.
* E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar"
*/
- /* @internal */
export function matchedText(pattern: Pattern, candidate: string): string {
Debug.assert(isPatternMatch(pattern, candidate));
return candidate.substr(pattern.prefix.length, candidate.length - pattern.suffix.length);
}
/** Return the object corresponding to the best pattern to match `candidate`. */
- /* @internal */
export function findBestPatternMatch(values: ReadonlyArray, getPattern: (value: T) => Pattern, candidate: string): T | undefined {
let matchedValue: T | undefined = undefined;
// use length of prefix as betterness criteria
@@ -2579,7 +2576,6 @@ namespace ts {
endsWith(candidate, suffix);
}
- /* @internal */
export function tryParsePattern(pattern: string): Pattern | undefined {
// This should be verified outside of here and a proper error thrown.
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
@@ -2628,4 +2624,6 @@ namespace ts {
export function and(f: (arg: T) => boolean, g: (arg: T) => boolean) {
return (arg: T) => f(arg) && g(arg);
}
+
+ export function assertTypeIsNever(_: never): void {}
}
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 9a3492e5c37..662e87d3159 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -627,11 +627,11 @@
"category": "Error",
"code": 1200
},
- "Import assignment cannot be used when targeting ECMAScript 2015 modules. Consider using 'import * as ns from \"mod\"', 'import {a} from \"mod\"', 'import d from \"mod\"', or another module format instead.": {
+ "Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from \"mod\"', 'import {a} from \"mod\"', 'import d from \"mod\"', or another module format instead.": {
"category": "Error",
"code": 1202
},
- "Export assignment cannot be used when targeting ECMAScript 2015 modules. Consider using 'export default' or another module format instead.": {
+ "Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.": {
"category": "Error",
"code": 1203
},
@@ -1920,6 +1920,10 @@
"category": "Error",
"code": 2562
},
+ "The containing function or module body is too large for control flow analysis.": {
+ "category": "Error",
+ "code": 2563
+ },
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600
@@ -2208,6 +2212,10 @@
"category": "Error",
"code": 2713
},
+ "The expression of an export assignment must be an identifier or qualified name in an ambient context.": {
+ "category": "Error",
+ "code": 2714
+ },
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
@@ -2678,7 +2686,7 @@
"category": "Message",
"code": 6015
},
- "Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.": {
+ "Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.": {
"category": "Message",
"code": 6016
},
@@ -3098,7 +3106,7 @@
"category": "Message",
"code": 6132
},
- "'{0}' is declared but never used.": {
+ "'{0}' is declared but its value is never read.": {
"category": "Error",
"code": 6133
},
@@ -3118,7 +3126,7 @@
"category": "Error",
"code": 6137
},
- "Property '{0}' is declared but never used.": {
+ "Property '{0}' is declared but its value is never read.": {
"category": "Error",
"code": 6138
},
@@ -3138,10 +3146,6 @@
"category": "Error",
"code": 6142
},
- "Module '{0}' was resolved to '{1}', but '--allowJs' is not set.": {
- "category": "Error",
- "code": 6143
- },
"Module '{0}' was resolved as locally declared ambient module in file '{1}'.": {
"category": "Message",
"code": 6144
@@ -3696,7 +3700,7 @@
"code": 95003
},
- "Extract function into {0}": {
+ "Extract to {0}": {
"category": "Message",
"code": 95004
}
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index 5444c618353..502329bbd84 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -406,6 +406,14 @@ namespace ts {
setWriter(/*output*/ undefined);
}
+ // TODO: Should this just be `emit`?
+ // See https://github.com/Microsoft/TypeScript/pull/18284#discussion_r137611034
+ function emitIfPresent(node: Node | undefined) {
+ if (node) {
+ emit(node);
+ }
+ }
+
function emit(node: Node) {
pipelineEmitWithNotification(EmitHint.Unspecified, node);
}
@@ -451,6 +459,7 @@ namespace ts {
case EmitHint.SourceFile: return pipelineEmitSourceFile(node);
case EmitHint.IdentifierName: return pipelineEmitIdentifierName(node);
case EmitHint.Expression: return pipelineEmitExpression(node);
+ case EmitHint.MappedTypeParameter: return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration));
case EmitHint.Unspecified: return pipelineEmitUnspecified(node);
}
}
@@ -465,6 +474,12 @@ namespace ts {
emitIdentifier(node);
}
+ function emitMappedTypeParameter(node: TypeParameterDeclaration): void {
+ emit(node.name);
+ write(" in ");
+ emit(node.constraint);
+ }
+
function pipelineEmitUnspecified(node: Node): void {
const kind = node.kind;
@@ -898,9 +913,9 @@ namespace ts {
function emitParameter(node: ParameterDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
- writeIfPresent(node.dotDotDotToken, "...");
+ emitIfPresent(node.dotDotDotToken);
emit(node.name);
- writeIfPresent(node.questionToken, "?");
+ emitIfPresent(node.questionToken);
emitWithPrefix(": ", node.type);
emitExpressionWithPrefix(" = ", node.initializer);
}
@@ -918,7 +933,7 @@ namespace ts {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emit(node.name);
- writeIfPresent(node.questionToken, "?");
+ emitIfPresent(node.questionToken);
emitWithPrefix(": ", node.type);
write(";");
}
@@ -927,7 +942,7 @@ namespace ts {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emit(node.name);
- writeIfPresent(node.questionToken, "?");
+ emitIfPresent(node.questionToken);
emitWithPrefix(": ", node.type);
emitExpressionWithPrefix(" = ", node.initializer);
write(";");
@@ -937,7 +952,7 @@ namespace ts {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emit(node.name);
- writeIfPresent(node.questionToken, "?");
+ emitIfPresent(node.questionToken);
emitTypeParameters(node, node.typeParameters);
emitParameters(node, node.parameters);
emitWithPrefix(": ", node.type);
@@ -947,9 +962,9 @@ namespace ts {
function emitMethodDeclaration(node: MethodDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
- writeIfPresent(node.asteriskToken, "*");
+ emitIfPresent(node.asteriskToken);
emit(node.name);
- writeIfPresent(node.questionToken, "?");
+ emitIfPresent(node.questionToken);
emitSignatureAndBody(node, emitSignatureHead);
}
@@ -1035,10 +1050,8 @@ namespace ts {
function emitTypeLiteral(node: TypeLiteralNode) {
write("{");
- // If the literal is empty, do not add spaces between braces.
- if (node.members.length > 0) {
- emitList(node, node.members, getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers);
- }
+ const flags = getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers;
+ emitList(node, node.members, flags | ListFormat.NoSpaceIfEmpty);
write("}");
}
@@ -1094,13 +1107,16 @@ namespace ts {
writeLine();
increaseIndent();
}
- writeIfPresent(node.readonlyToken, "readonly ");
+ if (node.readonlyToken) {
+ emit(node.readonlyToken);
+ write(" ");
+ }
+
write("[");
- emit(node.typeParameter.name);
- write(" in ");
- emit(node.typeParameter.constraint);
+ pipelineEmitWithNotification(EmitHint.MappedTypeParameter, node.typeParameter);
write("]");
- writeIfPresent(node.questionToken, "?");
+
+ emitIfPresent(node.questionToken);
write(": ");
emit(node.type);
write(";");
@@ -1148,7 +1164,7 @@ namespace ts {
function emitBindingElement(node: BindingElement) {
emitWithSuffix(node.propertyName, ": ");
- writeIfPresent(node.dotDotDotToken, "...");
+ emitIfPresent(node.dotDotDotToken);
emit(node.name);
emitExpressionWithPrefix(" = ", node.initializer);
}
@@ -1159,33 +1175,22 @@ namespace ts {
function emitArrayLiteralExpression(node: ArrayLiteralExpression) {
const elements = node.elements;
- if (elements.length === 0) {
- write("[]");
- }
- else {
- const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
- emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine);
- }
+ const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
+ emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine);
}
function emitObjectLiteralExpression(node: ObjectLiteralExpression) {
- const properties = node.properties;
- if (properties.length === 0) {
- write("{}");
+ const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
+ if (indentedFlag) {
+ increaseIndent();
}
- else {
- const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
- if (indentedFlag) {
- increaseIndent();
- }
- const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
- const allowTrailingComma = currentSourceFile.languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None;
- emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine);
+ const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
+ const allowTrailingComma = currentSourceFile.languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None;
+ emitList(node, node.properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine);
- if (indentedFlag) {
- decreaseIndent();
- }
+ if (indentedFlag) {
+ decreaseIndent();
}
}
@@ -1286,7 +1291,8 @@ namespace ts {
emitTypeParameters(node, node.typeParameters);
emitParametersForArrow(node, node.parameters);
emitWithPrefix(": ", node.type);
- write(" =>");
+ write(" ");
+ emit(node.equalsGreaterThanToken);
}
function emitDeleteExpression(node: DeleteExpression) {
@@ -1364,13 +1370,13 @@ namespace ts {
emitExpression(node.condition);
increaseIndentIf(indentBeforeQuestion, " ");
- write("?");
+ emit(node.questionToken);
increaseIndentIf(indentAfterQuestion, " ");
emitExpression(node.whenTrue);
decreaseIndentIf(indentBeforeQuestion, indentAfterQuestion);
increaseIndentIf(indentBeforeColon, " ");
- write(":");
+ emit(node.colonToken);
increaseIndentIf(indentAfterColon, " ");
emitExpression(node.whenFalse);
decreaseIndentIf(indentBeforeColon, indentAfterColon);
@@ -1382,7 +1388,8 @@ namespace ts {
}
function emitYieldExpression(node: YieldExpression) {
- write(node.asteriskToken ? "yield*" : "yield");
+ write("yield");
+ emit(node.asteriskToken);
emitExpressionWithPrefix(" ", node.expression);
}
@@ -1433,29 +1440,18 @@ namespace ts {
//
function emitBlock(node: Block) {
- if (isSingleLineEmptyBlock(node)) {
- writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node);
- write(" ");
- writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node);
- }
- else {
- writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node);
- emitBlockStatements(node);
- // We have to call emitLeadingComments explicitly here because otherwise leading comments of the close brace token will not be emitted
- increaseIndent();
- emitLeadingCommentsOfPosition(node.statements.end);
- decreaseIndent();
- writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node);
- }
+ writeToken(SyntaxKind.OpenBraceToken, node.pos, /*contextNode*/ node);
+ emitBlockStatements(node, /*forceSingleLine*/ !node.multiLine && isEmptyBlock(node));
+ // We have to call emitLeadingComments explicitly here because otherwise leading comments of the close brace token will not be emitted
+ increaseIndent();
+ emitLeadingCommentsOfPosition(node.statements.end);
+ decreaseIndent();
+ writeToken(SyntaxKind.CloseBraceToken, node.statements.end, /*contextNode*/ node);
}
- function emitBlockStatements(node: BlockLike) {
- if (getEmitFlags(node) & EmitFlags.SingleLine) {
- emitList(node, node.statements, ListFormat.SingleLineBlockStatements);
- }
- else {
- emitList(node, node.statements, ListFormat.MultiLineBlockStatements);
- }
+ function emitBlockStatements(node: BlockLike, forceSingleLine: boolean) {
+ const format = forceSingleLine || getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineBlockStatements : ListFormat.MultiLineBlockStatements;
+ emitList(node, node.statements, format);
}
function emitVariableStatement(node: VariableStatement) {
@@ -1662,7 +1658,9 @@ namespace ts {
function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
- write(node.asteriskToken ? "function* " : "function ");
+ write("function");
+ emitIfPresent(node.asteriskToken);
+ write(" ");
emitIdentifierName(node.name);
emitSignatureAndBody(node, emitSignatureHead);
}
@@ -1880,16 +1878,11 @@ namespace ts {
}
function emitModuleBlock(node: ModuleBlock) {
- if (isEmptyBlock(node)) {
- write("{ }");
- }
- else {
- pushNameGenerationScope();
- write("{");
- emitBlockStatements(node);
- write("}");
- popNameGenerationScope();
- }
+ pushNameGenerationScope();
+ write("{");
+ emitBlockStatements(node, /*forceSingleLine*/ isEmptyBlock(node));
+ write("}");
+ popNameGenerationScope();
}
function emitCaseBlock(node: CaseBlock) {
@@ -2068,9 +2061,7 @@ namespace ts {
function emitJsxExpression(node: JsxExpression) {
if (node.expression) {
write("{");
- if (node.dotDotDotToken) {
- write("...");
- }
+ emitIfPresent(node.dotDotDotToken);
emitExpression(node.expression);
write("}");
}
@@ -2128,13 +2119,12 @@ namespace ts {
emitTrailingCommentsOfPosition(statements.pos);
}
+ let format = ListFormat.CaseOrDefaultClauseStatements;
if (emitAsSingleStatement) {
write(" ");
- emit(statements[0]);
- }
- else {
- emitList(parentNode, statements, ListFormat.CaseOrDefaultClauseStatements);
+ format &= ~(ListFormat.MultiLine | ListFormat.Indented);
}
+ emitList(parentNode, statements, format);
}
function emitHeritageClause(node: HeritageClause) {
@@ -2384,7 +2374,7 @@ namespace ts {
function emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray) {
if (canEmitSimpleArrowHead(parentNode, parameters)) {
- emit(parameters[0]);
+ emitList(parentNode, parameters, ListFormat.Parameters & ~ListFormat.Parenthesis);
}
else {
emitParameters(parentNode, parameters);
@@ -2409,8 +2399,14 @@ namespace ts {
return;
}
- const isEmpty = isUndefined || children.length === 0 || start >= children.length || count === 0;
+ const isEmpty = isUndefined || start >= children.length || count === 0;
if (isEmpty && format & ListFormat.OptionalIfEmpty) {
+ if (onBeforeEmitNodeArray) {
+ onBeforeEmitNodeArray(children);
+ }
+ if (onAfterEmitNodeArray) {
+ onAfterEmitNodeArray(children);
+ }
return;
}
@@ -2427,7 +2423,7 @@ namespace ts {
if (format & ListFormat.MultiLine) {
writeLine();
}
- else if (format & ListFormat.SpaceBetweenBraces) {
+ else if (format & ListFormat.SpaceBetweenBraces && !(format & ListFormat.NoSpaceIfEmpty)) {
write(" ");
}
}
@@ -2519,7 +2515,7 @@ namespace ts {
// 2
// /* end of element 2 */
// ];
- if (previousSibling && delimiter && previousSibling.end !== parentNode.end) {
+ if (previousSibling && delimiter && previousSibling.end !== parentNode.end && !(getEmitFlags(previousSibling) & EmitFlags.NoTrailingComments)) {
emitLeadingCommentsOfPosition(previousSibling.end);
}
@@ -2568,12 +2564,6 @@ namespace ts {
}
}
- function writeIfPresent(node: Node, text: string) {
- if (node) {
- write(text);
- }
- }
-
function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) {
return onEmitSourceMapOfToken
? onEmitSourceMapOfToken(contextNode, token, pos, writeTokenText)
@@ -2584,7 +2574,7 @@ namespace ts {
if (onBeforeEmitToken) {
onBeforeEmitToken(node);
}
- writeTokenText(node.kind);
+ write(tokenToString(node.kind));
if (onAfterEmitToken) {
onAfterEmitToken(node);
}
@@ -2756,11 +2746,6 @@ namespace ts {
&& !rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile);
}
- function isSingleLineEmptyBlock(block: Block) {
- return !block.multiLine
- && isEmptyBlock(block);
- }
-
function isEmptyBlock(block: BlockLike) {
return block.statements.length === 0
&& rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile);
@@ -3107,6 +3092,9 @@ namespace ts {
NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list.
NoInterveningComments = 1 << 17, // Do not emit comments between each node
+ NoSpaceIfEmpty = 1 << 18, // If the literal is empty, do not add spaces between braces.
+ SingleElement = 1 << 19,
+
// Precomputed Formats
Modifiers = SingleLine | SpaceBetweenSiblings | NoInterveningComments,
HeritageClauses = SingleLine | SpaceBetweenSiblings,
@@ -3118,7 +3106,7 @@ namespace ts {
IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine,
ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings,
ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings,
- ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces,
+ ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces | NoSpaceIfEmpty,
ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets,
CommaListElements = CommaDelimited | SpaceBetweenSiblings | SingleLine,
CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis,
diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts
index cbca0d3fdf4..0fb068ea3bf 100644
--- a/src/compiler/factory.ts
+++ b/src/compiler/factory.ts
@@ -281,7 +281,7 @@ namespace ts {
|| node.questionToken !== questionToken
|| node.type !== type
|| node.initializer !== initializer
- ? updateNode(createParameter(decorators, modifiers, dotDotDotToken, name, node.questionToken, type, initializer), node)
+ ? updateNode(createParameter(decorators, modifiers, dotDotDotToken, name, questionToken, type, initializer), node)
: node;
}
@@ -773,13 +773,13 @@ namespace ts {
: node;
}
- export function createLiteralTypeNode(literal: Expression) {
+ export function createLiteralTypeNode(literal: LiteralTypeNode["literal"]) {
const node = createSynthesizedNode(SyntaxKind.LiteralType) as LiteralTypeNode;
node.literal = literal;
return node;
}
- export function updateLiteralTypeNode(node: LiteralTypeNode, literal: Expression) {
+ export function updateLiteralTypeNode(node: LiteralTypeNode, literal: LiteralTypeNode["literal"]) {
return node.literal !== literal
? updateNode(createLiteralTypeNode(literal), node)
: node;
@@ -1016,19 +1016,49 @@ namespace ts {
return node;
}
+ /* @deprecated */ export function updateArrowFunction(
+ node: ArrowFunction,
+ modifiers: ReadonlyArray | undefined,
+ typeParameters: ReadonlyArray | undefined,
+ parameters: ReadonlyArray,
+ type: TypeNode | undefined,
+ body: ConciseBody): ArrowFunction;
export function updateArrowFunction(
node: ArrowFunction,
modifiers: ReadonlyArray | undefined,
typeParameters: ReadonlyArray | undefined,
parameters: ReadonlyArray,
type: TypeNode | undefined,
- body: ConciseBody) {
+ equalsGreaterThanToken: Token,
+ body: ConciseBody): ArrowFunction;
+ export function updateArrowFunction(
+ node: ArrowFunction,
+ modifiers: ReadonlyArray | undefined,
+ typeParameters: ReadonlyArray | undefined,
+ parameters: ReadonlyArray,
+ type: TypeNode | undefined,
+ equalsGreaterThanTokenOrBody: Token | ConciseBody,
+ bodyOrUndefined?: ConciseBody,
+ ): ArrowFunction {
+ let equalsGreaterThanToken: Token;
+ let body: ConciseBody;
+ if (bodyOrUndefined === undefined) {
+ equalsGreaterThanToken = node.equalsGreaterThanToken;
+ body = cast(equalsGreaterThanTokenOrBody, isConciseBody);
+ }
+ else {
+ equalsGreaterThanToken = cast(equalsGreaterThanTokenOrBody, (n): n is Token =>
+ n.kind === SyntaxKind.EqualsGreaterThanToken);
+ body = bodyOrUndefined;
+ }
+
return node.modifiers !== modifiers
|| node.typeParameters !== typeParameters
|| node.parameters !== parameters
|| node.type !== type
+ || node.equalsGreaterThanToken !== equalsGreaterThanToken
|| node.body !== body
- ? updateNode(createArrowFunction(modifiers, typeParameters, parameters, type, node.equalsGreaterThanToken, body), node)
+ ? updateNode(createArrowFunction(modifiers, typeParameters, parameters, type, equalsGreaterThanToken, body), node)
: node;
}
@@ -1135,11 +1165,31 @@ namespace ts {
return node;
}
- export function updateConditional(node: ConditionalExpression, condition: Expression, whenTrue: Expression, whenFalse: Expression) {
+ /* @deprecated */ export function updateConditional(
+ node: ConditionalExpression,
+ condition: Expression,
+ whenTrue: Expression,
+ whenFalse: Expression): ConditionalExpression;
+ export function updateConditional(
+ node: ConditionalExpression,
+ condition: Expression,
+ questionToken: Token,
+ whenTrue: Expression,
+ colonToken: Token,
+ whenFalse: Expression): ConditionalExpression;
+ export function updateConditional(node: ConditionalExpression, condition: Expression, ...args: any[]) {
+ if (args.length === 2) {
+ const [whenTrue, whenFalse] = args;
+ return updateConditional(node, condition, node.questionToken, whenTrue, node.colonToken, whenFalse);
+ }
+ Debug.assert(args.length === 4);
+ const [questionToken, whenTrue, colonToken, whenFalse] = args;
return node.condition !== condition
+ || node.questionToken !== questionToken
|| node.whenTrue !== whenTrue
+ || node.colonToken !== colonToken
|| node.whenFalse !== whenFalse
- ? updateNode(createConditional(condition, node.questionToken, whenTrue, node.colonToken, whenFalse), node)
+ ? updateNode(createConditional(condition, questionToken, whenTrue, colonToken, whenFalse), node)
: node;
}
@@ -3891,11 +3941,10 @@ namespace ts {
return recreateOuterExpressions(expression, mutableCall, OuterExpressionKinds.PartiallyEmittedExpressions);
}
}
- else {
- const leftmostExpressionKind = getLeftmostExpression(emittedExpression, /*stopAtCallExpressions*/ false).kind;
- if (leftmostExpressionKind === SyntaxKind.ObjectLiteralExpression || leftmostExpressionKind === SyntaxKind.FunctionExpression) {
- return setTextRange(createParen(expression), expression);
- }
+
+ const leftmostExpressionKind = getLeftmostExpression(emittedExpression, /*stopAtCallExpressions*/ false).kind;
+ if (leftmostExpressionKind === SyntaxKind.ObjectLiteralExpression || leftmostExpressionKind === SyntaxKind.FunctionExpression) {
+ return setTextRange(createParen(expression), expression);
}
return expression;
@@ -4113,7 +4162,8 @@ namespace ts {
const moduleKind = getEmitModuleKind(compilerOptions);
let create = hasExportStarsToExportValues
&& moduleKind !== ModuleKind.System
- && moduleKind !== ModuleKind.ES2015;
+ && moduleKind !== ModuleKind.ES2015
+ && moduleKind !== ModuleKind.ESNext;
if (!create) {
const helpers = getEmitHelpers(node);
if (helpers) {
diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts
index 5fdac504896..84256b3a1b1 100644
--- a/src/compiler/moduleNameResolver.ts
+++ b/src/compiler/moduleNameResolver.ts
@@ -51,13 +51,17 @@ namespace ts {
DtsOnly /** Only '.d.ts' */
}
+ interface PathAndPackageId {
+ readonly fileName: string;
+ readonly packageId: PackageId;
+ }
/** Used with `Extensions.DtsOnly` to extract the path from TypeScript results. */
- function resolvedTypeScriptOnly(resolved: Resolved | undefined): string | undefined {
+ function resolvedTypeScriptOnly(resolved: Resolved | undefined): PathAndPackageId | undefined {
if (!resolved) {
return undefined;
}
Debug.assert(extensionIsTypeScript(resolved.extension));
- return resolved.path;
+ return { fileName: resolved.path, packageId: resolved.packageId };
}
function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
@@ -201,18 +205,18 @@ namespace ts {
let resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined;
if (resolved) {
if (!options.preserveSymlinks) {
- resolved = realPath(resolved, host, traceEnabled);
+ resolved = { ...resolved, fileName: realPath(resolved.fileName, host, traceEnabled) };
}
if (traceEnabled) {
- trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved, primary);
+ trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved.fileName, primary);
}
- resolvedTypeReferenceDirective = { primary, resolvedFileName: resolved };
+ resolvedTypeReferenceDirective = { primary, resolvedFileName: resolved.fileName, packageId: resolved.packageId };
}
return { resolvedTypeReferenceDirective, failedLookupLocations };
- function primaryLookup(): string | undefined {
+ function primaryLookup(): PathAndPackageId | undefined {
// Check primary library paths
if (typeRoots && typeRoots.length) {
if (traceEnabled) {
@@ -237,8 +241,8 @@ namespace ts {
}
}
- function secondaryLookup(): string | undefined {
- let resolvedFile: string;
+ function secondaryLookup(): PathAndPackageId | undefined {
+ let resolvedFile: PathAndPackageId;
const initialLocationForSecondaryLookup = containingFile && getDirectoryPath(containingFile);
if (initialLocationForSecondaryLookup !== undefined) {
@@ -675,7 +679,7 @@ namespace ts {
if (extension !== undefined) {
const path = tryFile(candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state);
if (path !== undefined) {
- return { path, extension, packageId: undefined };
+ return noPackageId({ path, ext: extension });
}
}
@@ -875,38 +879,49 @@ namespace ts {
return undefined;
}
- function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true): Resolved | undefined {
- const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
+ function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true) {
+ const { packageJsonContent, packageId } = considerPackageJson
+ ? getPackageJsonInfo(candidate, "", failedLookupLocations, onlyRecordFailures, state)
+ : { packageJsonContent: undefined, packageId: undefined };
+ return withPackageId(packageId, loadNodeModuleFromDirectoryWorker(extensions, candidate, failedLookupLocations, onlyRecordFailures, state, packageJsonContent));
+ }
- let packageId: PackageId | undefined;
-
- if (considerPackageJson) {
- const packageJsonPath = pathToPackageJson(candidate);
- if (directoryExists && state.host.fileExists(packageJsonPath)) {
- if (state.traceEnabled) {
- trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
- }
- const jsonContent = readJson(packageJsonPath, state.host);
-
- if (typeof jsonContent.name === "string" && typeof jsonContent.version === "string") {
- packageId = { name: jsonContent.name, version: jsonContent.version };
- }
-
- const fromPackageJson = loadModuleFromPackageJson(jsonContent, extensions, candidate, failedLookupLocations, state);
- if (fromPackageJson) {
- return withPackageId(packageId, fromPackageJson);
- }
- }
- else {
- if (directoryExists && state.traceEnabled) {
- trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
- }
- // record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
- failedLookupLocations.push(packageJsonPath);
- }
+ function loadNodeModuleFromDirectoryWorker(extensions: Extensions, candidate: string, failedLookupLocations: Push, onlyRecordFailures: boolean, state: ModuleResolutionState, packageJsonContent: PackageJson | undefined): PathAndExtension | undefined {
+ const fromPackageJson = packageJsonContent && loadModuleFromPackageJson(packageJsonContent, extensions, candidate, failedLookupLocations, state);
+ if (fromPackageJson) {
+ return fromPackageJson;
}
+ const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
+ return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state);
+ }
- return withPackageId(packageId, loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state));
+ function getPackageJsonInfo(
+ nodeModuleDirectory: string,
+ subModuleName: string,
+ failedLookupLocations: Push,
+ onlyRecordFailures: boolean,
+ { host, traceEnabled }: ModuleResolutionState,
+ ): { packageJsonContent: PackageJson | undefined, packageId: PackageId | undefined } {
+ const directoryExists = !onlyRecordFailures && directoryProbablyExists(nodeModuleDirectory, host);
+ const packageJsonPath = pathToPackageJson(nodeModuleDirectory);
+ if (directoryExists && host.fileExists(packageJsonPath)) {
+ if (traceEnabled) {
+ trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
+ }
+ const packageJsonContent = readJson(packageJsonPath, host);
+ const packageId: PackageId = typeof packageJsonContent.name === "string" && typeof packageJsonContent.version === "string"
+ ? { name: packageJsonContent.name, subModuleName, version: packageJsonContent.version }
+ : undefined;
+ return { packageJsonContent, packageId };
+ }
+ else {
+ if (directoryExists && traceEnabled) {
+ trace(host, Diagnostics.File_0_does_not_exist, packageJsonPath);
+ }
+ // record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
+ failedLookupLocations.push(packageJsonPath);
+ return { packageJsonContent: undefined, packageId: undefined };
+ }
}
function loadModuleFromPackageJson(jsonContent: PackageJson, extensions: Extensions, candidate: string, failedLookupLocations: Push, state: ModuleResolutionState): PathAndExtension | undefined {
@@ -961,10 +976,21 @@ namespace ts {
}
function loadModuleFromNodeModulesFolder(extensions: Extensions, moduleName: string, nodeModulesFolder: string, nodeModulesFolderExists: boolean, failedLookupLocations: Push, state: ModuleResolutionState): Resolved | undefined {
+ const { packageName, rest } = getPackageName(moduleName);
+ const packageRootPath = combinePaths(nodeModulesFolder, packageName);
+ const { packageJsonContent, packageId } = getPackageJsonInfo(packageRootPath, rest, failedLookupLocations, !nodeModulesFolderExists, state);
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
+ const pathAndExtension = loadModuleFromFile(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
+ loadNodeModuleFromDirectoryWorker(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state, packageJsonContent);
+ return withPackageId(packageId, pathAndExtension);
+ }
- return loadModuleFromFileNoPackageId(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
- loadNodeModuleFromDirectory(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
+ function getPackageName(moduleName: string): { packageName: string, rest: string } {
+ let idx = moduleName.indexOf(directorySeparator);
+ if (moduleName[0] === "@") {
+ idx = moduleName.indexOf(directorySeparator, idx + 1);
+ }
+ return idx === -1 ? { packageName: moduleName, rest: "" } : { packageName: moduleName.slice(0, idx), rest: moduleName.slice(idx + 1) };
}
function loadModuleFromNodeModules(extensions: Extensions, moduleName: string, directory: string, failedLookupLocations: Push, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache): SearchResult {
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 71c7d3aac49..766d53cb433 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -438,8 +438,10 @@ namespace ts {
visitNode(cbNode, (node).typeExpression);
}
case SyntaxKind.JSDocTypeLiteral:
- for (const tag of (node as JSDocTypeLiteral).jsDocPropertyTags) {
- visitNode(cbNode, tag);
+ if ((node as JSDocTypeLiteral).jsDocPropertyTags) {
+ for (const tag of (node as JSDocTypeLiteral).jsDocPropertyTags) {
+ visitNode(cbNode, tag);
+ }
}
return;
case SyntaxKind.PartiallyEmittedExpression:
@@ -729,7 +731,7 @@ namespace ts {
}
- function addJSDocComment(node: T): T {
+ function addJSDocComment(node: T): T {
const comments = getJSDocCommentRanges(node, sourceFile.text);
if (comments) {
for (const comment of comments) {
@@ -768,7 +770,7 @@ namespace ts {
const saveParent = parent;
parent = n;
forEachChild(n, visitNode);
- if (n.jsDoc) {
+ if (hasJSDocNodes(n)) {
for (const jsDoc of n.jsDoc) {
jsDoc.parent = n;
parent = jsDoc;
@@ -940,10 +942,6 @@ namespace ts {
return scanner.getStartPos();
}
- function getNodeEnd(): number {
- return scanner.getStartPos();
- }
-
// Use this function to access the current token instead of reading the currentToken
// variable. Since function results aren't narrowed in control flow analysis, this ensures
// that the type checker doesn't make wrong assumptions about the type of the current
@@ -1135,13 +1133,14 @@ namespace ts {
new TokenConstructor(kind, pos, pos);
}
- function createNodeArray(elements?: T[], pos?: number): MutableNodeArray {
- const array = >(elements || []);
- if (!(pos >= 0)) {
- pos = getNodePos();
- }
+ function createNodeArray(elements: T[], pos: number, end?: number): NodeArray {
+ // Since the element list of a node array is typically created by starting with an empty array and
+ // repeatedly calling push(), the list may not have the optimal memory layout. We invoke slice() for
+ // small arrays (1 to 4 elements) to give the VM a chance to allocate an optimal representation.
+ const length = elements.length;
+ const array = >(length >= 1 && length <= 4 ? elements.slice() : elements);
array.pos = pos;
- array.end = pos;
+ array.end = end === undefined ? scanner.getStartPos() : end;
return array;
}
@@ -1208,7 +1207,10 @@ namespace ts {
return finishNode(node);
}
- return createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, diagnosticMessage || Diagnostics.Identifier_expected);
+ // Only for end of file because the error gets reported incorrectly on embedded script tags.
+ const reportAtCurrentPosition = token() === SyntaxKind.EndOfFileToken;
+
+ return createMissingNode(SyntaxKind.Identifier, reportAtCurrentPosition, diagnosticMessage || Diagnostics.Identifier_expected);
}
function parseIdentifier(diagnosticMessage?: DiagnosticMessage): Identifier {
@@ -1527,12 +1529,13 @@ namespace ts {
function parseList(kind: ParsingContext, parseElement: () => T): NodeArray {
const saveParsingContext = parsingContext;
parsingContext |= 1 << kind;
- const result = createNodeArray();
+ const list = [];
+ const listPos = getNodePos();
while (!isListTerminator(kind)) {
if (isListElement(kind, /*inErrorRecovery*/ false)) {
const element = parseListElement(kind, parseElement);
- result.push(element);
+ list.push(element);
continue;
}
@@ -1542,9 +1545,8 @@ namespace ts {
}
}
- result.end = getNodeEnd();
parsingContext = saveParsingContext;
- return result;
+ return createNodeArray(list, listPos);
}
function parseListElement(parsingContext: ParsingContext, parseElement: () => T): T {
@@ -1874,13 +1876,14 @@ namespace ts {
function parseDelimitedList(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray {
const saveParsingContext = parsingContext;
parsingContext |= 1 << kind;
- const result = createNodeArray();
+ const list = [];
+ const listPos = getNodePos();
let commaStart = -1; // Meaning the previous token was not a comma
while (true) {
if (isListElement(kind, /*inErrorRecovery*/ false)) {
const startPos = scanner.getStartPos();
- result.push(parseListElement(kind, parseElement));
+ list.push(parseListElement(kind, parseElement));
commaStart = scanner.getTokenPos();
if (parseOptional(SyntaxKind.CommaToken)) {
@@ -1924,6 +1927,8 @@ namespace ts {
}
}
+ parsingContext = saveParsingContext;
+ const result = createNodeArray(list, listPos);
// Recording the trailing comma is deliberately done after the previous
// loop, and not just if we see a list terminator. This is because the list
// may have ended incorrectly, but it is still important to know if there
@@ -1933,14 +1938,11 @@ namespace ts {
// Always preserve a trailing comma by marking it on the NodeArray
result.hasTrailingComma = true;
}
-
- result.end = getNodeEnd();
- parsingContext = saveParsingContext;
return result;
}
function createMissingList(): NodeArray {
- return createNodeArray();
+ return createNodeArray([], getNodePos());
}
function parseBracketedList(kind: ParsingContext, parseElement: () => T, open: SyntaxKind, close: SyntaxKind): NodeArray {
@@ -2015,15 +2017,15 @@ namespace ts {
template.head = parseTemplateHead();
Debug.assert(template.head.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
- const templateSpans = createNodeArray();
+ const list = [];
+ const listPos = getNodePos();
do {
- templateSpans.push(parseTemplateSpan());
+ list.push(parseTemplateSpan());
}
- while (lastOrUndefined(templateSpans).literal.kind === SyntaxKind.TemplateMiddle);
+ while (lastOrUndefined(list).literal.kind === SyntaxKind.TemplateMiddle);
- templateSpans.end = getNodeEnd();
- template.templateSpans = templateSpans;
+ template.templateSpans = createNodeArray(list, listPos);
return finishNode(template);
}
@@ -2158,7 +2160,7 @@ namespace ts {
const result = createNode(SyntaxKind.JSDocFunctionType);
nextToken();
fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type | SignatureFlags.JSDoc, result);
- return finishNode(result);
+ return addJSDocComment(finishNode(result));
}
const node = createNode(SyntaxKind.TypeReference);
node.typeName = parseIdentifierName();
@@ -2237,10 +2239,11 @@ namespace ts {
return token() === SyntaxKind.DotDotDotToken ||
isIdentifierOrPattern() ||
isModifierKind(token()) ||
- token() === SyntaxKind.AtToken || isStartOfType();
+ token() === SyntaxKind.AtToken ||
+ isStartOfType(/*inStartOfParameter*/ true);
}
- function parseParameter(): ParameterDeclaration {
+ function parseParameter(requireEqualsToken?: boolean): ParameterDeclaration {
const node = createNode(SyntaxKind.Parameter);
if (token() === SyntaxKind.ThisKeyword) {
node.name = createIdentifier(/*isIdentifier*/ true);
@@ -2269,19 +2272,11 @@ namespace ts {
node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken);
node.type = parseParameterType();
- node.initializer = parseBindingElementInitializer(/*inParameter*/ true);
+ node.initializer = parseInitializer(/*inParameter*/ true, requireEqualsToken);
return addJSDocComment(finishNode(node));
}
- function parseBindingElementInitializer(inParameter: boolean) {
- return inParameter ? parseParameterInitializer() : parseNonParameterInitializer();
- }
-
- function parseParameterInitializer() {
- return parseInitializer(/*inParameter*/ true);
- }
-
function fillSignature(
returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken,
flags: SignatureFlags,
@@ -2334,7 +2329,8 @@ namespace ts {
setYieldContext(!!(flags & SignatureFlags.Yield));
setAwaitContext(!!(flags & SignatureFlags.Await));
- const result = parseDelimitedList(ParsingContext.Parameters, flags & SignatureFlags.JSDoc ? parseJSDocParameter : parseParameter);
+ const result = parseDelimitedList(ParsingContext.Parameters,
+ flags & SignatureFlags.JSDoc ? parseJSDocParameter : () => parseParameter(!!(flags & SignatureFlags.RequireCompleteParameterList)));
setYieldContext(savedYieldContext);
setAwaitContext(savedAwaitContext);
@@ -2365,7 +2361,7 @@ namespace ts {
parseSemicolon();
}
- function parseSignatureMember(kind: SyntaxKind): CallSignatureDeclaration | ConstructSignatureDeclaration {
+ function parseSignatureMember(kind: SyntaxKind.CallSignature | SyntaxKind.ConstructSignature): CallSignatureDeclaration | ConstructSignatureDeclaration {
const node = createNode(kind);
if (kind === SyntaxKind.ConstructSignature) {
parseExpected(SyntaxKind.NewKeyword);
@@ -2445,7 +2441,7 @@ namespace ts {
node.parameters = parseBracketedList(ParsingContext.Parameters, parseParameter, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken);
node.type = parseTypeAnnotation();
parseTypeMemberSemicolon();
- return finishNode(node);
+ return addJSDocComment(finishNode(node));
}
function parsePropertyOrMethodSignature(fullStart: number, modifiers: NodeArray): PropertySignature | MethodSignature {
@@ -2605,7 +2601,7 @@ namespace ts {
parseExpected(SyntaxKind.NewKeyword);
}
fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type, node);
- return finishNode(node);
+ return addJSDocComment(finishNode(node));
}
function parseKeywordAndNoDot(): TypeNode | undefined {
@@ -2621,16 +2617,9 @@ namespace ts {
unaryMinusExpression.operator = SyntaxKind.MinusToken;
nextToken();
}
- let expression: UnaryExpression;
- switch (token()) {
- case SyntaxKind.StringLiteral:
- case SyntaxKind.NumericLiteral:
- expression = parseLiteralLikeNode(token()) as LiteralExpression;
- break;
- case SyntaxKind.TrueKeyword:
- case SyntaxKind.FalseKeyword:
- expression = parseTokenNode();
- }
+ let expression: BooleanLiteral | LiteralExpression | PrefixUnaryExpression = token() === SyntaxKind.TrueKeyword || token() === SyntaxKind.FalseKeyword
+ ? parseTokenNode()
+ : parseLiteralLikeNode(token()) as LiteralExpression;
if (negative) {
unaryMinusExpression.operand = expression;
finishNode(unaryMinusExpression);
@@ -2666,6 +2655,7 @@ namespace ts {
return parseJSDocNodeWithType(SyntaxKind.JSDocVariadicType);
case SyntaxKind.ExclamationToken:
return parseJSDocNodeWithType(SyntaxKind.JSDocNonNullableType);
+ case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.TrueKeyword:
@@ -2698,7 +2688,7 @@ namespace ts {
}
}
- function isStartOfType(): boolean {
+ function isStartOfType(inStartOfParameter?: boolean): boolean {
switch (token()) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.StringKeyword:
@@ -2728,11 +2718,11 @@ namespace ts {
case SyntaxKind.DotDotDotToken:
return true;
case SyntaxKind.MinusToken:
- return lookAhead(nextTokenIsNumericLiteral);
+ return !inStartOfParameter && lookAhead(nextTokenIsNumericLiteral);
case SyntaxKind.OpenParenToken:
// Only consider '(' the start of a type if followed by ')', '...', an identifier, a modifier,
// or something that starts a type. We don't want to consider things like '(1)' a type.
- return lookAhead(isStartOfParenthesizedOrFunctionType);
+ return !inStartOfParameter && lookAhead(isStartOfParenthesizedOrFunctionType);
default:
return isIdentifier();
}
@@ -2806,13 +2796,12 @@ namespace ts {
parseOptional(operator);
let type = parseConstituentType();
if (token() === operator) {
- const types = createNodeArray([type], type.pos);
+ const types = [type];
while (parseOptional(operator)) {
types.push(parseConstituentType());
}
- types.end = getNodeEnd();
const node = createNode(kind, type.pos);
- node.types = types;
+ node.types = createNodeArray(types, type.pos);
type = finishNode(node);
}
return type;
@@ -3018,7 +3007,7 @@ namespace ts {
return expr;
}
- function parseInitializer(inParameter: boolean): Expression {
+ function parseInitializer(inParameter: boolean, requireEqualsToken?: boolean): Expression {
if (token() !== SyntaxKind.EqualsToken) {
// It's not uncommon during typing for the user to miss writing the '=' token. Check if
// there is no newline after the last token and if we're on an expression. If so, parse
@@ -3033,11 +3022,17 @@ namespace ts {
// do not try to parse initializer
return undefined;
}
+ if (inParameter && requireEqualsToken) {
+ // = is required when speculatively parsing arrow function parameters,
+ // so return a fake initializer as a signal that the equals token was missing
+ const result = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics._0_expected, "=") as Identifier;
+ result.escapedText = "= not found" as __String;
+ return result;
+ }
}
// Initializer[In, Yield] :
// = AssignmentExpression[?In, ?Yield]
-
parseExpected(SyntaxKind.EqualsToken);
return parseAssignmentExpressionOrHigher();
}
@@ -3178,8 +3173,7 @@ namespace ts {
parameter.name = identifier;
finishNode(parameter);
- node.parameters = createNodeArray([parameter], parameter.pos);
- node.parameters.end = parameter.end;
+ node.parameters = createNodeArray([parameter], parameter.pos, parameter.end);
node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, "=>");
node.body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier);
@@ -3353,8 +3347,7 @@ namespace ts {
function tryParseAsyncSimpleArrowFunctionExpression(): ArrowFunction | undefined {
// We do a check here so that we won't be doing unnecessarily call to "lookAhead"
if (token() === SyntaxKind.AsyncKeyword) {
- const isUnParenthesizedAsyncArrowFunction = lookAhead(isUnParenthesizedAsyncArrowFunctionWorker);
- if (isUnParenthesizedAsyncArrowFunction === Tristate.True) {
+ if (lookAhead(isUnParenthesizedAsyncArrowFunctionWorker) === Tristate.True) {
const asyncModifier = parseModifiersForArrowFunction();
const expr = parseBinaryExpressionOrHigher(/*precedence*/ 0);
return parseSimpleArrowFunctionExpression(expr, asyncModifier);
@@ -3388,7 +3381,6 @@ namespace ts {
const node = createNode(SyntaxKind.ArrowFunction);
node.modifiers = parseModifiersForArrowFunction();
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
-
// Arrow functions are never generators.
//
// If we're speculatively parsing a signature for a parenthesized arrow function, then
@@ -3411,7 +3403,8 @@ namespace ts {
// - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation.
//
// So we need just a bit of lookahead to ensure that it can only be a signature.
- if (!allowAmbiguity && token() !== SyntaxKind.EqualsGreaterThanToken && token() !== SyntaxKind.OpenBraceToken) {
+ if (!allowAmbiguity && ((token() !== SyntaxKind.EqualsGreaterThanToken && token() !== SyntaxKind.OpenBraceToken) ||
+ find(node.parameters, p => p.initializer && ts.isIdentifier(p.initializer) && p.initializer.escapedText === "= not found"))) {
// Returning undefined here will cause our caller to rewind to where we started from.
return undefined;
}
@@ -4029,7 +4022,8 @@ namespace ts {
}
function parseJsxChildren(openingTagName: LeftHandSideExpression): NodeArray {
- const result = createNodeArray();
+ const list = [];
+ const listPos = getNodePos();
const saveParsingContext = parsingContext;
parsingContext |= 1 << ParsingContext.JsxChildren;
@@ -4050,15 +4044,13 @@ namespace ts {
}
const child = parseJsxChild();
if (child) {
- result.push(child);
+ list.push(child);
}
}
- result.end = scanner.getTokenPos();
-
parsingContext = saveParsingContext;
- return result;
+ return createNodeArray(list, listPos);
}
function parseJsxAttributes(): JsxAttributes {
@@ -5161,7 +5153,7 @@ namespace ts {
const node = createNode(SyntaxKind.BindingElement);
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
node.name = parseIdentifierOrPattern();
- node.initializer = parseBindingElementInitializer(/*inParameter*/ false);
+ node.initializer = parseInitializer(/*inParameter*/ false);
return finishNode(node);
}
@@ -5178,7 +5170,7 @@ namespace ts {
node.propertyName = propertyName;
node.name = parseIdentifierOrPattern();
}
- node.initializer = parseBindingElementInitializer(/*inParameter*/ false);
+ node.initializer = parseInitializer(/*inParameter*/ false);
return finishNode(node);
}
@@ -5217,7 +5209,7 @@ namespace ts {
node.name = parseIdentifierOrPattern();
node.type = parseTypeAnnotation();
if (!isInOrOfKeyword(token())) {
- node.initializer = parseInitializer(/*inParameter*/ false);
+ node.initializer = parseNonParameterInitializer();
}
return finishNode(node);
}
@@ -5451,27 +5443,19 @@ namespace ts {
}
function parseDecorators(): NodeArray {
- let decorators: NodeArray & Decorator[];
+ let list: Decorator[];
+ const listPos = getNodePos();
while (true) {
const decoratorStart = getNodePos();
if (!parseOptional(SyntaxKind.AtToken)) {
break;
}
-
const decorator = createNode(SyntaxKind.Decorator, decoratorStart);
decorator.expression = doInDecoratorContext(parseLeftHandSideExpressionOrHigher);
finishNode(decorator);
- if (!decorators) {
- decorators = createNodeArray([decorator], decoratorStart);
- }
- else {
- decorators.push(decorator);
- }
+ (list || (list = [])).push(decorator);
}
- if (decorators) {
- decorators.end = getNodeEnd();
- }
- return decorators;
+ return list && createNodeArray(list, listPos);
}
/*
@@ -5482,7 +5466,8 @@ namespace ts {
* In such situations, 'permitInvalidConstAsModifier' should be set to true.
*/
function parseModifiers(permitInvalidConstAsModifier?: boolean): NodeArray | undefined {
- let modifiers: MutableNodeArray | undefined;
+ let list: Modifier[];
+ const listPos = getNodePos();
while (true) {
const modifierStart = scanner.getStartPos();
const modifierKind = token();
@@ -5501,17 +5486,9 @@ namespace ts {
}
const modifier = finishNode(createNode(modifierKind, modifierStart));
- if (!modifiers) {
- modifiers = createNodeArray([modifier], modifierStart);
- }
- else {
- modifiers.push(modifier);
- }
+ (list || (list = [])).push(modifier);
}
- if (modifiers) {
- modifiers.end = scanner.getStartPos();
- }
- return modifiers;
+ return list && createNodeArray(list, listPos);
}
function parseModifiersForArrowFunction(): NodeArray {
@@ -5522,9 +5499,7 @@ namespace ts {
nextToken();
const modifier = finishNode(createNode(modifierKind, modifierStart));
modifiers = createNodeArray([modifier], modifierStart);
- modifiers.end = scanner.getStartPos();
}
-
return modifiers;
}
@@ -6182,7 +6157,7 @@ namespace ts {
return jsDoc ? { jsDoc, diagnostics } : undefined;
}
- export function parseJSDocComment(parent: Node, start: number, length: number): JSDoc {
+ export function parseJSDocComment(parent: HasJSDoc, start: number, length: number): JSDoc {
const saveToken = currentToken;
const saveParseDiagnosticsLength = parseDiagnostics.length;
const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode;
@@ -6226,7 +6201,9 @@ namespace ts {
Debug.assert(start <= end);
Debug.assert(end <= content.length);
- let tags: MutableNodeArray;
+ let tags: JSDocTag[];
+ let tagsPos: number;
+ let tagsEnd: number;
const comments: string[] = [];
let result: JSDoc;
@@ -6359,7 +6336,7 @@ namespace ts {
function createJSDocComment(): JSDoc {
const result = createNode(SyntaxKind.JSDocComment, start);
- result.tags = tags;
+ result.tags = tags && createNodeArray(tags, tagsPos, tagsEnd);
result.comment = comments.length ? comments.join("") : undefined;
return finishNode(result, end);
}
@@ -6499,12 +6476,13 @@ namespace ts {
tag.comment = comments.join("");
if (!tags) {
- tags = createNodeArray([tag], tag.pos);
+ tags = [tag];
+ tagsPos = tag.pos;
}
else {
tags.push(tag);
}
- tags.end = tag.end;
+ tagsEnd = tag.end;
}
function tryParseTypeExpression(): JSDocTypeExpression | undefined {
@@ -6671,19 +6649,18 @@ namespace ts {
if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) {
let child: JSDocTypeTag | JSDocPropertyTag | false;
let jsdocTypeLiteral: JSDocTypeLiteral;
- let alreadyHasTypeTag = false;
+ let childTypeTag: JSDocTypeTag;
const start = scanner.getStartPos();
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Property))) {
if (!jsdocTypeLiteral) {
jsdocTypeLiteral = createNode(SyntaxKind.JSDocTypeLiteral, start);
}
if (child.kind === SyntaxKind.JSDocTypeTag) {
- if (alreadyHasTypeTag) {
+ if (childTypeTag) {
break;
}
else {
- jsdocTypeLiteral.jsDocTypeTag = child;
- alreadyHasTypeTag = true;
+ childTypeTag = child;
}
}
else {
@@ -6697,7 +6674,9 @@ namespace ts {
if (typeExpression && typeExpression.type.kind === SyntaxKind.ArrayType) {
jsdocTypeLiteral.isArrayType = true;
}
- typedefTag.typeExpression = finishNode(jsdocTypeLiteral);
+ typedefTag.typeExpression = childTypeTag && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ?
+ childTypeTag.typeExpression :
+ finishNode(jsdocTypeLiteral);
}
}
@@ -6804,7 +6783,8 @@ namespace ts {
}
// Type parameter list looks like '@template T,U,V'
- const typeParameters = createNodeArray();
+ const typeParameters = [];
+ const typeParametersPos = getNodePos();
while (true) {
const name = parseJSDocIdentifierName();
@@ -6832,9 +6812,8 @@ namespace ts {
const result = createNode(SyntaxKind.JSDocTemplateTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
- result.typeParameters = typeParameters;
+ result.typeParameters = createNodeArray(typeParameters, typeParametersPos);
finishNode(result);
- typeParameters.end = result.end;
return result;
}
@@ -6997,7 +6976,7 @@ namespace ts {
}
forEachChild(node, visitNode, visitArray);
- if (node.jsDoc) {
+ if (hasJSDocNodes(node)) {
for (const jsDocComment of node.jsDoc) {
forEachChild(jsDocComment, visitNode, visitArray);
}
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index ec220ad248e..f03a073dfb7 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -245,7 +245,7 @@ namespace ts {
const redForegroundEscapeSequence = "\u001b[91m";
const yellowForegroundEscapeSequence = "\u001b[93m";
const blueForegroundEscapeSequence = "\u001b[93m";
- const gutterStyleSequence = "\u001b[100;30m";
+ const gutterStyleSequence = "\u001b[30;47m";
const gutterSeparator = " ";
const resetEscapeSequence = "\u001b[0m";
const ellipsis = "...";
@@ -268,7 +268,7 @@ namespace ts {
return s;
}
- export function formatDiagnosticsWithColorAndContext(diagnostics: Diagnostic[], host: FormatDiagnosticsHost): string {
+ export function formatDiagnosticsWithColorAndContext(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string {
let output = "";
for (const diagnostic of diagnostics) {
if (diagnostic.file) {
@@ -284,12 +284,12 @@ namespace ts {
gutterWidth = Math.max(ellipsis.length, gutterWidth);
}
- output += sys.newLine;
+ output += host.getNewLine();
for (let i = firstLine; i <= lastLine; i++) {
// If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
// so we'll skip ahead to the second-to-last line.
if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) {
- output += formatAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + sys.newLine;
+ output += formatAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + host.getNewLine();
i = lastLine - 1;
}
@@ -301,7 +301,7 @@ namespace ts {
// Output the gutter and the actual contents of the line.
output += formatAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator;
- output += lineContent + sys.newLine;
+ output += lineContent + host.getNewLine();
// Output the gutter and the error span for the line using tildes.
output += formatAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator;
@@ -323,17 +323,17 @@ namespace ts {
}
output += resetEscapeSequence;
- output += sys.newLine;
+ output += host.getNewLine();
}
- output += sys.newLine;
+ output += host.getNewLine();
output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `;
}
const categoryColor = getCategoryFormat(diagnostic.category);
const category = DiagnosticCategory[diagnostic.category].toLowerCase();
- output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`;
- output += sys.newLine;
+ output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine()) }`;
+ output += host.getNewLine();
}
return output;
}
@@ -438,6 +438,8 @@ namespace ts {
host = host || createCompilerHost(options);
let skipDefaultLib = options.noLib;
+ const getDefaultLibraryFileName = memoize(() => host.getDefaultLibFileName(options));
+ const defaultLibraryPath = host.getDefaultLibLocation ? host.getDefaultLibLocation() : getDirectoryPath(getDefaultLibraryFileName());
const programDiagnostics = createDiagnosticCollection();
const currentDirectory = host.getCurrentDirectory();
const supportedExtensions = getSupportedExtensions(options);
@@ -513,12 +515,11 @@ namespace ts {
// If '--lib' is not specified, include default library file according to '--target'
// otherwise, using options specified in '--lib' instead of '--target' default library file
if (!options.lib) {
- processRootFile(host.getDefaultLibFileName(options), /*isDefaultLib*/ true);
+ processRootFile(getDefaultLibraryFileName(), /*isDefaultLib*/ true);
}
else {
- const libDirectory = host.getDefaultLibLocation ? host.getDefaultLibLocation() : getDirectoryPath(host.getDefaultLibFileName(options));
forEach(options.lib, libFileName => {
- processRootFile(combinePaths(libDirectory, libFileName), /*isDefaultLib*/ true);
+ processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true);
});
}
}
@@ -557,6 +558,7 @@ namespace ts {
getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
isSourceFileFromExternalLibrary,
+ isSourceFileDefaultLibrary,
dropDiagnosticsProducingTypeChecker,
getSourceFileFromReference,
sourceFileToPackageName,
@@ -977,6 +979,18 @@ namespace ts {
return sourceFilesFoundSearchingNodeModules.get(file.path);
}
+ function isSourceFileDefaultLibrary(file: SourceFile): boolean {
+ if (file.hasNoDefaultLib) {
+ return true;
+ }
+
+ if (defaultLibraryPath && defaultLibraryPath.length !== 0) {
+ return containsPath(defaultLibraryPath, file.path, currentDirectory, /*ignoreCase*/ !host.useCaseSensitiveFileNames());
+ }
+
+ return compareStrings(file.fileName, getDefaultLibraryFileName(), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
+ }
+
function getDiagnosticsProducingTypeChecker() {
return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
}
@@ -1158,9 +1172,7 @@ namespace ts {
const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName);
const diagnostics = bindDiagnostics.concat(checkDiagnostics, fileProcessingDiagnosticsInFile, programDiagnosticsInFile);
- return isSourceFileJavaScript(sourceFile)
- ? filter(diagnostics, shouldReportDiagnostic)
- : diagnostics;
+ return filter(diagnostics, shouldReportDiagnostic);
});
}
@@ -1208,7 +1220,7 @@ namespace ts {
diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_can_only_be_used_in_a_ts_file, "?"));
return;
}
- // falls through
+ // falls through
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
@@ -1290,7 +1302,7 @@ namespace ts {
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.type_parameter_declarations_can_only_be_used_in_a_ts_file));
return;
}
- // falls through
+ // falls through
case SyntaxKind.VariableStatement:
// Check modifiers
if (nodes === (parent).modifiers) {
@@ -1338,8 +1350,8 @@ namespace ts {
if (isConstValid) {
continue;
}
- // to report error,
- // falls through
+ // to report error,
+ // falls through
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
@@ -1427,7 +1439,7 @@ namespace ts {
}
function processRootFile(fileName: string, isDefaultLib: boolean) {
- processSourceFile(normalizePath(fileName), isDefaultLib);
+ processSourceFile(normalizePath(fileName), isDefaultLib, /*packageId*/ undefined);
}
function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
@@ -1553,10 +1565,10 @@ namespace ts {
}
function getSourceFileFromReferenceWorker(
- fileName: string,
- getSourceFile: (fileName: string) => SourceFile | undefined,
- fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void,
- refFile?: SourceFile): SourceFile | undefined {
+ fileName: string,
+ getSourceFile: (fileName: string) => SourceFile | undefined,
+ fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void,
+ refFile?: SourceFile): SourceFile | undefined {
if (hasExtension(fileName)) {
if (!options.allowNonTsExtensions && !forEach(supportedExtensions, extension => fileExtensionIs(host.getCanonicalFileName(fileName), extension))) {
@@ -1591,9 +1603,9 @@ namespace ts {
}
/** This has side effects through `findSourceFile`. */
- function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
+ function processSourceFile(fileName: string, isDefaultLib: boolean, packageId: PackageId | undefined, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
getSourceFileFromReferenceWorker(fileName,
- fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd, /*packageId*/ undefined),
+ fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd, packageId),
(diagnostic, ...args) => {
fileProcessingDiagnostics.add(refFile !== undefined && refEnd !== undefined && refPos !== undefined
? createFileDiagnostic(refFile, refPos, refEnd - refPos, diagnostic, ...args)
@@ -1675,7 +1687,7 @@ namespace ts {
});
if (packageId) {
- const packageIdKey = `${packageId.name}@${packageId.version}`;
+ const packageIdKey = `${packageId.name}/${packageId.subModuleName}@${packageId.version}`;
const fileFromPackageId = packageIdToSourceFile.get(packageIdKey);
if (fileFromPackageId) {
// Some other SourceFile already exists with this package name and version.
@@ -1735,7 +1747,7 @@ namespace ts {
function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
forEach(file.referencedFiles, ref => {
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
- processSourceFile(referencedFileName, isDefaultLib, file, ref.pos, ref.end);
+ processSourceFile(referencedFileName, isDefaultLib, /*packageId*/ undefined, file, ref.pos, ref.end);
});
}
@@ -1766,7 +1778,7 @@ namespace ts {
if (resolvedTypeReferenceDirective) {
if (resolvedTypeReferenceDirective.primary) {
// resolved from the primary path
- processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd);
+ processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd);
}
else {
// If we already resolved to this file, it must have been a secondary reference. Check file contents
@@ -1789,7 +1801,7 @@ namespace ts {
}
else {
// First resolution of this library
- processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, refFile, refPos, refEnd);
+ processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, refFile, refPos, refEnd);
}
}
}
@@ -1832,7 +1844,8 @@ namespace ts {
}
const isFromNodeModulesSearch = resolution.isExternalLibraryImport;
- const isJsFileFromNodeModules = isFromNodeModulesSearch && !extensionIsTypeScript(resolution.extension);
+ const isJsFile = !extensionIsTypeScript(resolution.extension);
+ const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile;
const resolvedFileName = resolution.resolvedFileName;
if (isFromNodeModulesSearch) {
@@ -1847,7 +1860,12 @@ namespace ts {
const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth;
// Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs')
// This may still end up being an untyped module -- the file won't be included but imports will be allowed.
- const shouldAddFile = resolvedFileName && !getResolutionDiagnostic(options, resolution) && !options.noResolve && i < file.imports.length && !elideImport;
+ const shouldAddFile = resolvedFileName
+ && !getResolutionDiagnostic(options, resolution)
+ && !options.noResolve
+ && i < file.imports.length
+ && !elideImport
+ && !(isJsFile && !options.allowJs);
if (elideImport) {
modulesWithElidedImports.set(file.path, true);
@@ -2222,7 +2240,7 @@ namespace ts {
return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set;
}
function needAllowJs() {
- return options.allowJs ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_allowJs_is_not_set;
+ return options.allowJs || !options.noImplicitAny ? undefined : Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type;
}
}
diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts
index a130d8427da..c9c14198279 100644
--- a/src/compiler/scanner.ts
+++ b/src/compiler/scanner.ts
@@ -337,7 +337,7 @@ namespace ts {
Debug.assert(res < lineStarts[line + 1]);
}
else if (debugText !== undefined) {
- Debug.assert(res < debugText.length);
+ Debug.assert(res <= debugText.length); // Allow single character overflow for trailing newline
}
return res;
}
diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts
index 43058358ee5..90c9063c140 100644
--- a/src/compiler/transformers/es2017.ts
+++ b/src/compiler/transformers/es2017.ts
@@ -21,9 +21,6 @@ namespace ts {
const compilerOptions = context.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
- // These variables contain state that changes as we descend into the tree.
- let currentSourceFile: SourceFile;
-
/**
* Keeps track of whether expression substitution has been enabled for specific edge cases.
* They are persisted between each SourceFile transformation and should not be reset.
@@ -51,12 +48,8 @@ namespace ts {
return node;
}
- currentSourceFile = node;
-
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());
-
- currentSourceFile = undefined;
return visited;
}
@@ -197,9 +190,10 @@ namespace ts {
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
+ node.equalsGreaterThanToken,
getFunctionFlags(node) & FunctionFlags.Async
? transformAsyncFunctionBody(node)
- : visitFunctionBody(node.body, visitor, context)
+ : visitFunctionBody(node.body, visitor, context),
);
}
diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts
index 3bdcc9e9ee7..0fca09b4540 100644
--- a/src/compiler/transformers/esnext.ts
+++ b/src/compiler/transformers/esnext.ts
@@ -595,7 +595,8 @@ namespace ts {
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
- transformFunctionBody(node)
+ node.equalsGreaterThanToken,
+ transformFunctionBody(node),
);
enclosingFunctionFlags = savedEnclosingFunctionFlags;
return updated;
diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts
index 20195adeef4..06ecae3a63f 100644
--- a/src/compiler/transformers/generators.ts
+++ b/src/compiler/transformers/generators.ts
@@ -244,7 +244,6 @@ namespace ts {
const previousOnSubstituteNode = context.onSubstituteNode;
context.onSubstituteNode = onSubstituteNode;
- let currentSourceFile: SourceFile;
let renamedCatchVariables: Map;
let renamedCatchVariableDeclarations: Identifier[];
@@ -300,12 +299,9 @@ namespace ts {
return node;
}
- currentSourceFile = node;
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());
-
- currentSourceFile = undefined;
return visited;
}
diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts
index 493f5a43a1f..08c1fccfe16 100644
--- a/src/compiler/transformers/module/module.ts
+++ b/src/compiler/transformers/module/module.ts
@@ -200,6 +200,7 @@ namespace ts {
*/
function transformUMDModule(node: SourceFile) {
const { aliasedModuleNames, unaliasedModuleNames, importAliasNames } = collectAsynchronousDependencies(node, /*includeNonAmdDependencies*/ false);
+ const moduleName = tryGetModuleNameFromFile(node, host, compilerOptions);
const umdHeader = createFunctionExpression(
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
@@ -260,6 +261,8 @@ namespace ts {
createIdentifier("define"),
/*typeArguments*/ undefined,
[
+ // Add the module name (if provided).
+ ...(moduleName ? [moduleName] : []),
createArrayLiteral([
createLiteral("require"),
createLiteral("exports"),
diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts
index e1c239736d5..8c47ec82f70 100644
--- a/src/compiler/transformers/module/system.ts
+++ b/src/compiler/transformers/module/system.ts
@@ -1132,7 +1132,8 @@ namespace ts {
*/
function createExportExpression(name: Identifier | StringLiteral, value: Expression) {
const exportName = isIdentifier(name) ? createLiteral(name) : name;
- return createCall(exportFunction, /*typeArguments*/ undefined, [exportName, value]);
+ setEmitFlags(value, getEmitFlags(value) | EmitFlags.NoComments);
+ return setCommentRange(createCall(exportFunction, /*typeArguments*/ undefined, [exportName, value]), value);
}
//
diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts
index 692c758bc75..8640c642675 100644
--- a/src/compiler/transformers/ts.ts
+++ b/src/compiler/transformers/ts.ts
@@ -208,6 +208,24 @@ namespace ts {
* @param node The node to visit.
*/
function sourceElementVisitorWorker(node: Node): VisitResult {
+ switch (node.kind) {
+ case SyntaxKind.ImportDeclaration:
+ case SyntaxKind.ImportEqualsDeclaration:
+ case SyntaxKind.ExportAssignment:
+ case SyntaxKind.ExportDeclaration:
+ return visitEllidableStatement(node);
+ default:
+ return visitorWorker(node);
+ }
+ }
+
+ function visitEllidableStatement(node: ImportDeclaration | ImportEqualsDeclaration | ExportAssignment | ExportDeclaration): VisitResult {
+ const parsed = getParseTreeNode(node);
+ if (parsed !== node) {
+ // If the node has been transformed by a `before` transformer, perform no ellision on it
+ // As the type information we would attempt to lookup to perform ellision is potentially unavailable for the synthesized nodes
+ return node;
+ }
switch (node.kind) {
case SyntaxKind.ImportDeclaration:
return visitImportDeclaration(node);
@@ -218,7 +236,7 @@ namespace ts {
case SyntaxKind.ExportDeclaration:
return visitExportDeclaration(node);
default:
- return visitorWorker(node);
+ Debug.fail("Unhandled ellided statement");
}
}
@@ -503,7 +521,7 @@ namespace ts {
function visitSourceFile(node: SourceFile) {
const alwaysStrict = (compilerOptions.alwaysStrict === undefined ? compilerOptions.strict : compilerOptions.alwaysStrict) &&
- !(isExternalModule(node) && moduleKind === ModuleKind.ES2015);
+ !(isExternalModule(node) && moduleKind >= ModuleKind.ES2015);
return updateSourceFileNode(
node,
visitLexicalEnvironment(node.statements, sourceElementVisitor, context, /*start*/ 0, alwaysStrict));
@@ -2291,7 +2309,8 @@ namespace ts {
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
- visitFunctionBody(node.body, visitor, context)
+ node.equalsGreaterThanToken,
+ visitFunctionBody(node.body, visitor, context),
);
return updated;
}
@@ -2646,6 +2665,7 @@ namespace ts {
return isExportOfNamespace(node)
|| (isExternalModuleExport(node)
&& moduleKind !== ModuleKind.ES2015
+ && moduleKind !== ModuleKind.ESNext
&& moduleKind !== ModuleKind.System);
}
diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts
index d5e584c7ac5..b109b166f5c 100644
--- a/src/compiler/tsc.ts
+++ b/src/compiler/tsc.ts
@@ -100,7 +100,6 @@ namespace ts {
const commandLine = parseCommandLine(args);
let configFileName: string; // Configuration file name (if any)
let cachedConfigFileText: string; // Cached configuration file text, used for reparsing (if any)
- let configFileWatcher: FileWatcher; // Configuration file watcher
let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal
let cachedProgram: Program; // Program cached from last compilation
let rootFileNames: string[]; // Root fileNames for compilation
@@ -189,7 +188,7 @@ namespace ts {
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
if (configFileName) {
- configFileWatcher = sys.watchFile(configFileName, configFileChanged);
+ sys.watchFile(configFileName, configFileChanged);
}
if (sys.watchDirectory && configFileName) {
const directory = ts.getDirectoryPath(configFileName);
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index b22638789c1..9f40c7f3b38 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -516,8 +516,6 @@ namespace ts {
parent?: Node; // Parent node (initialized by binding)
/* @internal */ original?: Node; // The original node if this is an updated node.
/* @internal */ startsOnNewLine?: boolean; // Whether a synthesized node should start on a new line (used by transforms).
- /* @internal */ jsDoc?: JSDoc[]; // JSDoc that directly precedes this node
- /* @internal */ jsDocCache?: ReadonlyArray; // Cache for getJSDocTags
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
@@ -528,6 +526,44 @@ namespace ts {
/* @internal */ contextualMapper?: TypeMapper; // Mapper for contextual type
}
+ export interface JSDocContainer {
+ /* @internal */ jsDoc?: JSDoc[]; // JSDoc that directly precedes this node
+ /* @internal */ jsDocCache?: ReadonlyArray; // Cache for getJSDocTags
+ }
+
+ export type HasJSDoc =
+ | ParameterDeclaration
+ | CallSignatureDeclaration
+ | ConstructSignatureDeclaration
+ | MethodSignature
+ | PropertySignature
+ | ArrowFunction
+ | ParenthesizedExpression
+ | SpreadAssignment
+ | ShorthandPropertyAssignment
+ | PropertyAssignment
+ | FunctionExpression
+ | LabeledStatement
+ | ExpressionStatement
+ | VariableStatement
+ | FunctionDeclaration
+ | ConstructorDeclaration
+ | MethodDeclaration
+ | PropertyDeclaration
+ | AccessorDeclaration
+ | ClassLikeDeclaration
+ | InterfaceDeclaration
+ | TypeAliasDeclaration
+ | EnumMember
+ | EnumDeclaration
+ | ModuleDeclaration
+ | ImportEqualsDeclaration
+ | IndexSignatureDeclaration
+ | FunctionTypeNode
+ | ConstructorTypeNode
+ | JSDocFunctionType
+ | EndOfFileToken;
+
/* @internal */
export type MutableNodeArray = NodeArray & T[];
@@ -546,7 +582,7 @@ namespace ts {
export type EqualsToken = Token;
export type AsteriskToken = Token