diff --git a/Gulpfile.ts b/Gulpfile.ts
index a3db20dfd8a..676d07ec570 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,26 +666,9 @@ 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);
});
}
});
@@ -711,7 +692,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 +701,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 +821,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);
}
diff --git a/Jakefile.js b/Jakefile.js
index b3e18e8cb1a..6fd2f549015 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);
}
}
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/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/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 fa5441f6841..70bf20cdbf6 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);
@@ -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);
}
@@ -4895,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);
}
/**
@@ -5491,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);
@@ -5905,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 {
@@ -6354,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 = [];
@@ -6401,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") {
@@ -6616,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);
@@ -6632,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 {
@@ -6787,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
@@ -6800,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) {
@@ -6817,7 +6856,7 @@ namespace ts {
const id = getTypeListId(typeArguments);
let instantiation = links.instantiations.get(id);
if (!instantiation) {
- links.instantiations.set(id, instantiation = instantiateType(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;
}
@@ -7059,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;
@@ -7265,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) {
@@ -7278,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;
@@ -7411,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;
}
@@ -7504,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;
@@ -7799,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();
@@ -7826,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)
@@ -7937,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);
@@ -7971,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:
@@ -8089,10 +8159,6 @@ namespace ts {
mapper;
}
- function identityMapper(type: Type): Type {
- return type;
- }
-
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
return t => instantiateType(mapper1(t), mapper2);
}
@@ -8473,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);
}
@@ -10012,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 {
@@ -10358,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 ||
@@ -10526,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;
@@ -10550,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) {
@@ -10559,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;
@@ -10599,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) {
@@ -10639,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));
}
@@ -10790,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;
}
@@ -11431,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
@@ -11450,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) {
@@ -11518,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;
}
}
@@ -11561,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;
}
@@ -12784,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
@@ -12811,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);
}
}
}
@@ -13501,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)) {
@@ -13511,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) {
@@ -13527,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.
@@ -13537,7 +13667,7 @@ namespace ts {
if (isOptional) {
prop.flags |= SymbolFlags.Optional;
}
- if (hasDynamicName(memberDecl)) {
+ if (!literalName && hasDynamicName(memberDecl)) {
patternWithComputedProperties = true;
}
}
@@ -13595,7 +13725,7 @@ namespace ts {
checkNodeDeferred(memberDecl);
}
- if (hasDynamicName(memberDecl)) {
+ if (!literalName && hasDynamicName(memberDecl)) {
if (isNumericName(memberDecl.name)) {
hasComputedNumberProperty = true;
}
@@ -13661,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)));
}
@@ -13926,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);
@@ -14355,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
@@ -14631,7 +14763,7 @@ namespace ts {
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
- markPropertyAsReferenced(prop);
+ markPropertyAsReferenced(prop, node);
getNodeLinks(node).resolvedSymbol = prop;
@@ -14665,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));
@@ -14678,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.
@@ -14731,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
@@ -14811,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;
}
@@ -15080,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)) {
@@ -15114,7 +15260,6 @@ namespace ts {
}
}
else if (node.kind === SyntaxKind.Decorator) {
- isDecorator = true;
typeArguments = undefined;
argCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
}
@@ -15184,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[] {
@@ -15219,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.
@@ -15935,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;
@@ -15945,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;
@@ -16093,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.
@@ -16122,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);
}
@@ -16322,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.");
}
/**
@@ -16509,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;
@@ -16630,8 +16776,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);
}
@@ -17997,7 +18144,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;
@@ -18708,7 +18855,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];
@@ -19133,6 +19280,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
@@ -19503,8 +19652,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)
@@ -19798,11 +19950,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)));
}
}
});
@@ -19817,7 +19969,8 @@ 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)) {
@@ -19826,7 +19979,7 @@ namespace ts {
}
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);
}
}
@@ -19844,13 +19997,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));
}
}
}
@@ -19871,7 +20024,7 @@ namespace ts {
}
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));
+ error(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(typeParameter.symbol.escapedName));
}
}
}
@@ -19884,7 +20037,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));
}
}
}
@@ -19897,7 +20050,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);
}
@@ -20095,7 +20255,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) {
@@ -20150,7 +20310,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;
}
@@ -20234,7 +20394,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);
}
@@ -21491,7 +21651,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;
@@ -22111,9 +22271,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);
}
}
}
@@ -22149,7 +22309,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);
}
}
@@ -22170,7 +22330,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));
}
@@ -22210,10 +22370,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
@@ -22487,6 +22651,7 @@ namespace ts {
deferredNodes = [];
deferredUnusedIdentifierNodes = produceDiagnostics && noUnusedIdentifiers ? [] : undefined;
+ flowAnalysisDisabled = false;
forEach(node.statements, checkSourceElement);
@@ -22925,14 +23090,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
@@ -22962,6 +23129,9 @@ namespace ts {
: undefined;
return objectType && getPropertyOfType(objectType, escapeLeadingUnderscores((node as StringLiteral | NumericLiteral).text));
+ case SyntaxKind.DefaultKeyword:
+ return getSymbolOfNode(node.parent);
+
default:
return undefined;
}
@@ -23271,7 +23441,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;
}
@@ -23581,7 +23751,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 {
@@ -23904,7 +24074,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) {
@@ -23926,13 +24096,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);
}
@@ -24494,7 +24657,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);
@@ -24810,7 +24973,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..f5e2a4069e4 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++) {
@@ -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..e273950424c 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
},
@@ -3696,7 +3704,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 4492c3ed474..7252a9f0c1d 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;
}
@@ -4108,7 +4158,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..ddffe876d80 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,18 @@ namespace ts {
}
function loadModuleFromNodeModulesFolder(extensions: Extensions, moduleName: string, nodeModulesFolder: string, nodeModulesFolderExists: boolean, failedLookupLocations: Push, state: ModuleResolutionState): Resolved | undefined {
+ const { top, rest } = getNameOfTopDirectory(moduleName);
+ const packageRootPath = combinePaths(nodeModulesFolder, top);
+ 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 getNameOfTopDirectory(name: string): { top: string, rest: string } {
+ const idx = name.indexOf(directorySeparator);
+ return idx === -1 ? { top: name, rest: "" } : { top: name.slice(0, idx), rest: name.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..f18d396e9dd 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));
}
@@ -1208,7 +1222,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 +1304,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 +1352,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 +1441,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 +1567,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 +1605,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 +1689,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 +1749,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 +1780,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 +1803,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);
}
}
}
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 e2d9977f302..5347d7caaf0 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;
export type EqualsGreaterThanToken = Token;
- export type EndOfFileToken = Token;
+ export type EndOfFileToken = Token & JSDocContainer;
export type AtToken = Token;
export type ReadonlyToken = Token;
export type AwaitKeywordToken = Token;
@@ -636,6 +672,7 @@ namespace ts {
export interface Decorator extends Node {
kind: SyntaxKind.Decorator;
+ parent?: NamedDeclaration;
expression: LeftHandSideExpression;
}
@@ -650,32 +687,34 @@ namespace ts {
expression?: Expression;
}
- export interface SignatureDeclaration extends NamedDeclaration {
- kind: SyntaxKind.CallSignature
- | SyntaxKind.ConstructSignature
- | SyntaxKind.MethodSignature
- | SyntaxKind.IndexSignature
- | SyntaxKind.FunctionType
- | SyntaxKind.ConstructorType
- | SyntaxKind.JSDocFunctionType
- | SyntaxKind.FunctionDeclaration
- | SyntaxKind.MethodDeclaration
- | SyntaxKind.Constructor
- | SyntaxKind.GetAccessor
- | SyntaxKind.SetAccessor
- | SyntaxKind.FunctionExpression
- | SyntaxKind.ArrowFunction;
+ export interface SignatureDeclarationBase extends NamedDeclaration, JSDocContainer {
+ kind: SignatureDeclaration["kind"];
name?: PropertyName;
typeParameters?: NodeArray;
parameters: NodeArray;
type: TypeNode | undefined;
}
- export interface CallSignatureDeclaration extends SignatureDeclaration, TypeElement {
+ export type SignatureDeclaration =
+ | CallSignatureDeclaration
+ | ConstructSignatureDeclaration
+ | MethodSignature
+ | IndexSignatureDeclaration
+ | FunctionTypeNode
+ | ConstructorTypeNode
+ | JSDocFunctionType
+ | FunctionDeclaration
+ | MethodDeclaration
+ | ConstructorDeclaration
+ | AccessorDeclaration
+ | FunctionExpression
+ | ArrowFunction;
+
+ export interface CallSignatureDeclaration extends SignatureDeclarationBase, TypeElement {
kind: SyntaxKind.CallSignature;
}
- export interface ConstructSignatureDeclaration extends SignatureDeclaration, TypeElement {
+ export interface ConstructSignatureDeclaration extends SignatureDeclarationBase, TypeElement {
kind: SyntaxKind.ConstructSignature;
}
@@ -695,7 +734,7 @@ namespace ts {
declarations: NodeArray;
}
- export interface ParameterDeclaration extends NamedDeclaration {
+ export interface ParameterDeclaration extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.Parameter;
parent?: SignatureDeclaration;
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
@@ -714,7 +753,7 @@ namespace ts {
initializer?: Expression; // Optional initializer
}
- export interface PropertySignature extends TypeElement {
+ export interface PropertySignature extends TypeElement, JSDocContainer {
kind: SyntaxKind.PropertySignature;
name: PropertyName; // Declared property name
questionToken?: QuestionToken; // Present on optional property
@@ -722,7 +761,7 @@ namespace ts {
initializer?: Expression; // Optional initializer
}
- export interface PropertyDeclaration extends ClassElement {
+ export interface PropertyDeclaration extends ClassElement, JSDocContainer {
kind: SyntaxKind.PropertyDeclaration;
questionToken?: QuestionToken; // Present for use with reporting a grammar error
name: PropertyName;
@@ -743,14 +782,16 @@ namespace ts {
| AccessorDeclaration
;
- export interface PropertyAssignment extends ObjectLiteralElement {
+ export interface PropertyAssignment extends ObjectLiteralElement, JSDocContainer {
+ parent: ObjectLiteralExpression;
kind: SyntaxKind.PropertyAssignment;
name: PropertyName;
questionToken?: QuestionToken;
initializer: Expression;
}
- export interface ShorthandPropertyAssignment extends ObjectLiteralElement {
+ export interface ShorthandPropertyAssignment extends ObjectLiteralElement, JSDocContainer {
+ parent: ObjectLiteralExpression;
kind: SyntaxKind.ShorthandPropertyAssignment;
name: Identifier;
questionToken?: QuestionToken;
@@ -760,7 +801,8 @@ namespace ts {
objectAssignmentInitializer?: Expression;
}
- export interface SpreadAssignment extends ObjectLiteralElement {
+ export interface SpreadAssignment extends ObjectLiteralElement, JSDocContainer {
+ parent: ObjectLiteralExpression;
kind: SyntaxKind.SpreadAssignment;
expression: Expression;
}
@@ -778,7 +820,7 @@ namespace ts {
export interface VariableLikeDeclaration extends NamedDeclaration {
propertyName?: PropertyName;
dotDotDotToken?: DotDotDotToken;
- name?: DeclarationName; // May be missing for ParameterDeclaration, see comment there
+ name: DeclarationName;
questionToken?: QuestionToken;
type?: TypeNode;
initializer?: Expression;
@@ -812,7 +854,7 @@ namespace ts {
* - MethodDeclaration
* - AccessorDeclaration
*/
- export interface FunctionLikeDeclarationBase extends SignatureDeclaration {
+ export interface FunctionLikeDeclarationBase extends SignatureDeclarationBase {
_functionLikeDeclarationBrand: any;
asteriskToken?: AsteriskToken;
@@ -843,7 +885,7 @@ namespace ts {
body?: FunctionBody;
}
- export interface MethodSignature extends SignatureDeclaration, TypeElement {
+ export interface MethodSignature extends SignatureDeclarationBase, TypeElement {
kind: SyntaxKind.MethodSignature;
name: PropertyName;
}
@@ -857,13 +899,13 @@ namespace ts {
// Because of this, it may be necessary to determine what sort of MethodDeclaration you have
// at later stages of the compiler pipeline. In that case, you can either check the parent kind
// of the method, or use helpers like isObjectLiteralMethodDeclaration
- export interface MethodDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement {
+ export interface MethodDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
kind: SyntaxKind.MethodDeclaration;
name: PropertyName;
body?: FunctionBody;
}
- export interface ConstructorDeclaration extends FunctionLikeDeclarationBase, ClassElement {
+ export interface ConstructorDeclaration extends FunctionLikeDeclarationBase, ClassElement, JSDocContainer {
kind: SyntaxKind.Constructor;
parent?: ClassDeclaration | ClassExpression;
body?: FunctionBody;
@@ -877,7 +919,7 @@ namespace ts {
// See the comment on MethodDeclaration for the intuition behind GetAccessorDeclaration being a
// ClassElement and an ObjectLiteralElement.
- export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement {
+ export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
kind: SyntaxKind.GetAccessor;
parent?: ClassDeclaration | ClassExpression | ObjectLiteralExpression;
name: PropertyName;
@@ -886,7 +928,7 @@ namespace ts {
// See the comment on MethodDeclaration for the intuition behind SetAccessorDeclaration being a
// ClassElement and an ObjectLiteralElement.
- export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement {
+ export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
kind: SyntaxKind.SetAccessor;
parent?: ClassDeclaration | ClassExpression | ObjectLiteralExpression;
name: PropertyName;
@@ -895,7 +937,7 @@ namespace ts {
export type AccessorDeclaration = GetAccessorDeclaration | SetAccessorDeclaration;
- export interface IndexSignatureDeclaration extends SignatureDeclaration, ClassElement, TypeElement {
+ export interface IndexSignatureDeclaration extends SignatureDeclarationBase, ClassElement, TypeElement {
kind: SyntaxKind.IndexSignature;
parent?: ClassDeclaration | ClassExpression | InterfaceDeclaration | TypeLiteralNode;
}
@@ -924,11 +966,11 @@ namespace ts {
export type FunctionOrConstructorTypeNode = FunctionTypeNode | ConstructorTypeNode;
- export interface FunctionTypeNode extends TypeNode, SignatureDeclaration {
+ export interface FunctionTypeNode extends TypeNode, SignatureDeclarationBase {
kind: SyntaxKind.FunctionType;
}
- export interface ConstructorTypeNode extends TypeNode, SignatureDeclaration {
+ export interface ConstructorTypeNode extends TypeNode, SignatureDeclarationBase {
kind: SyntaxKind.ConstructorType;
}
@@ -942,6 +984,7 @@ namespace ts {
export interface TypePredicateNode extends TypeNode {
kind: SyntaxKind.TypePredicate;
+ parent?: SignatureDeclaration;
parameterName: Identifier | ThisTypeNode;
type: TypeNode;
}
@@ -998,7 +1041,6 @@ namespace ts {
export interface MappedTypeNode extends TypeNode, Declaration {
kind: SyntaxKind.MappedType;
- parent?: TypeAliasDeclaration;
readonlyToken?: ReadonlyToken;
typeParameter: TypeParameterDeclaration;
questionToken?: QuestionToken;
@@ -1007,7 +1049,7 @@ namespace ts {
export interface LiteralTypeNode extends TypeNode {
kind: SyntaxKind.LiteralType;
- literal: Expression;
+ literal: BooleanLiteral | LiteralExpression | PrefixUnaryExpression;
}
export interface StringLiteral extends LiteralExpression {
@@ -1351,13 +1393,13 @@ namespace ts {
export type FunctionBody = Block;
export type ConciseBody = FunctionBody | Expression;
- export interface FunctionExpression extends PrimaryExpression, FunctionLikeDeclarationBase {
+ export interface FunctionExpression extends PrimaryExpression, FunctionLikeDeclarationBase, JSDocContainer {
kind: SyntaxKind.FunctionExpression;
name?: Identifier;
body: FunctionBody; // Required, whereas the member inherited from FunctionDeclaration is optional
}
- export interface ArrowFunction extends Expression, FunctionLikeDeclarationBase {
+ export interface ArrowFunction extends Expression, FunctionLikeDeclarationBase, JSDocContainer {
kind: SyntaxKind.ArrowFunction;
equalsGreaterThanToken: EqualsGreaterThanToken;
body: ConciseBody;
@@ -1436,7 +1478,7 @@ namespace ts {
literal: TemplateMiddle | TemplateTail;
}
- export interface ParenthesizedExpression extends PrimaryExpression {
+ export interface ParenthesizedExpression extends PrimaryExpression, JSDocContainer {
kind: SyntaxKind.ParenthesizedExpression;
expression: Expression;
}
@@ -1450,6 +1492,7 @@ namespace ts {
export interface SpreadElement extends Expression {
kind: SyntaxKind.SpreadElement;
+ parent?: ArrayLiteralExpression | CallExpression | NewExpression;
expression: Expression;
}
@@ -1691,12 +1734,12 @@ namespace ts {
/*@internal*/ multiLine?: boolean;
}
- export interface VariableStatement extends Statement {
+ export interface VariableStatement extends Statement, JSDocContainer {
kind: SyntaxKind.VariableStatement;
declarationList: VariableDeclarationList;
}
- export interface ExpressionStatement extends Statement {
+ export interface ExpressionStatement extends Statement, JSDocContainer {
kind: SyntaxKind.ExpressionStatement;
expression: Expression;
}
@@ -1802,7 +1845,7 @@ namespace ts {
export type CaseOrDefaultClause = CaseClause | DefaultClause;
- export interface LabeledStatement extends Statement {
+ export interface LabeledStatement extends Statement, JSDocContainer {
kind: SyntaxKind.LabeledStatement;
label: Identifier;
statement: Statement;
@@ -1829,7 +1872,7 @@ namespace ts {
export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag;
- export interface ClassLikeDeclaration extends NamedDeclaration {
+ export interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
name?: Identifier;
typeParameters?: NodeArray;
@@ -1837,15 +1880,17 @@ namespace ts {
members: NodeArray;
}
- export interface ClassDeclaration extends ClassLikeDeclaration, DeclarationStatement {
+ export interface ClassDeclaration extends ClassLikeDeclarationBase, DeclarationStatement {
kind: SyntaxKind.ClassDeclaration;
name?: Identifier;
}
- export interface ClassExpression extends ClassLikeDeclaration, PrimaryExpression {
+ export interface ClassExpression extends ClassLikeDeclarationBase, PrimaryExpression {
kind: SyntaxKind.ClassExpression;
}
+ export type ClassLikeDeclaration = ClassDeclaration | ClassExpression;
+
export interface ClassElement extends NamedDeclaration {
_classElementBrand: any;
name?: PropertyName;
@@ -1857,7 +1902,7 @@ namespace ts {
questionToken?: QuestionToken;
}
- export interface InterfaceDeclaration extends DeclarationStatement {
+ export interface InterfaceDeclaration extends DeclarationStatement, JSDocContainer {
kind: SyntaxKind.InterfaceDeclaration;
name: Identifier;
typeParameters?: NodeArray;
@@ -1872,14 +1917,14 @@ namespace ts {
types: NodeArray;
}
- export interface TypeAliasDeclaration extends DeclarationStatement {
+ export interface TypeAliasDeclaration extends DeclarationStatement, JSDocContainer {
kind: SyntaxKind.TypeAliasDeclaration;
name: Identifier;
typeParameters?: NodeArray;
type: TypeNode;
}
- export interface EnumMember extends NamedDeclaration {
+ export interface EnumMember extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.EnumMember;
parent?: EnumDeclaration;
// This does include ComputedPropertyName, but the parser will give an error
@@ -1888,7 +1933,7 @@ namespace ts {
initializer?: Expression;
}
- export interface EnumDeclaration extends DeclarationStatement {
+ export interface EnumDeclaration extends DeclarationStatement, JSDocContainer {
kind: SyntaxKind.EnumDeclaration;
name: Identifier;
members: NodeArray;
@@ -1898,7 +1943,7 @@ namespace ts {
export type ModuleBody = NamespaceBody | JSDocNamespaceBody;
- export interface ModuleDeclaration extends DeclarationStatement {
+ export interface ModuleDeclaration extends DeclarationStatement, JSDocContainer {
kind: SyntaxKind.ModuleDeclaration;
parent?: ModuleBody | SourceFile;
name: ModuleName;
@@ -1932,7 +1977,7 @@ namespace ts {
* - import x = require("mod");
* - import x = M.x;
*/
- export interface ImportEqualsDeclaration extends DeclarationStatement {
+ export interface ImportEqualsDeclaration extends DeclarationStatement, JSDocContainer {
kind: SyntaxKind.ImportEqualsDeclaration;
parent?: SourceFile | ModuleBlock;
name: Identifier;
@@ -2085,7 +2130,7 @@ namespace ts {
type: TypeNode;
}
- export interface JSDocFunctionType extends JSDocType, SignatureDeclaration {
+ export interface JSDocFunctionType extends JSDocType, SignatureDeclarationBase {
kind: SyntaxKind.JSDocFunctionType;
}
@@ -2098,6 +2143,7 @@ namespace ts {
export interface JSDoc extends Node {
kind: SyntaxKind.JSDocComment;
+ parent?: HasJSDoc;
tags: NodeArray | undefined;
comment: string | undefined;
}
@@ -2165,7 +2211,6 @@ namespace ts {
export interface JSDocTypeLiteral extends JSDocType {
kind: SyntaxKind.JSDocTypeLiteral;
jsDocPropertyTags?: ReadonlyArray;
- jsDocTypeTag?: JSDocTypeTag;
/** If true, then this type literal represents an *array* of its type. */
isArrayType?: boolean;
}
@@ -2468,6 +2513,8 @@ namespace ts {
/* @internal */ getFileProcessingDiagnostics(): DiagnosticCollection;
/* @internal */ getResolvedTypeReferenceDirectives(): Map;
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
+ /* @internal */ isSourceFileDefaultLibrary(file: SourceFile): boolean;
+
// For testing purposes only.
/* @internal */ structureIsReused?: StructureIsReused;
@@ -2658,7 +2705,8 @@ namespace ts {
* So for `{ a } | { b }`, this will include both `a` and `b`.
* Does not include properties of primitive types.
*/
- /* @internal */ getAllPossiblePropertiesOfType(type: Type): Symbol[];
+ /* @internal */ isArrayLikeType(type: Type): boolean;
+ /* @internal */ getAllPossiblePropertiesOfTypes(type: ReadonlyArray): Symbol[];
/* @internal */ resolveName(name: string, location: Node, meaning: SymbolFlags): Symbol | undefined;
/* @internal */ getJsxNamespace(): string;
}
@@ -3171,6 +3219,7 @@ namespace ts {
/* @internal */
Nullable = Undefined | Null,
Literal = StringLiteral | NumberLiteral | BooleanLiteral,
+ Unit = Literal | Nullable,
StringOrNumberLiteral = StringLiteral | NumberLiteral,
/* @internal */
DefinitelyFalsy = StringLiteral | NumberLiteral | BooleanLiteral | Void | Undefined | Null,
@@ -3447,6 +3496,8 @@ namespace ts {
/* @internal */
erasedSignatureCache?: Signature; // Erased version of signature (deferred)
/* @internal */
+ canonicalSignatureCache?: Signature; // Canonical version of signature (deferred)
+ /* @internal */
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
/* @internal */
typePredicate?: TypePredicate;
@@ -3581,7 +3632,7 @@ namespace ts {
name: string;
}
- export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[];
+ export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | null | undefined;
export interface CompilerOptions {
/*@internal*/ all?: boolean;
@@ -4007,6 +4058,11 @@ namespace ts {
* If accessing a non-index file, this should include its name e.g. "foo/bar".
*/
name: string;
+ /**
+ * Name of a submodule within this package.
+ * May be "".
+ */
+ subModuleName: string;
/** Version of the package, e.g. "1.2.3" */
version: string;
}
@@ -4030,6 +4086,7 @@ namespace ts {
primary: boolean;
// The location of the .d.ts file we located, or undefined if resolution failed
resolvedFileName?: string;
+ packageId?: PackageId;
}
export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
@@ -4251,10 +4308,11 @@ namespace ts {
}
export const enum EmitHint {
- SourceFile, // Emitting a SourceFile
- Expression, // Emitting an Expression
- IdentifierName, // Emitting an IdentifierName
- Unspecified, // Emitting an otherwise unspecified node
+ SourceFile, // Emitting a SourceFile
+ Expression, // Emitting an Expression
+ IdentifierName, // Emitting an IdentifierName
+ MappedTypeParameter, // Emitting a TypeParameterDeclaration inside of a MappedTypeNode
+ Unspecified, // Emitting an otherwise unspecified node
}
/* @internal */
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index a6ae7925a53..f3b40c702b6 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -32,7 +32,6 @@ namespace ts {
}
const stringWriter = createSingleLineStringWriter();
- let stringWriterAcquired = false;
function createSingleLineStringWriter(): StringSymbolWriter {
let str = "";
@@ -62,15 +61,14 @@ namespace ts {
}
export function usingSingleLineStringWriter(action: (writer: StringSymbolWriter) => void): string {
+ const oldString = stringWriter.string();
try {
- Debug.assert(!stringWriterAcquired);
- stringWriterAcquired = true;
action(stringWriter);
return stringWriter.string();
}
finally {
stringWriter.clear();
- stringWriterAcquired = false;
+ stringWriter.writeKeyword(oldString);
}
}
@@ -106,7 +104,7 @@ namespace ts {
}
function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
- return a === b || a && b && a.name === b.name && a.version === b.version;
+ return a === b || a && b && a.name === b.name && a.subModuleName === b.subModuleName && a.version === b.version;
}
export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean {
@@ -279,7 +277,7 @@ namespace ts {
return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
}
- if (includeJsDoc && node.jsDoc && node.jsDoc.length > 0) {
+ if (includeJsDoc && hasJSDocNodes(node)) {
return getTokenPosOfNode(node.jsDoc[0]);
}
@@ -502,13 +500,11 @@ namespace ts {
case SyntaxKind.ArrowFunction:
return true;
default:
- staticAssertNever(node);
+ assertTypeIsNever(node);
return false;
}
}
- function staticAssertNever(_: never): void {}
-
// Gets the nearest enclosing block scope container that has the provided node
// as a descendant, that is not the provided node.
export function getEnclosingBlockScopeContainer(node: Node): Node {
@@ -1257,57 +1253,60 @@ namespace ts {
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
case SyntaxKind.ThisKeyword:
- const parent = node.parent;
- switch (parent.kind) {
- case SyntaxKind.VariableDeclaration:
- case SyntaxKind.Parameter:
- case SyntaxKind.PropertyDeclaration:
- case SyntaxKind.PropertySignature:
- case SyntaxKind.EnumMember:
- case SyntaxKind.PropertyAssignment:
- case SyntaxKind.BindingElement:
- return (parent).initializer === node;
- case SyntaxKind.ExpressionStatement:
- case SyntaxKind.IfStatement:
- case SyntaxKind.DoStatement:
- case SyntaxKind.WhileStatement:
- case SyntaxKind.ReturnStatement:
- case SyntaxKind.WithStatement:
- case SyntaxKind.SwitchStatement:
- case SyntaxKind.CaseClause:
- case SyntaxKind.ThrowStatement:
- return (parent).expression === node;
- case SyntaxKind.ForStatement:
- const forStatement = parent;
- return (forStatement.initializer === node && forStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
- forStatement.condition === node ||
- forStatement.incrementor === node;
- case SyntaxKind.ForInStatement:
- case SyntaxKind.ForOfStatement:
- const forInStatement = parent;
- return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
- forInStatement.expression === node;
- case SyntaxKind.TypeAssertionExpression:
- case SyntaxKind.AsExpression:
- return node === (parent).expression;
- case SyntaxKind.TemplateSpan:
- return node === (parent).expression;
- case SyntaxKind.ComputedPropertyName:
- return node === (parent).expression;
- case SyntaxKind.Decorator:
- case SyntaxKind.JsxExpression:
- case SyntaxKind.JsxSpreadAttribute:
- case SyntaxKind.SpreadAssignment:
- return true;
- case SyntaxKind.ExpressionWithTypeArguments:
- return (parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(parent);
- default:
- if (isPartOfExpression(parent)) {
- return true;
- }
- }
+ return isInExpressionContext(node);
+ default:
+ return false;
+ }
+ }
+
+ export function isInExpressionContext(node: Node): boolean {
+ const parent = node.parent;
+ switch (parent.kind) {
+ case SyntaxKind.VariableDeclaration:
+ case SyntaxKind.Parameter:
+ case SyntaxKind.PropertyDeclaration:
+ case SyntaxKind.PropertySignature:
+ case SyntaxKind.EnumMember:
+ case SyntaxKind.PropertyAssignment:
+ case SyntaxKind.BindingElement:
+ return (parent).initializer === node;
+ case SyntaxKind.ExpressionStatement:
+ case SyntaxKind.IfStatement:
+ case SyntaxKind.DoStatement:
+ case SyntaxKind.WhileStatement:
+ case SyntaxKind.ReturnStatement:
+ case SyntaxKind.WithStatement:
+ case SyntaxKind.SwitchStatement:
+ case SyntaxKind.CaseClause:
+ case SyntaxKind.ThrowStatement:
+ return (parent).expression === node;
+ case SyntaxKind.ForStatement:
+ const forStatement = parent;
+ return (forStatement.initializer === node && forStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
+ forStatement.condition === node ||
+ forStatement.incrementor === node;
+ case SyntaxKind.ForInStatement:
+ case SyntaxKind.ForOfStatement:
+ const forInStatement = parent;
+ return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
+ forInStatement.expression === node;
+ case SyntaxKind.TypeAssertionExpression:
+ case SyntaxKind.AsExpression:
+ return node === (parent).expression;
+ case SyntaxKind.TemplateSpan:
+ return node === (parent).expression;
+ case SyntaxKind.ComputedPropertyName:
+ return node === (parent).expression;
+ case SyntaxKind.Decorator:
+ case SyntaxKind.JsxExpression:
+ case SyntaxKind.JsxSpreadAttribute:
+ case SyntaxKind.SpreadAssignment:
+ return true;
+ case SyntaxKind.ExpressionWithTypeArguments:
+ return (parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(parent);
+ default:
+ return isPartOfExpression(parent);
}
- return false;
}
export function isExternalModuleImportEqualsDeclaration(node: Node) {
@@ -1327,11 +1326,11 @@ namespace ts {
return isInJavaScriptFile(file);
}
- export function isInJavaScriptFile(node: Node): boolean {
+ export function isInJavaScriptFile(node: Node | undefined): boolean {
return node && !!(node.flags & NodeFlags.JavaScriptFile);
}
- export function isInJSDoc(node: Node): boolean {
+ export function isInJSDoc(node: Node | undefined): boolean {
return node && !!(node.flags & NodeFlags.JSDoc);
}
@@ -1551,11 +1550,13 @@ namespace ts {
result = addRange(result, getJSDocParameterTags(node as ParameterDeclaration));
}
- if (isVariableLike(node) && node.initializer) {
+ if (isVariableLike(node) && node.initializer && hasJSDocNodes(node.initializer)) {
result = addRange(result, node.initializer.jsDoc);
}
- result = addRange(result, node.jsDoc);
+ if (hasJSDocNodes(node)) {
+ result = addRange(result, node.jsDoc);
+ }
}
}
@@ -3444,6 +3445,46 @@ namespace ts {
export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags {
return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags;
}
+
+ export function isWriteOnlyAccess(node: Node) {
+ return accessKind(node) === AccessKind.Write;
+ }
+
+ export function isWriteAccess(node: Node) {
+ return accessKind(node) !== AccessKind.Read;
+ }
+
+ const enum AccessKind {
+ /** Only reads from a variable. */
+ Read,
+ /** Only writes to a variable without using the result. E.g.: `x++;`. */
+ Write,
+ /** Writes to a variable and uses the result as an expression. E.g.: `f(x++);`. */
+ ReadWrite
+ }
+ function accessKind(node: Node): AccessKind {
+ const { parent } = node;
+ if (!parent) return AccessKind.Read;
+
+ switch (parent.kind) {
+ case SyntaxKind.PostfixUnaryExpression:
+ case SyntaxKind.PrefixUnaryExpression:
+ const { operator } = parent as PrefixUnaryExpression | PostfixUnaryExpression;
+ return operator === SyntaxKind.PlusPlusToken || operator === SyntaxKind.MinusMinusToken ? writeOrReadWrite() : AccessKind.Read;
+ case SyntaxKind.BinaryExpression:
+ const { left, operatorToken } = parent as BinaryExpression;
+ return left === node && isAssignmentOperator(operatorToken.kind) ? writeOrReadWrite() : AccessKind.Read;
+ case SyntaxKind.PropertyAccessExpression:
+ return (parent as PropertyAccessExpression).name !== node ? AccessKind.Read : accessKind(parent);
+ default:
+ return AccessKind.Read;
+ }
+
+ function writeOrReadWrite(): AccessKind {
+ // If grandparent is not an ExpressionStatement, this is used as an expression in addition to having a side effect.
+ return parent.parent && parent.parent.kind === SyntaxKind.ExpressionStatement ? AccessKind.Write : AccessKind.ReadWrite;
+ }
+ }
}
namespace ts {
@@ -3900,7 +3941,66 @@ namespace ts {
return id;
}
- export function getNameOfDeclaration(declaration: Declaration): DeclarationName | undefined {
+ /**
+ * A JSDocTypedef tag has an _optional_ name field - if a name is not directly present, we should
+ * attempt to draw the name from the node the declaration is on (as that declaration is what its' symbol
+ * will be merged with)
+ */
+ function nameForNamelessJSDocTypedef(declaration: JSDocTypedefTag): Identifier | undefined {
+ const hostNode = declaration.parent.parent;
+ if (!hostNode) {
+ return undefined;
+ }
+ // Covers classes, functions - any named declaration host node
+ if (isDeclaration(hostNode)) {
+ return getDeclarationIdentifier(hostNode);
+ }
+ // Covers remaining cases
+ switch (hostNode.kind) {
+ case SyntaxKind.VariableStatement:
+ if ((hostNode as VariableStatement).declarationList &&
+ (hostNode as VariableStatement).declarationList.declarations[0]) {
+ return getDeclarationIdentifier((hostNode as VariableStatement).declarationList.declarations[0]);
+ }
+ return undefined;
+ case SyntaxKind.ExpressionStatement:
+ const expr = (hostNode as ExpressionStatement).expression;
+ switch (expr.kind) {
+ case SyntaxKind.PropertyAccessExpression:
+ return (expr as PropertyAccessExpression).name;
+ case SyntaxKind.ElementAccessExpression:
+ const arg = (expr as ElementAccessExpression).argumentExpression;
+ if (isIdentifier(arg)) {
+ return arg;
+ }
+ }
+ return undefined;
+ case SyntaxKind.EndOfFileToken:
+ return undefined;
+ case SyntaxKind.ParenthesizedExpression: {
+ return getDeclarationIdentifier(hostNode.expression);
+ }
+ case SyntaxKind.LabeledStatement: {
+ if (isDeclaration(hostNode.statement) || isExpression(hostNode.statement)) {
+ return getDeclarationIdentifier(hostNode.statement);
+ }
+ return undefined;
+ }
+ default:
+ Debug.assertNever(hostNode, "Found typedef tag attached to node which it should not be!");
+ }
+ }
+
+ function getDeclarationIdentifier(node: Declaration | Expression) {
+ const name = getNameOfDeclaration(node);
+ return isIdentifier(name) ? name : undefined;
+ }
+
+ export function getNameOfJSDocTypedef(declaration: JSDocTypedefTag): Identifier | undefined {
+ return declaration.name || nameForNamelessJSDocTypedef(declaration as JSDocTypedefTag);
+ }
+
+ export function getNameOfDeclaration(declaration: Declaration | Expression): DeclarationName | undefined {
if (!declaration) {
return undefined;
}
@@ -3919,6 +4019,9 @@ namespace ts {
return undefined;
}
}
+ else if (declaration.kind === SyntaxKind.JSDocTypedefTag) {
+ return getNameOfJSDocTypedef(declaration as JSDocTypedefTag);
+ }
else {
return (declaration as NamedDeclaration).name;
}
@@ -4017,10 +4120,10 @@ namespace ts {
/** Get all JSDoc tags related to a node, including those on parent nodes. */
export function getJSDocTags(node: Node): ReadonlyArray | undefined {
- let tags = node.jsDocCache;
+ let tags = (node as JSDocContainer).jsDocCache;
// If cache is 'null', that means we did the work of searching for JSDoc tags and came up with nothing.
if (tags === undefined) {
- node.jsDocCache = tags = flatMap(getJSDocCommentsAndTags(node), j => isJSDoc(j) ? j.tags : j);
+ (node as JSDocContainer).jsDocCache = tags = flatMap(getJSDocCommentsAndTags(node), j => isJSDoc(j) ? j.tags : j);
}
return tags;
}
@@ -4714,8 +4817,7 @@ namespace ts {
/* @internal */
export function isNodeArray(array: ReadonlyArray): array is NodeArray {
- return array.hasOwnProperty("pos")
- && array.hasOwnProperty("end");
+ return array.hasOwnProperty("pos") && array.hasOwnProperty("end");
}
// Literals
@@ -4816,16 +4918,29 @@ namespace ts {
}
/* @internal */
- export function isFunctionLikeKind(kind: SyntaxKind): boolean {
+ export function isFunctionLikeDeclaration(node: Node): node is FunctionLikeDeclaration {
+ return node && isFunctionLikeDeclarationKind(node.kind);
+ }
+
+ function isFunctionLikeDeclarationKind(kind: SyntaxKind): boolean {
switch (kind) {
- case SyntaxKind.Constructor:
- case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
- case SyntaxKind.ArrowFunction:
case SyntaxKind.MethodDeclaration:
- case SyntaxKind.MethodSignature:
+ case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
+ case SyntaxKind.FunctionExpression:
+ case SyntaxKind.ArrowFunction:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /* @internal */
+ export function isFunctionLikeKind(kind: SyntaxKind): boolean {
+ switch (kind) {
+ case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
@@ -4833,9 +4948,14 @@ namespace ts {
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.ConstructorType:
return true;
+ default:
+ return isFunctionLikeDeclarationKind(kind);
}
+ }
- return false;
+ /* @internal */
+ export function isFunctionOrModuleBlock(node: Node): boolean {
+ return isSourceFile(node) || isModuleBlock(node) || isBlock(node) && isFunctionLike(node.parent);
}
// Classes
@@ -5415,4 +5535,10 @@ namespace ts {
export function isJSDocTag(node: Node): boolean {
return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode;
}
+
+ /** True if has jsdoc nodes attached to it. */
+ /* @internal */
+ export function hasJSDocNodes(node: Node): node is HasJSDoc {
+ return !!(node as JSDocContainer).jsDoc && (node as JSDocContainer).jsDoc.length > 0;
+ }
}
diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts
index 1ce42199372..7d46630e227 100644
--- a/src/compiler/visitor.ts
+++ b/src/compiler/visitor.ts
@@ -488,6 +488,7 @@ namespace ts {
nodesVisitor((node).typeParameters, visitor, isTypeParameterDeclaration),
visitParameterList((node).parameters, visitor, context, nodesVisitor),
visitNode((node).type, visitor, isTypeNode),
+ visitNode((node).equalsGreaterThanToken, visitor, isToken),
visitFunctionBody((node).body, visitor, context));
case SyntaxKind.DeleteExpression:
@@ -523,7 +524,9 @@ namespace ts {
case SyntaxKind.ConditionalExpression:
return updateConditional(node,
visitNode((node).condition, visitor, isExpression),
+ visitNode((node).questionToken, visitor, isToken),
visitNode((node).whenTrue, visitor, isExpression),
+ visitNode((node).colonToken, visitor, isToken),
visitNode((node).whenFalse, visitor, isExpression));
case SyntaxKind.TemplateExpression:
diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts
index 170a23e34f2..dc3aa64c6ac 100644
--- a/src/harness/compilerRunner.ts
+++ b/src/harness/compilerRunner.ts
@@ -11,19 +11,13 @@ const enum CompilerTestType {
class CompilerBaselineRunner extends RunnerBase {
private basePath = "tests/cases";
private testSuiteName: TestRunnerKind;
- private errors: boolean;
private emit: boolean;
- private decl: boolean;
- private output: boolean;
public options: string;
constructor(public testType: CompilerTestType) {
super();
- this.errors = true;
this.emit = true;
- this.decl = true;
- this.output = true;
if (testType === CompilerTestType.Conformance) {
this.testSuiteName = "conformance";
}
@@ -141,7 +135,7 @@ class CompilerBaselineRunner extends RunnerBase {
// check errors
it("Correct errors for " + fileName, () => {
- Harness.Compiler.doErrorBaseline(justName, tsConfigFiles.concat(toBeCompiled, otherFiles), result.errors);
+ Harness.Compiler.doErrorBaseline(justName, tsConfigFiles.concat(toBeCompiled, otherFiles), result.errors, !!options.pretty);
});
it (`Correct module resolution tracing for ${fileName}`, () => {
@@ -214,26 +208,14 @@ class CompilerBaselineRunner extends RunnerBase {
private parseOptions() {
if (this.options && this.options.length > 0) {
- this.errors = false;
this.emit = false;
- this.decl = false;
- this.output = false;
const opts = this.options.split(",");
for (let i = 0; i < opts.length; i++) {
switch (opts[i]) {
- case "error":
- this.errors = true;
- break;
case "emit":
this.emit = true;
break;
- case "decl":
- this.decl = true;
- break;
- case "output":
- this.output = true;
- break;
default:
throw new Error("unsupported flag");
}
diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index 3010fc53533..64e25e0eb9a 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -22,10 +22,6 @@
namespace FourSlash {
ts.disableIncrementalParsing = false;
- function normalizeNewLines(s: string) {
- return s.replace(/\r\n/g, "\n");
- }
-
// Represents a parsed source file with metadata
export interface FourSlashFile {
// The contents of the file (with markers, etc stripped out)
@@ -364,7 +360,7 @@ namespace FourSlash {
baseIndentSize: 0,
indentSize: 4,
tabSize: 4,
- newLineCharacter: Harness.IO.newLine(),
+ newLineCharacter: "\n",
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
insertSpaceAfterCommaDelimiter: true,
@@ -762,7 +758,7 @@ namespace FourSlash {
}
}
- public verifyCompletionsAt(markerName: string, expected: string[]) {
+ public verifyCompletionsAt(markerName: string, expected: string[], options?: FourSlashInterface.CompletionsAtOptions) {
this.goToMarker(markerName);
const actualCompletions = this.getCompletionListAtCaret();
@@ -770,6 +766,10 @@ namespace FourSlash {
this.raiseError(`No completions at position '${this.currentCaretPosition}'.`);
}
+ if (options && options.isNewIdentifierLocation !== undefined && actualCompletions.isNewIdentifierLocation !== options.isNewIdentifierLocation) {
+ this.raiseError(`Expected 'isNewIdentifierLocation' to be ${options.isNewIdentifierLocation}, got ${actualCompletions.isNewIdentifierLocation}`);
+ }
+
const actual = actualCompletions.entries;
if (actual.length !== expected.length) {
@@ -1599,7 +1599,7 @@ namespace FourSlash {
}
}
- public printCurrentFileState(makeWhitespaceVisible: boolean, makeCaretVisible: boolean) {
+ public printCurrentFileState(showWhitespace: boolean, makeCaretVisible: boolean) {
for (const file of this.testData.files) {
const active = (this.activeFile === file);
Harness.IO.log(`=== Script (${file.fileName}) ${(active ? "(active, cursor at |)" : "")} ===`);
@@ -1607,8 +1607,8 @@ namespace FourSlash {
if (active) {
content = content.substr(0, this.currentCaretPosition) + (makeCaretVisible ? "|" : "") + content.substr(this.currentCaretPosition);
}
- if (makeWhitespaceVisible) {
- content = TestState.makeWhitespaceVisible(content);
+ if (showWhitespace) {
+ content = makeWhitespaceVisible(content);
}
Harness.IO.log(content);
}
@@ -2124,10 +2124,8 @@ namespace FourSlash {
public verifyCurrentFileContent(text: string) {
const actual = this.getFileContent(this.activeFile.fileName);
- if (normalizeNewLines(actual) !== normalizeNewLines(text)) {
- throw new Error("verifyCurrentFileContent\n" +
- "\tExpected: \"" + TestState.makeWhitespaceVisible(text) + "\"\n" +
- "\t Actual: \"" + TestState.makeWhitespaceVisible(actual) + "\"");
+ if (actual !== text) {
+ throw new Error(`verifyCurrentFileContent failed:\n${showTextDiff(text, actual)}`);
}
}
@@ -2301,11 +2299,11 @@ namespace FourSlash {
const actualText = this.rangeText(ranges[0]);
const result = includeWhiteSpace
- ? normalizeNewLines(actualText) === normalizeNewLines(expectedText)
+ ? actualText === expectedText
: this.removeWhitespace(actualText) === this.removeWhitespace(expectedText);
if (!result) {
- this.raiseError(`Actual text doesn't match expected text. Actual:\n'${actualText}'\nExpected:\n'${expectedText}'`);
+ this.raiseError(`Actual range text doesn't match expected text.\n${showTextDiff(expectedText, actualText)}`);
}
}
@@ -2399,18 +2397,22 @@ namespace FourSlash {
const originalContent = scriptInfo.content;
for (const codeFix of codeFixes) {
this.applyEdits(codeFix.changes[0].fileName, codeFix.changes[0].textChanges, /*isFormattingEdit*/ false);
- actualTextArray.push(this.normalizeNewlines(this.rangeText(ranges[0])));
+ let text = this.rangeText(ranges[0]);
+ // TODO:GH#18445 (remove this line to see errors in many `importNameCodeFix` tests)
+ text = text.replace(/\r\n/g, "\n");
+ actualTextArray.push(text);
scriptInfo.updateContent(originalContent);
}
- const sortedExpectedArray = ts.map(expectedTextArray, str => this.normalizeNewlines(str)).sort();
+ const sortedExpectedArray = expectedTextArray.sort();
const sortedActualArray = actualTextArray.sort();
- if (!ts.arrayIsEqualTo(sortedExpectedArray, sortedActualArray)) {
- this.raiseError(
- `Actual text array doesn't match expected text array. \nActual: \n'${sortedActualArray.join("\n\n")}'\n---\nExpected: \n'${sortedExpectedArray.join("\n\n")}'`);
- }
+ ts.zipWith(sortedExpectedArray, sortedActualArray, (expected, actual, index) => {
+ if (expected !== actual) {
+ this.raiseError(`Import fix at index ${index} doesn't match.\n${showTextDiff(expected, actual)}`);
+ }
+ });
}
- public verifyDocCommentTemplate(expected?: ts.TextInsertion) {
+ public verifyDocCommentTemplate(expected: ts.TextInsertion | undefined) {
const name = "verifyDocCommentTemplate";
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition);
@@ -2427,7 +2429,7 @@ namespace FourSlash {
}
if (actual.newText !== expected.newText) {
- this.raiseError(`${name} failed - expected insertion:\n"${this.clarifyNewlines(expected.newText)}"\nactual insertion:\n"${this.clarifyNewlines(actual.newText)}"`);
+ this.raiseError(`${name} failed for expected insertion.\n${showTextDiff(expected.newText, actual.newText)}`);
}
if (actual.caretOffset !== expected.caretOffset) {
@@ -2436,17 +2438,6 @@ namespace FourSlash {
}
}
- private clarifyNewlines(str: string) {
- return str.replace(/\r?\n/g, lineEnding => {
- const representation = lineEnding === "\r\n" ? "CRLF" : "LF";
- return "# - " + representation + lineEnding;
- });
- }
-
- private normalizeNewlines(str: string) {
- return str.replace(/\r?\n/g, "\n");
- }
-
public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) {
const openBraceMap = ts.createMapFromTemplate