Merge branch 'master' into glob2_merged

This commit is contained in:
Richard Knoll
2016-06-17 15:34:06 -07:00
653 changed files with 290179 additions and 274327 deletions
+1 -1
View File
@@ -34,7 +34,6 @@ tests/webhost/*.d.ts
tests/webhost/webtsc.js
tests/cases/**/*.js
tests/cases/**/*.js.map
tests/cases/**/*.d.ts
*.config
scripts/debug.bat
scripts/run.bat
@@ -49,3 +48,4 @@ internal/
**/.vs
**/.vscode
!**/.vscode/tasks.json
!tests/cases/projects/projectOption/**/node_modules
+2 -2
View File
@@ -91,10 +91,10 @@ These two files represent the DOM typings and are auto-generated. To make any mo
## Running the Tests
To run all tests, invoke the `runtests` target using jake:
To run all tests, invoke the `runtests-parallel` target using jake:
```Shell
jake runtests
jake runtests-parallel
```
This run will all tests; to run only a specific subset of tests, use:
+120 -64
View File
@@ -5,6 +5,7 @@ var os = require("os");
var path = require("path");
var child_process = require("child_process");
var Linter = require("tslint");
var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel;
// Variables
var compilerDirectory = "src/compiler/";
@@ -155,6 +156,7 @@ var harnessSources = harnessCoreSources.concat([
"commandLineParsing.ts",
"convertCompilerOptionsFromJson.ts",
"convertTypingOptionsFromJson.ts",
"tsserverProjectSystem.ts",
"matchFiles.ts"
].map(function (f) {
return path.join(unittestsDirectory, f);
@@ -189,7 +191,10 @@ var es2016LibrarySourceMap = es2016LibrarySource.map(function (source) {
return { target: "lib." + source, sources: ["header.d.ts", source] };
});
var es2017LibrarySource = ["es2017.object.d.ts"];
var es2017LibrarySource = [
"es2017.object.d.ts",
"es2017.sharedmemory.d.ts"
];
var es2017LibrarySourceMap = es2017LibrarySource.map(function (source) {
return { target: "lib." + source, sources: ["header.d.ts", source] };
@@ -209,7 +214,7 @@ var librarySourceMap = [
{ target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] },
{ target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] },
{ target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] },
// JavaScript + all host library
{ target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) },
{ target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }
@@ -273,6 +278,7 @@ var builtLocalCompiler = path.join(builtLocalDirectory, compilerFilename);
* @param {boolean} opts.noResolve: true if compiler should not include non-rooted files in compilation
* @param {boolean} opts.stripInternal: true if compiler should remove declarations marked as @internal
* @param {boolean} opts.noMapRoot: true if compiler omit mapRoot option
* @param {boolean} opts.inlineSourceMap: true if compiler should inline sourceMap
* @param callback: a function to execute after the compilation process ends
*/
function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts, callback) {
@@ -310,10 +316,16 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts
}
if (useDebugMode) {
options += " -sourcemap";
if (!opts.noMapRoot) {
options += " -mapRoot file:///" + path.resolve(path.dirname(outFile));
if (opts.inlineSourceMap) {
options += " --inlineSourceMap --inlineSources";
} else {
options += " -sourcemap";
if (!opts.noMapRoot) {
options += " -mapRoot file:///" + path.resolve(path.dirname(outFile));
}
}
} else {
options += " --newLine LF";
}
if (opts.stripInternal) {
@@ -490,7 +502,13 @@ var nodeStandaloneDefinitionsFile = path.join(builtLocalDirectory, "typescript_s
compileFile(servicesFile, servicesSources,[builtLocalDirectory, copyright].concat(servicesSources),
/*prefixes*/ [copyright],
/*useBuiltCompiler*/ true,
{ noOutFile: false, generateDeclarations: true, preserveConstEnums: true, keepComments: true, noResolve: false, stripInternal: true },
/*opts*/ { noOutFile: false,
generateDeclarations: true,
preserveConstEnums: true,
keepComments: true,
noResolve: false,
stripInternal: true
},
/*callback*/ function () {
jake.cpR(servicesFile, nodePackageFile, {silent: true});
@@ -513,16 +531,21 @@ compileFile(servicesFile, servicesSources,[builtLocalDirectory, copyright].conca
fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents);
});
compileFile(servicesFileInBrowserTest, servicesSources,[builtLocalDirectory, copyright].concat(servicesSources),
/*prefixes*/ [copyright],
/*useBuiltCompiler*/ true,
{ noOutFile: false, generateDeclarations: true, preserveConstEnums: true, keepComments: true, noResolve: false, stripInternal: true, noMapRoot: true },
/*callback*/ function () {
var content = fs.readFileSync(servicesFileInBrowserTest).toString();
var i = content.lastIndexOf("\n");
fs.writeFileSync(servicesFileInBrowserTest, content.substring(0, i) + "\r\n//# sourceURL=../built/local/typeScriptServices.js" + content.substring(i));
});
compileFile(
servicesFileInBrowserTest,
servicesSources,
[builtLocalDirectory, copyright].concat(servicesSources),
/*prefixes*/ [copyright],
/*useBuiltCompiler*/ true,
{ noOutFile: false,
generateDeclarations: true,
preserveConstEnums: true,
keepComments: true,
noResolve: false,
stripInternal: true,
noMapRoot: true,
inlineSourceMap: true
});
var serverFile = path.join(builtLocalDirectory, "tsserver.js");
compileFile(serverFile, serverSources,[builtLocalDirectory, copyright].concat(serverSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true);
@@ -623,7 +646,13 @@ directory(builtLocalDirectory);
// Task to build the tests infrastructure using the built compiler
var run = path.join(builtLocalDirectory, "run.js");
compileFile(run, harnessSources, [builtLocalDirectory, tscFile].concat(libraryTargets).concat(harnessSources), [], /*useBuiltCompiler:*/ true);
compileFile(
/*outFile*/ run,
/*source*/ harnessSources,
/*prereqs*/ [builtLocalDirectory, tscFile].concat(libraryTargets).concat(harnessSources),
/*prefixes*/ [],
/*useBuiltCompiler:*/ true,
/*opts*/ { inlineSourceMap: true });
var internalTests = "internal/";
@@ -682,9 +711,8 @@ function cleanTestDirs() {
}
// used to pass data from jake command line directly to run.js
function writeTestConfigFile(tests, light, testConfigFile) {
console.log('Running test(s): ' + tests);
var testConfigContents = JSON.stringify({ test: [tests], light: light });
function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount) {
var testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light: light, workerCount: workerCount, taskConfigsFolder: taskConfigsFolder });
fs.writeFileSync('test.config', testConfigContents);
}
@@ -694,7 +722,7 @@ function deleteTemporaryProjectOutput() {
}
}
function runConsoleTests(defaultReporter, defaultSubsets) {
function runConsoleTests(defaultReporter, runInParallel) {
cleanTestDirs();
var debug = process.env.debug || process.env.d;
tests = process.env.test || process.env.tests || process.env.t;
@@ -703,9 +731,22 @@ function runConsoleTests(defaultReporter, defaultSubsets) {
if(fs.existsSync(testConfigFile)) {
fs.unlinkSync(testConfigFile);
}
var workerCount, taskConfigsFolder;
if (runInParallel) {
// generate name to store task configuration files
var prefix = os.tmpdir() + "/ts-tests";
var i = 1;
do {
taskConfigsFolder = prefix + i;
i++;
} while (fs.existsSync(taskConfigsFolder));
fs.mkdirSync(taskConfigsFolder);
if (tests || light) {
writeTestConfigFile(tests, light, testConfigFile);
workerCount = process.env.workerCount || os.cpus().length;
}
if (tests || light || taskConfigsFolder) {
writeTestConfigFile(tests, light, taskConfigsFolder, workerCount);
}
if (tests && tests.toLocaleLowerCase() === "rwc") {
@@ -719,61 +760,76 @@ function runConsoleTests(defaultReporter, defaultSubsets) {
// timeout normally isn't necessary but Travis-CI has been timing out on compiler baselines occasionally
// default timeout is 2sec which really should be enough, but maybe we just need a small amount longer
var subsetRegexes;
if(defaultSubsets.length === 0) {
subsetRegexes = [tests];
}
else {
var subsets = tests ? tests.split("|") : defaultSubsets;
subsetRegexes = subsets.map(function (sub) { return "^" + sub + ".*$"; });
subsetRegexes.push("^(?!" + subsets.join("|") + ").*$");
}
var counter = subsetRegexes.length;
var errorStatus;
subsetRegexes.forEach(function (subsetRegex, i) {
tests = subsetRegex ? ' -g "' + subsetRegex + '"' : '';
if(!runInParallel) {
tests = tests ? ' -g "' + tests + '"' : '';
var cmd = "mocha" + (debug ? " --debug-brk" : "") + " -R " + reporter + tests + colors + ' -t ' + testTimeout + ' ' + run;
console.log(cmd);
function finish(status) {
counter--;
// save first error status
if (status !== undefined && errorStatus === undefined) {
errorStatus = status;
}
deleteTemporaryProjectOutput();
if (counter !== 0 || errorStatus === undefined) {
// run linter when last worker is finished
if (lintFlag && counter === 0) {
var lint = jake.Task['lint'];
lint.addListener('complete', function () {
complete();
});
lint.invoke();
}
complete();
}
else {
fail("Process exited with code " + status);
}
}
var savedNodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = "development";
exec(cmd, function () {
process.env.NODE_ENV = savedNodeEnv;
runLinter();
finish();
}, function(e, status) {
process.env.NODE_ENV = savedNodeEnv;
finish(status);
});
});
}
else {
var savedNodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = "development";
runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function (err) {
process.env.NODE_ENV = savedNodeEnv;
// 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();
}
});
}
function failWithStatus(status) {
fail("Process exited with code " + status);
}
function finish(errorStatus) {
deleteTemporaryProjectOutput();
if (errorStatus !== undefined) {
failWithStatus(errorStatus);
}
else {
complete();
}
}
function runLinter() {
if (!lintFlag) {
return;
}
var lint = jake.Task['lint'];
lint.addListener('complete', function () {
complete();
});
lint.invoke();
}
}
var testTimeout = 20000;
desc("Runs all the tests in parallel using the built run.js file. Optional arguments are: t[ests]=category1|category2|... d[ebug]=true.");
task("runtests-parallel", ["build-rules", "tests", builtLocalDirectory], function() {
runConsoleTests('min', ['compiler', 'conformance', 'Projects', 'fourslash']);
runConsoleTests('min', /*runInParallel*/ true);
}, {async: true});
desc("Runs the tests using the built run.js file. Optional arguments are: t[ests]=regex r[eporter]=[list|spec|json|<more>] d[ebug]=true color[s]=false lint=true.");
task("runtests", ["build-rules", "tests", builtLocalDirectory], function() {
runConsoleTests('mocha-fivemat-progress-reporter', []);
runConsoleTests('mocha-fivemat-progress-reporter', /*runInParallel*/ false);
}, {async: true});
desc("Generates code coverage data via instanbul");
@@ -790,7 +846,7 @@ compileFile(nodeServerOutFile, [nodeServerInFile], [builtLocalDirectory, tscFile
desc("Runs browserify on run.js to produce a file suitable for running tests in the browser");
task("browserify", ["tests", builtLocalDirectory, nodeServerOutFile], function() {
var cmd = 'browserify built/local/run.js -o built/local/bundle.js';
var cmd = 'browserify built/local/run.js -d -o built/local/bundle.js';
exec(cmd);
}, {async: true});
@@ -807,11 +863,11 @@ task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFi
fs.unlinkSync(testConfigFile);
}
if(tests || light) {
writeTestConfigFile(tests, light, testConfigFile);
writeTestConfigFile(tests, light);
}
tests = tests ? tests : '';
var cmd = host + " tests/webTestServer.js " + port + " " + browser + " " + tests;
var cmd = host + " tests/webTestServer.js " + port + " " + browser + " " + JSON.stringify(tests);
console.log(cmd);
exec(cmd);
}, {async: true});
+13 -1
View File
@@ -152,12 +152,24 @@ interface ObjectConstructor {
*/
getOwnPropertyNames(o: any): string[];
/**
* Creates an object that has null prototype.
* @param o Object to use as a prototype. May be null
*/
create(o: null): any;
/**
* Creates an object that has the specified prototype, and that optionally contains specified properties.
* @param o Object to use as a prototype. May be null
*/
create<T>(o: T): T;
/**
* Creates an object that has the specified prototype, and that optionally contains specified properties.
* @param o Object to use as a prototype. May be null
* @param properties JavaScript object that contains one or more property descriptors.
*/
create(o: any, properties?: PropertyDescriptorMap): any;
create(o: any, properties: PropertyDescriptorMap): any;
/**
* Adds a property to an object, or modifies attributes of an existing property.
+2 -1
View File
@@ -15,4 +15,5 @@ and limitations under the License.
/// <reference no-default-lib="true"/>
/// <reference path="lib.es2016.d.ts" />
/// <reference path="lib.es2017.object.d.ts" />
/// <reference path="lib.es2017.object.d.ts" />
/// <reference path="lib.es2017.sharedmemory.d.ts" />
+43
View File
@@ -0,0 +1,43 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference no-default-lib="true"/>
/// <reference path="lib.es2015.symbol.d.ts" />
/// <reference path="lib.es2015.symbol.wellknown.d.ts" />
interface SharedArrayBuffer {
/**
* Read-only. The length of the ArrayBuffer (in bytes).
*/
readonly byteLength: number;
/*
* The SharedArrayBuffer constructor's length property whose value is 1.
*/
length: number;
/**
* Returns a section of an SharedArrayBuffer.
*/
slice(begin:number, end?:number): SharedArrayBuffer;
readonly [Symbol.species]: SharedArrayBuffer;
readonly [Symbol.toStringTag]: "SharedArrayBuffer";
}
interface SharedArrayBufferConstructor {
readonly prototype: SharedArrayBuffer;
new (byteLength: number): SharedArrayBuffer;
}
declare var SharedArrayBuffer: SharedArrayBufferConstructor;
+13 -1
View File
@@ -152,12 +152,24 @@ interface ObjectConstructor {
*/
getOwnPropertyNames(o: any): string[];
/**
* Creates an object that has null prototype.
* @param o Object to use as a prototype. May be null
*/
create(o: null): any;
/**
* Creates an object that has the specified prototype, and that optionally contains specified properties.
* @param o Object to use as a prototype. May be null
*/
create<T>(o: T): T;
/**
* Creates an object that has the specified prototype, and that optionally contains specified properties.
* @param o Object to use as a prototype. May be null
* @param properties JavaScript object that contains one or more property descriptors.
*/
create(o: any, properties?: PropertyDescriptorMap): any;
create(o: any, properties: PropertyDescriptorMap): any;
/**
* Adds a property to an object, or modifies attributes of an existing property.
+13 -1
View File
@@ -152,12 +152,24 @@ interface ObjectConstructor {
*/
getOwnPropertyNames(o: any): string[];
/**
* Creates an object that has null prototype.
* @param o Object to use as a prototype. May be null
*/
create(o: null): any;
/**
* Creates an object that has the specified prototype, and that optionally contains specified properties.
* @param o Object to use as a prototype. May be null
*/
create<T>(o: T): T;
/**
* Creates an object that has the specified prototype, and that optionally contains specified properties.
* @param o Object to use as a prototype. May be null
* @param properties JavaScript object that contains one or more property descriptors.
*/
create(o: any, properties?: PropertyDescriptorMap): any;
create(o: any, properties: PropertyDescriptorMap): any;
/**
* Adds a property to an object, or modifies attributes of an existing property.
+38042 -37654
View File
File diff suppressed because one or more lines are too long
+52451 -51851
View File
File diff suppressed because one or more lines are too long
+8715 -8648
View File
File diff suppressed because it is too large Load Diff
+52217 -51617
View File
File diff suppressed because one or more lines are too long
+2508 -2488
View File
File diff suppressed because it is too large Load Diff
+58975 -58311
View File
File diff suppressed because one or more lines are too long
+2508 -2488
View File
File diff suppressed because it is too large Load Diff
+58975 -58311
View File
File diff suppressed because one or more lines are too long
+26
View File
@@ -0,0 +1,26 @@
/**
* 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;
+359
View File
@@ -0,0 +1,359 @@
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,
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
});
rl.on("line", onmessage);
p.on("exit", onexit)
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() {
if (partition.end === undefined) {
partition.end = Date.now();
}
partition.duration = partition.end - partition.start;
var summaryColor = partition.failed ? "fail" : "green";
var summarySymbol = partition.failed ? Base.symbols.err : Base.symbols.ok;
var summaryTests = (partition.passed === partition.tests ? partition.passed : partition.passed + "/" + partition.tests) + " 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;
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;
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 (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)
}
};
}
}
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;
}
+250 -187
View File
@@ -53,7 +53,8 @@ namespace ts {
return state;
}
else if (node.kind === SyntaxKind.ModuleDeclaration) {
return getModuleInstanceState((<ModuleDeclaration>node).body);
const body = (<ModuleDeclaration>node).body;
return body ? getModuleInstanceState(body) : ModuleInstanceState.Instantiated;
}
else {
return ModuleInstanceState.Instantiated;
@@ -77,12 +78,14 @@ namespace ts {
// Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements...
IsBlockScopedContainer = 1 << 1,
HasLocals = 1 << 2,
// The current node is the container of a control flow path. The current control flow should
// be saved and restored, and a new control flow initialized within the container.
IsControlFlowContainer = 1 << 2,
// If the current node is a container that also container that also contains locals. Examples:
//
// Functions, Methods, Modules, Source-files.
IsContainerWithLocals = IsContainer | HasLocals
IsFunctionLike = 1 << 3,
IsFunctionExpression = 1 << 4,
HasLocals = 1 << 5,
IsInterface = 1 << 6,
}
const binder = createBinder();
@@ -103,22 +106,19 @@ namespace ts {
let lastContainer: Node;
let seenThisKeyword: boolean;
// state used by reachability checks
let hasExplicitReturn: boolean;
// state used by control flow analysis
let currentFlow: FlowNode;
let currentBreakTarget: FlowLabel;
let currentContinueTarget: FlowLabel;
let currentReturnTarget: FlowLabel;
let currentTrueTarget: FlowLabel;
let currentFalseTarget: FlowLabel;
let preSwitchCaseFlow: FlowNode;
let activeLabels: ActiveLabel[];
let hasExplicitReturn: boolean;
// state used for emit helpers
let hasClassExtends: boolean;
let hasAsyncFunctions: boolean;
let hasDecorators: boolean;
let hasParameterDecorators: boolean;
let hasJsxSpreadAttribute: boolean;
let emitFlags: NodeFlags;
// If this file is an external module, then it is automatically in strict-mode according to
// ES6. If it is not an external module, then we'll determine if it is in strict mode or
@@ -156,18 +156,15 @@ namespace ts {
blockScopeContainer = undefined;
lastContainer = undefined;
seenThisKeyword = false;
hasExplicitReturn = false;
currentFlow = undefined;
currentBreakTarget = undefined;
currentContinueTarget = undefined;
currentReturnTarget = undefined;
currentTrueTarget = undefined;
currentFalseTarget = undefined;
activeLabels = undefined;
hasClassExtends = false;
hasAsyncFunctions = false;
hasDecorators = false;
hasParameterDecorators = false;
hasJsxSpreadAttribute = false;
hasExplicitReturn = false;
emitFlags = NodeFlags.None;
}
return bindSourceFile;
@@ -267,6 +264,18 @@ namespace ts {
let functionType = <JSDocFunctionType>node.parent;
let index = indexOf(functionType.parameters, node);
return "p" + index;
case SyntaxKind.JSDocTypedefTag:
const parentNode = node.parent && node.parent.parent;
let nameFromParentNode: string;
if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) {
if ((<VariableStatement>parentNode).declarationList.declarations.length > 0) {
const nameIdentifier = (<VariableStatement>parentNode).declarationList.declarations[0].name;
if (nameIdentifier.kind === SyntaxKind.Identifier) {
nameFromParentNode = (<Identifier>nameIdentifier).text;
}
}
}
return nameFromParentNode;
}
}
@@ -400,17 +409,13 @@ namespace ts {
// All container nodes are kept on a linked list in declaration order. This list is used by
// the getLocalNameOfContainer function in the type checker to validate that the local name
// used for a container is unique.
function bindChildren(node: Node) {
function bindContainer(node: Node, containerFlags: ContainerFlags) {
// Before we recurse into a node's children, we first save the existing parent, container
// and block-container. Then after we pop out of processing the children, we restore
// these saved values.
const saveParent = parent;
const saveContainer = container;
const savedBlockScopeContainer = blockScopeContainer;
// This node will now be set as the parent of all of its children as we recurse into them.
parent = node;
// Depending on what kind of node this is, we may have to adjust the current container
// and block-container. If the current node is a container, then it is automatically
// considered the current block-container as well. Also, for containers that we know
@@ -428,115 +433,90 @@ namespace ts {
// reusing a node from a previous compilation, that node may have had 'locals' created
// for it. We must clear this so we don't accidentally move any stale data forward from
// a previous compilation.
const containerFlags = getContainerFlags(node);
if (containerFlags & ContainerFlags.IsContainer) {
container = blockScopeContainer = node;
if (containerFlags & ContainerFlags.HasLocals) {
container.locals = {};
}
addToContainerChain(container);
}
else if (containerFlags & ContainerFlags.IsBlockScopedContainer) {
blockScopeContainer = node;
blockScopeContainer.locals = undefined;
}
let savedHasExplicitReturn: boolean;
let savedCurrentFlow: FlowNode;
let savedBreakTarget: FlowLabel;
let savedContinueTarget: FlowLabel;
let savedActiveLabels: ActiveLabel[];
const kind = node.kind;
let flags = node.flags;
// reset all reachability check related flags on node (for incremental scenarios)
flags &= ~NodeFlags.ReachabilityCheckFlags;
// reset all emit helper flags on node (for incremental scenarios)
flags &= ~NodeFlags.EmitHelperFlags;
if (kind === SyntaxKind.InterfaceDeclaration) {
seenThisKeyword = false;
}
const saveState = kind === SyntaxKind.SourceFile || kind === SyntaxKind.ModuleBlock || isFunctionLikeKind(kind);
if (saveState) {
savedHasExplicitReturn = hasExplicitReturn;
savedCurrentFlow = currentFlow;
savedBreakTarget = currentBreakTarget;
savedContinueTarget = currentContinueTarget;
savedActiveLabels = activeLabels;
hasExplicitReturn = false;
currentFlow = { flags: FlowFlags.Start };
if (containerFlags & ContainerFlags.IsControlFlowContainer) {
const saveCurrentFlow = currentFlow;
const saveBreakTarget = currentBreakTarget;
const saveContinueTarget = currentContinueTarget;
const saveReturnTarget = currentReturnTarget;
const saveActiveLabels = activeLabels;
const saveHasExplicitReturn = hasExplicitReturn;
const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !!getImmediatelyInvokedFunctionExpression(node);
// An IIFE is considered part of the containing control flow. Return statements behave
// similarly to break statements that exit to a label just past the statement body.
if (isIIFE) {
currentReturnTarget = createBranchLabel();
}
else {
currentFlow = { flags: FlowFlags.Start };
if (containerFlags & ContainerFlags.IsFunctionExpression) {
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction>node;
}
currentReturnTarget = undefined;
}
currentBreakTarget = undefined;
currentContinueTarget = undefined;
activeLabels = undefined;
}
if (isInJavaScriptFile(node) && node.jsDocComment) {
bind(node.jsDocComment);
}
bindReachableStatement(node);
if (!(currentFlow.flags & FlowFlags.Unreachable) && isFunctionLikeKind(kind) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
flags |= NodeFlags.HasImplicitReturn;
if (hasExplicitReturn) {
flags |= NodeFlags.HasExplicitReturn;
hasExplicitReturn = false;
bindChildren(node);
// Reset all reachability check related flags on node (for incremental scenarios)
// Reset all emit helper flags on node (for incremental scenarios)
node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
node.flags |= NodeFlags.HasImplicitReturn;
if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn;
}
if (node.kind === SyntaxKind.SourceFile) {
node.flags |= emitFlags;
}
if (isIIFE) {
addAntecedent(currentReturnTarget, currentFlow);
currentFlow = finishFlowLabel(currentReturnTarget);
}
else {
currentFlow = saveCurrentFlow;
}
currentBreakTarget = saveBreakTarget;
currentContinueTarget = saveContinueTarget;
currentReturnTarget = saveReturnTarget;
activeLabels = saveActiveLabels;
hasExplicitReturn = saveHasExplicitReturn;
}
if (kind === SyntaxKind.InterfaceDeclaration) {
flags = seenThisKeyword ? flags | NodeFlags.ContainsThis : flags & ~NodeFlags.ContainsThis;
else if (containerFlags & ContainerFlags.IsInterface) {
seenThisKeyword = false;
bindChildren(node);
node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis;
}
if (kind === SyntaxKind.SourceFile) {
if (hasClassExtends) {
flags |= NodeFlags.HasClassExtends;
}
if (hasDecorators) {
flags |= NodeFlags.HasDecorators;
}
if (hasParameterDecorators) {
flags |= NodeFlags.HasParamDecorators;
}
if (hasAsyncFunctions) {
flags |= NodeFlags.HasAsyncFunctions;
}
if (hasJsxSpreadAttribute) {
flags |= NodeFlags.HasJsxSpreadAttribute;
}
else {
bindChildren(node);
}
node.flags = flags;
if (saveState) {
hasExplicitReturn = savedHasExplicitReturn;
currentFlow = savedCurrentFlow;
currentBreakTarget = savedBreakTarget;
currentContinueTarget = savedContinueTarget;
activeLabels = savedActiveLabels;
}
container = saveContainer;
parent = saveParent;
blockScopeContainer = savedBlockScopeContainer;
}
/**
* Returns true if node and its subnodes were successfully traversed.
* Returning false means that node was not examined and caller needs to dive into the node himself.
*/
function bindReachableStatement(node: Node): void {
function bindChildren(node: Node): void {
// 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 (isInJavaScriptFile(node) && node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
bind(jsDocComment);
}
}
if (checkUnreachable(node)) {
forEachChild(node, bind);
return;
}
switch (node.kind) {
case SyntaxKind.WhileStatement:
bindWhileStatement(<WhileStatement>node);
@@ -589,6 +569,9 @@ namespace ts {
case SyntaxKind.VariableDeclaration:
bindVariableDeclarationFlow(<VariableDeclaration>node);
break;
case SyntaxKind.CallExpression:
bindCallExpressionFlow(<CallExpression>node);
break;
default:
forEachChild(node, bind);
break;
@@ -627,10 +610,11 @@ namespace ts {
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
if (isNarrowingExpression(expr.left) && (expr.right.kind === SyntaxKind.NullKeyword || expr.right.kind === SyntaxKind.Identifier)) {
if ((isNarrowingExpression(expr.left) && (expr.right.kind === SyntaxKind.NullKeyword || expr.right.kind === SyntaxKind.Identifier)) ||
(isNarrowingExpression(expr.right) && (expr.left.kind === SyntaxKind.NullKeyword || expr.left.kind === SyntaxKind.Identifier))) {
return true;
}
if (expr.left.kind === SyntaxKind.TypeOfExpression && isNarrowingExpression((<TypeOfExpression>expr.left).expression) && expr.right.kind === SyntaxKind.StringLiteral) {
if (isTypeOfNarrowingBinaryExpression(expr)) {
return true;
}
return false;
@@ -642,6 +626,20 @@ namespace ts {
return false;
}
function isTypeOfNarrowingBinaryExpression(expr: BinaryExpression) {
let typeOf: Expression;
if (expr.left.kind === SyntaxKind.StringLiteral) {
typeOf = expr.right;
}
else if (expr.right.kind === SyntaxKind.StringLiteral) {
typeOf = expr.left;
}
else {
typeOf = undefined;
}
return typeOf && typeOf.kind === SyntaxKind.TypeOfExpression && isNarrowingExpression((<TypeOfExpression>typeOf).expression);
}
function createBranchLabel(): FlowLabel {
return {
flags: FlowFlags.BranchLabel,
@@ -848,6 +846,9 @@ namespace ts {
bind(node.expression);
if (node.kind === SyntaxKind.ReturnStatement) {
hasExplicitReturn = true;
if (currentReturnTarget) {
addAntecedent(currentReturnTarget, currentFlow);
}
}
currentFlow = unreachableFlow;
}
@@ -912,8 +913,8 @@ namespace ts {
preSwitchCaseFlow = currentFlow;
bind(node.caseBlock);
addAntecedent(postSwitchLabel, currentFlow);
const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause);
if (!hasDefault) {
const hasNonEmptyDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause && c.statements.length);
if (!hasNonEmptyDefault) {
addAntecedent(postSwitchLabel, preSwitchCaseFlow);
}
currentBreakTarget = saveBreakTarget;
@@ -1098,35 +1099,67 @@ namespace ts {
}
}
function bindCallExpressionFlow(node: CallExpression) {
// If the target of the call expression is a function expression or arrow function we have
// an immediately invoked function expression (IIFE). Initialize the flowNode property to
// the current control flow (which includes evaluation of the IIFE arguments).
let expr: Expression = node.expression;
while (expr.kind === SyntaxKind.ParenthesizedExpression) {
expr = (<ParenthesizedExpression>expr).expression;
}
if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) {
forEach(node.typeArguments, bind);
forEach(node.arguments, bind);
bind(node.expression);
}
else {
forEachChild(node, bind);
}
}
function getContainerFlags(node: Node): ContainerFlags {
switch (node.kind) {
case SyntaxKind.ClassExpression:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.TypeLiteral:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocRecordType:
return ContainerFlags.IsContainer;
case SyntaxKind.InterfaceDeclaration:
return ContainerFlags.IsContainer | ContainerFlags.IsInterface;
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.TypeAliasDeclaration:
return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
case SyntaxKind.SourceFile:
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals;
case SyntaxKind.Constructor:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionType:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.ConstructorType:
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.SourceFile:
case SyntaxKind.TypeAliasDeclaration:
return ContainerFlags.IsContainerWithLocals;
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression;
case SyntaxKind.ModuleBlock:
return ContainerFlags.IsControlFlowContainer;
case SyntaxKind.PropertyDeclaration:
return (<PropertyDeclaration>node).initializer ? ContainerFlags.IsControlFlowContainer : 0;
case SyntaxKind.CatchClause:
case SyntaxKind.ForStatement:
@@ -1166,9 +1199,9 @@ namespace ts {
lastContainer = next;
}
function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void {
function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol {
// Just call this directly so that the return type of this function stays "void".
declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes);
return declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes);
}
function declareSymbolAndAddToSymbolTableWorker(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol {
@@ -1194,6 +1227,7 @@ namespace ts {
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.JSDocRecordType:
case SyntaxKind.JSDocTypeLiteral:
// Interface/Object-types always have their children added to the 'members' of
// their container. They are only accessible through an instance of their
// container, and are never in scope otherwise (even inside the body of the
@@ -1222,7 +1256,7 @@ namespace ts {
// their container in the tree. To accomplish this, we simply add their declared
// symbol to the 'locals' of the container. These symbols can then be found as
// the type checker walks up the containers, checking them for matching names.
return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes);
return declareSymbol(container.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
}
}
@@ -1240,7 +1274,7 @@ namespace ts {
function hasExportDeclarations(node: ModuleDeclaration | SourceFile): boolean {
const body = node.kind === SyntaxKind.SourceFile ? node : (<ModuleDeclaration>node).body;
if (body.kind === SyntaxKind.SourceFile || body.kind === SyntaxKind.ModuleBlock) {
if (body && (body.kind === SyntaxKind.SourceFile || body.kind === SyntaxKind.ModuleBlock)) {
for (const stat of (<Block>body).statements) {
if (stat.kind === SyntaxKind.ExportDeclaration || stat.kind === SyntaxKind.ExportAssignment) {
return true;
@@ -1271,7 +1305,22 @@ namespace ts {
declareSymbolAndAddToSymbolTable(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes);
}
else {
declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes);
let pattern: Pattern | undefined;
if (node.name.kind === SyntaxKind.StringLiteral) {
const text = (<StringLiteral>node.name).text;
if (hasZeroOrOneAsteriskCharacter(text)) {
pattern = tryParsePattern(text);
}
else {
errorOnFirstToken(node.name, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, text);
}
}
const symbol = declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes);
if (pattern) {
(file.patternAmbientModules || (file.patternAmbientModules = [])).push({ pattern, symbol });
}
}
}
else {
@@ -1560,15 +1609,9 @@ namespace ts {
if (!node) {
return;
}
node.parent = parent;
const savedInStrictMode = inStrictMode;
if (!savedInStrictMode) {
updateStrictMode(node);
}
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
const saveInStrictMode = inStrictMode;
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
// and then potentially add the symbol to an appropriate symbol table. Possible
// destination symbol tables are:
//
@@ -1576,47 +1619,40 @@ namespace ts {
// 2) The 'members' table of the current container's symbol.
// 3) The 'locals' table of the current container.
//
// However, not all symbols will end up in any of these tables. 'Anonymous' symbols
// However, not all symbols will end up in any of these tables. 'Anonymous' symbols
// (like TypeLiterals for example) will not be put in any table.
bindWorker(node);
// Then we recurse into the children of the node to bind them as well. For certain
// symbols we do specialized work when we recurse. For example, we'll keep track of
// the current 'container' node when it changes. This helps us know which symbol table
// a local should go into for example.
bindChildren(node);
inStrictMode = savedInStrictMode;
}
function updateStrictMode(node: Node) {
switch (node.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.ModuleBlock:
updateStrictModeStatementList((<SourceFile | ModuleBlock>node).statements);
return;
case SyntaxKind.Block:
if (isFunctionLike(node.parent)) {
updateStrictModeStatementList((<Block>node).statements);
}
return;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
// All classes are automatically in strict mode in ES6.
inStrictMode = true;
return;
// Then we recurse into the children of the node to bind them as well. For certain
// symbols we do specialized work when we recurse. For example, we'll keep track of
// the current 'container' node when it changes. This helps us know which symbol table
// a local should go into for example. Since terminal nodes are known not to have
// children, as an optimization we don't process those.
if (node.kind > SyntaxKind.LastToken) {
const saveParent = parent;
parent = node;
const containerFlags = getContainerFlags(node);
if (containerFlags === ContainerFlags.None) {
bindChildren(node);
}
else {
bindContainer(node, containerFlags);
}
parent = saveParent;
}
inStrictMode = saveInStrictMode;
}
function updateStrictModeStatementList(statements: NodeArray<Statement>) {
for (const statement of statements) {
if (!isPrologueDirective(statement)) {
return;
}
if (!inStrictMode) {
for (const statement of statements) {
if (!isPrologueDirective(statement)) {
return;
}
if (isUseStrictPrologueDirective(<ExpressionStatement>statement)) {
inStrictMode = true;
return;
if (isUseStrictPrologueDirective(<ExpressionStatement>statement)) {
inStrictMode = true;
return;
}
}
}
}
@@ -1696,6 +1732,8 @@ namespace ts {
case SyntaxKind.PropertySignature:
case SyntaxKind.JSDocRecordMember:
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property | ((<PropertyDeclaration>node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
case SyntaxKind.JSDocPropertyTag:
return bindJSDocProperty(<JSDocPropertyTag>node);
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
@@ -1703,7 +1741,7 @@ namespace ts {
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes);
case SyntaxKind.JsxSpreadAttribute:
hasJsxSpreadAttribute = true;
emitFlags |= NodeFlags.HasJsxSpreadAttribute;
return;
case SyntaxKind.CallSignature:
@@ -1731,6 +1769,7 @@ namespace ts {
case SyntaxKind.JSDocFunctionType:
return bindFunctionOrConstructorType(<SignatureDeclaration>node);
case SyntaxKind.TypeLiteral:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocRecordType:
return bindAnonymousDeclaration(<TypeLiteralNode>node, SymbolFlags.TypeLiteral, "__type");
case SyntaxKind.ObjectLiteralExpression:
@@ -1748,9 +1787,12 @@ namespace ts {
// Members of classes, interfaces, and modules
case SyntaxKind.ClassExpression:
case SyntaxKind.ClassDeclaration:
// All classes are automatically in strict mode in ES6.
inStrictMode = true;
return bindClassLikeDeclaration(<ClassLikeDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
return bindBlockScopedDeclaration(<Declaration>node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes);
case SyntaxKind.JSDocTypedefTag:
case SyntaxKind.TypeAliasDeclaration:
return bindBlockScopedDeclaration(<Declaration>node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
case SyntaxKind.EnumDeclaration:
@@ -1773,7 +1815,15 @@ namespace ts {
case SyntaxKind.ExportAssignment:
return bindExportAssignment(<ExportAssignment>node);
case SyntaxKind.SourceFile:
updateStrictModeStatementList((<SourceFile>node).statements);
return bindSourceFileIfExternalModule();
case SyntaxKind.Block:
if (!isFunctionLike(node.parent)) {
return;
}
// Fall through
case SyntaxKind.ModuleBlock:
return updateStrictModeStatementList((<Block | ModuleBlock>node).statements);
}
}
@@ -1880,12 +1930,20 @@ namespace ts {
}
function bindThisPropertyAssignment(node: BinaryExpression) {
// Declare a 'member' in case it turns out the container was an ES5 class
if (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.FunctionDeclaration) {
container.symbol.members = container.symbol.members || {};
// It's acceptable for multiple 'this' assignments of the same identifier to occur
declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
// Declare a 'member' in case it turns out the container was an ES5 class or ES6 constructor
let assignee: Node;
if (container.kind === SyntaxKind.FunctionDeclaration || container.kind === SyntaxKind.FunctionDeclaration) {
assignee = container;
}
else if (container.kind === SyntaxKind.Constructor) {
assignee = container.parent;
}
else {
return;
}
assignee.symbol.members = assignee.symbol.members || {};
// It's acceptable for multiple 'this' assignments of the same identifier to occur
declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
}
function bindPrototypePropertyAssignment(node: BinaryExpression) {
@@ -1927,10 +1985,10 @@ namespace ts {
function bindClassLikeDeclaration(node: ClassLikeDeclaration) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (getClassExtendsHeritageClauseElement(node) !== undefined) {
hasClassExtends = true;
emitFlags |= NodeFlags.HasClassExtends;
}
if (nodeIsDecorated(node)) {
hasDecorators = true;
emitFlags |= NodeFlags.HasDecorators;
}
}
@@ -2006,8 +2064,7 @@ namespace ts {
if (!isDeclarationFile(file) &&
!isInAmbientContext(node) &&
nodeIsDecorated(node)) {
hasDecorators = true;
hasParameterDecorators = true;
emitFlags |= (NodeFlags.HasDecorators | NodeFlags.HasParamDecorators);
}
if (inStrictMode) {
@@ -2034,27 +2091,29 @@ namespace ts {
function bindFunctionDeclaration(node: FunctionDeclaration) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
emitFlags |= NodeFlags.HasAsyncFunctions;
}
}
checkStrictModeFunctionName(<FunctionDeclaration>node);
if (inStrictMode) {
checkStrictModeFunctionDeclaration(node);
return bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);
bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);
}
else {
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);
declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);
}
}
function bindFunctionExpression(node: FunctionExpression) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
emitFlags |= NodeFlags.HasAsyncFunctions;
}
}
if (currentFlow) {
node.flowNode = currentFlow;
}
checkStrictModeFunctionName(<FunctionExpression>node);
const bindingName = (<FunctionExpression>node).name ? (<FunctionExpression>node).name.text : "__function";
return bindAnonymousDeclaration(<FunctionExpression>node, SymbolFlags.Function, bindingName);
@@ -2063,10 +2122,10 @@ namespace ts {
function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
emitFlags |= NodeFlags.HasAsyncFunctions;
}
if (nodeIsDecorated(node)) {
hasDecorators = true;
emitFlags |= NodeFlags.HasDecorators;
}
}
@@ -2075,6 +2134,10 @@ namespace ts {
: declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
}
function bindJSDocProperty(node: JSDocPropertyTag) {
return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
}
// reachability checks
function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean {
+259 -168
View File
@@ -110,16 +110,16 @@ namespace ts {
const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
const nullableWideningFlags = strictNullChecks ? 0 : TypeFlags.ContainsUndefinedOrNull;
const anyType = createIntrinsicType(TypeFlags.Any, "any");
const stringType = createIntrinsicType(TypeFlags.String, "string");
const numberType = createIntrinsicType(TypeFlags.Number, "number");
const booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean");
const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
const voidType = createIntrinsicType(TypeFlags.Void, "void");
const undefinedType = createIntrinsicType(TypeFlags.Undefined | nullableWideningFlags, "undefined");
const nullType = createIntrinsicType(TypeFlags.Null | nullableWideningFlags, "null");
const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined");
const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
const nullType = createIntrinsicType(TypeFlags.Null, "null");
const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
const neverType = createIntrinsicType(TypeFlags.Never, "never");
@@ -140,6 +140,12 @@ namespace ts {
const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true);
const globals: SymbolTable = {};
/**
* List of every ambient module with a "*" wildcard.
* Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches.
* This is only used if there is no exact match.
*/
let patternAmbientModules: PatternAmbientModule[];
let getGlobalESSymbolConstructorSymbol: () => Symbol;
@@ -986,7 +992,9 @@ namespace ts {
const moduleSymbol = resolveExternalModuleName(node, (<ImportDeclaration>node.parent).moduleSpecifier);
if (moduleSymbol) {
const exportDefaultSymbol = moduleSymbol.exports["export="] ?
const exportDefaultSymbol = isShorthandAmbientModule(moduleSymbol.valueDeclaration) ?
moduleSymbol :
moduleSymbol.exports["export="] ?
getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") :
resolveSymbol(moduleSymbol.exports["default"]);
@@ -1060,6 +1068,10 @@ namespace ts {
if (targetSymbol) {
const name = specifier.propertyName || specifier.name;
if (name.text) {
if (isShorthandAmbientModule(moduleSymbol.valueDeclaration)) {
return moduleSymbol;
}
let symbolFromVariable: Symbol;
// First check if module was specified with "export=". If so, get the member from the resolved type
if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) {
@@ -1180,11 +1192,7 @@ namespace ts {
}
// This function is only for imports with entity names
function getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, importDeclaration?: ImportEqualsDeclaration): Symbol {
if (!importDeclaration) {
importDeclaration = <ImportEqualsDeclaration>getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration);
Debug.assert(importDeclaration !== undefined);
}
function getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, importDeclaration: ImportEqualsDeclaration, dontResolveAlias?: boolean): Symbol {
// There are three things we might try to look for. In the following examples,
// the search term is enclosed in |...|:
//
@@ -1196,13 +1204,13 @@ namespace ts {
}
// Check for case 1 and 3 in the above example
if (entityName.kind === SyntaxKind.Identifier || entityName.parent.kind === SyntaxKind.QualifiedName) {
return resolveEntityName(entityName, SymbolFlags.Namespace);
return resolveEntityName(entityName, SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
}
else {
// Case 2 in above example
// entityName.kind could be a QualifiedName or a Missing identifier
Debug.assert(entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration);
return resolveEntityName(entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
return resolveEntityName(entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
}
}
@@ -1211,7 +1219,7 @@ namespace ts {
}
// Resolves a qualified name and any involved aliases
function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean): Symbol {
function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol {
if (nodeIsMissing(name)) {
return undefined;
}
@@ -1245,7 +1253,7 @@ namespace ts {
Debug.fail("Unknown entity name kind.");
}
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
return symbol.flags & meaning ? symbol : resolveAlias(symbol);
return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol);
}
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol {
@@ -1289,6 +1297,14 @@ namespace ts {
}
return undefined;
}
if (patternAmbientModules) {
const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleName);
if (pattern) {
return getMergedSymbol(pattern.symbol);
}
}
if (moduleNotFoundError) {
// report errors only if it was requested
error(moduleReferenceLiteral, moduleNotFoundError, moduleName);
@@ -2848,7 +2864,7 @@ namespace ts {
}
// In strict null checking mode, if a default value of a non-undefined type is specified, remove
// undefined from the final type.
if (strictNullChecks && declaration.initializer && !(getNullableKind(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) {
if (strictNullChecks && declaration.initializer && !(getCombinedTypeFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) {
type = getTypeWithFacts(type, TypeFacts.NEUndefined);
}
return type;
@@ -2891,7 +2907,7 @@ namespace ts {
}
function addOptionality(type: Type, optional: boolean): Type {
return strictNullChecks && optional ? addNullableKind(type, TypeFlags.Undefined) : type;
return strictNullChecks && optional ? addTypeKind(type, TypeFlags.Undefined) : type;
}
// Return the inferred type for a variable, parameter, or property declaration
@@ -3224,9 +3240,14 @@ namespace ts {
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
const type = createObjectType(TypeFlags.Anonymous, symbol);
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ?
addNullableKind(type, TypeFlags.Undefined) : type;
if (symbol.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && isShorthandAmbientModule(<ModuleDeclaration>symbol.valueDeclaration)) {
links.type = anyType;
}
else {
const type = createObjectType(TypeFlags.Anonymous, symbol);
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ?
addTypeKind(type, TypeFlags.Undefined) : type;
}
}
return links.type;
}
@@ -3409,7 +3430,7 @@ namespace ts {
error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol));
return type.resolvedBaseConstructorType = unknownType;
}
if (baseConstructorType !== unknownType && baseConstructorType !== nullType && !isConstructorType(baseConstructorType)) {
if (baseConstructorType !== unknownType && baseConstructorType !== nullWideningType && !isConstructorType(baseConstructorType)) {
error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, typeToString(baseConstructorType));
return type.resolvedBaseConstructorType = unknownType;
}
@@ -3585,8 +3606,22 @@ namespace ts {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) {
return unknownType;
}
const declaration = <TypeAliasDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
let type = getTypeFromTypeNode(declaration.type);
let type: Type;
let declaration: JSDocTypedefTag | TypeAliasDeclaration = <JSDocTypedefTag>getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag);
if (declaration) {
if (declaration.jsDocTypeLiteral) {
type = getTypeFromTypeNode(declaration.jsDocTypeLiteral);
}
else {
type = getTypeFromTypeNode(declaration.typeExpression.type);
}
}
else {
declaration = <TypeAliasDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
type = getTypeFromTypeNode(declaration.type);
}
if (popTypeResolution()) {
links.typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
if (links.typeParameters) {
@@ -5001,6 +5036,7 @@ namespace ts {
containsAny?: boolean;
containsUndefined?: boolean;
containsNull?: boolean;
containsNonWideningType?: boolean;
}
function addTypeToSet(typeSet: TypeSet, type: Type, typeSetKind: TypeFlags) {
@@ -5011,6 +5047,7 @@ namespace ts {
if (type.flags & TypeFlags.Any) typeSet.containsAny = true;
if (type.flags & TypeFlags.Undefined) typeSet.containsUndefined = true;
if (type.flags & TypeFlags.Null) typeSet.containsNull = true;
if (!(type.flags & TypeFlags.ContainsWideningType)) typeSet.containsNonWideningType = true;
}
else if (type !== neverType && !contains(typeSet, type)) {
typeSet.push(type);
@@ -5071,8 +5108,8 @@ namespace ts {
removeSubtypes(typeSet);
}
if (typeSet.length === 0) {
return typeSet.containsNull ? nullType :
typeSet.containsUndefined ? undefinedType :
return typeSet.containsNull ? typeSet.containsNonWideningType ? nullType : nullWideningType :
typeSet.containsUndefined ? typeSet.containsNonWideningType ? undefinedType : undefinedWideningType :
neverType;
}
else if (typeSet.length === 1) {
@@ -5257,6 +5294,7 @@ namespace ts {
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.TypeLiteral:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.JSDocRecordType:
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
@@ -5871,7 +5909,7 @@ namespace ts {
if (!(target.flags & TypeFlags.Never)) {
if (target.flags & TypeFlags.Any || source.flags & TypeFlags.Never) return Ternary.True;
if (source.flags & TypeFlags.Undefined) {
if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void) || source === emptyArrayElementType) return Ternary.True;
if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void)) return Ternary.True;
}
if (source.flags & TypeFlags.Null) {
if (!strictNullChecks || target.flags & TypeFlags.Null) return Ternary.True;
@@ -6034,9 +6072,10 @@ namespace ts {
function isKnownProperty(type: Type, name: string): boolean {
if (type.flags & TypeFlags.ObjectType) {
const resolved = resolveStructuredTypeMembers(type);
if ((relation === assignableRelation || relation === comparableRelation) &&
(type === globalObjectType || isEmptyObjectType(resolved)) ||
resolved.stringIndexInfo || resolved.numberIndexInfo || getPropertyOfType(type, name)) {
if ((relation === assignableRelation || relation === comparableRelation) && (type === globalObjectType || isEmptyObjectType(resolved)) ||
resolved.stringIndexInfo ||
(resolved.numberIndexInfo && isNumericLiteralName(name)) ||
getPropertyOfType(type, name)) {
return true;
}
}
@@ -6732,7 +6771,7 @@ namespace ts {
return getUnionType(types);
}
const supertype = forEach(primaryTypes, t => isSupertypeOfEach(t, primaryTypes) ? t : undefined);
return supertype && addNullableKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable);
return supertype && addTypeKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable);
}
function reportNoCommonSupertypeError(types: Type[], errorLocation: Node, errorMessageChainHead: DiagnosticMessageChain): void {
@@ -6803,28 +6842,22 @@ namespace ts {
return !!(type.flags & TypeFlags.Tuple);
}
function getNullableKind(type: Type): TypeFlags {
let flags = type.flags;
if (flags & TypeFlags.Union) {
for (const t of (type as UnionType).types) {
flags |= t.flags;
}
}
return flags & TypeFlags.Nullable;
function getCombinedTypeFlags(type: Type): TypeFlags {
return type.flags & TypeFlags.Union ? getCombinedFlagsOfTypes((<UnionType>type).types) : type.flags;
}
function addNullableKind(type: Type, kind: TypeFlags): Type {
if ((getNullableKind(type) & kind) !== kind) {
const types = [type];
if (kind & TypeFlags.Undefined) {
types.push(undefinedType);
}
if (kind & TypeFlags.Null) {
types.push(nullType);
}
type = getUnionType(types);
function addTypeKind(type: Type, kind: TypeFlags) {
if ((getCombinedTypeFlags(type) & kind) === kind) {
return type;
}
return type;
const types = [type];
if (kind & TypeFlags.String) types.push(stringType);
if (kind & TypeFlags.Number) types.push(numberType);
if (kind & TypeFlags.Boolean) types.push(booleanType);
if (kind & TypeFlags.Void) types.push(voidType);
if (kind & TypeFlags.Undefined) types.push(undefinedType);
if (kind & TypeFlags.Null) types.push(nullType);
return getUnionType(types);
}
function getNonNullableType(type: Type): Type {
@@ -6960,7 +6993,7 @@ namespace ts {
if (type.flags & TypeFlags.ObjectLiteral) {
for (const p of getPropertiesOfObjectType(type)) {
const t = getTypeOfSymbol(p);
if (t.flags & TypeFlags.ContainsUndefinedOrNull) {
if (t.flags & TypeFlags.ContainsWideningType) {
if (!reportWideningErrorsInType(t)) {
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t)));
}
@@ -7007,7 +7040,7 @@ namespace ts {
}
function reportErrorsFromWidening(declaration: Declaration, type: Type) {
if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsUndefinedOrNull) {
if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsWideningType) {
// Report implicit any error within type if possible, otherwise report error on declaration
if (!reportWideningErrorsInType(type)) {
reportImplicitAnyError(declaration, type);
@@ -7648,12 +7681,27 @@ namespace ts {
getInitialTypeOfBindingElement(<BindingElement>node);
}
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean) {
function getReferenceFromExpression(node: Expression): Expression {
switch (node.kind) {
case SyntaxKind.ParenthesizedExpression:
return getReferenceFromExpression((<ParenthesizedExpression>node).expression);
case SyntaxKind.BinaryExpression:
switch ((<BinaryExpression>node).operatorToken.kind) {
case SyntaxKind.EqualsToken:
return getReferenceFromExpression((<BinaryExpression>node).left);
case SyntaxKind.CommaToken:
return getReferenceFromExpression((<BinaryExpression>node).right);
}
}
return node;
}
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) {
let key: string;
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
return declaredType;
}
const initialType = assumeInitialized ? declaredType : addNullableKind(declaredType, TypeFlags.Undefined);
const initialType = assumeInitialized ? declaredType : addTypeKind(declaredType, TypeFlags.Undefined);
const visitedFlowStart = visitedFlowCount;
const result = getTypeAtFlowNode(reference.flowNode);
visitedFlowCount = visitedFlowStart;
@@ -7694,15 +7742,21 @@ namespace ts {
getTypeAtFlowBranchLabel(<FlowLabel>flow) :
getTypeAtFlowLoopLabel(<FlowLabel>flow);
}
else if (flow.flags & FlowFlags.Unreachable) {
else if (flow.flags & FlowFlags.Start) {
// Check if we should continue with the control flow of the containing function.
const container = (<FlowStart>flow).container;
if (container && includeOuterFunctions) {
flow = container.flowNode;
continue;
}
// At the top of the flow we have the initial type.
type = initialType;
}
else {
// Unreachable code errors are reported in the binding phase. Here we
// simply return the declared type to reduce follow-on errors.
type = declaredType;
}
else {
// At the top of the flow we have the initial type.
type = initialType;
}
if (flow.flags & FlowFlags.Shared) {
// Record visited node and the associated type in the cache.
visitedFlowNodes[visitedFlowCount] = flow;
@@ -7841,10 +7895,11 @@ namespace ts {
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
if (isNullOrUndefinedLiteral(expr.right)) {
if (isNullOrUndefinedLiteral(expr.left) || isNullOrUndefinedLiteral(expr.right)) {
return narrowTypeByNullCheck(type, expr, assumeTrue);
}
if (expr.left.kind === SyntaxKind.TypeOfExpression && expr.right.kind === SyntaxKind.StringLiteral) {
if (expr.left.kind === SyntaxKind.TypeOfExpression && expr.right.kind === SyntaxKind.StringLiteral ||
expr.left.kind === SyntaxKind.StringLiteral && expr.right.kind === SyntaxKind.TypeOfExpression) {
return narrowTypeByTypeof(type, expr, assumeTrue);
}
break;
@@ -7857,18 +7912,20 @@ namespace ts {
}
function narrowTypeByNullCheck(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
// We have '==', '!=', '===', or '!==' operator with 'null' or 'undefined' on the right
// We have '==', '!=', '===', or '!==' operator with 'null' or 'undefined' on one side
const operator = expr.operatorToken.kind;
const nullLike = isNullOrUndefinedLiteral(expr.left) ? expr.left : expr.right;
const narrowed = isNullOrUndefinedLiteral(expr.left) ? expr.right : expr.left;
if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
assumeTrue = !assumeTrue;
}
if (!strictNullChecks || !isMatchingReference(reference, expr.left)) {
if (!strictNullChecks || !isMatchingReference(reference, getReferenceFromExpression(narrowed))) {
return type;
}
const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken;
const facts = doubleEquals ?
assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull :
expr.right.kind === SyntaxKind.NullKeyword ?
nullLike.kind === SyntaxKind.NullKeyword ?
assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull :
assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined;
return getTypeWithFacts(type, facts);
@@ -7877,12 +7934,12 @@ namespace ts {
function narrowTypeByTypeof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
// We have '==', '!=', '====', or !==' operator with 'typeof xxx' on the left
// and string literal on the right
const left = <TypeOfExpression>expr.left;
const right = <LiteralExpression>expr.right;
if (!isMatchingReference(reference, left.expression)) {
const narrowed = getReferenceFromExpression((<TypeOfExpression>(expr.left.kind === SyntaxKind.TypeOfExpression ? expr.left : expr.right)).expression);
const literal = <LiteralExpression>(expr.right.kind === SyntaxKind.StringLiteral ? expr.right : expr.left);
if (!isMatchingReference(reference, narrowed)) {
// For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the
// narrowed type of 'y' to its declared type.
if (containsMatchingReference(reference, left.expression)) {
if (containsMatchingReference(reference, narrowed)) {
return declaredType;
}
return type;
@@ -7895,22 +7952,23 @@ namespace ts {
// We narrow a non-union type to an exact primitive type if the non-union type
// is a supertype of that primtive type. For example, type 'any' can be narrowed
// to one of the primitive types.
const targetType = getProperty(typeofTypesByName, right.text);
const targetType = getProperty(typeofTypesByName, literal.text);
if (targetType && isTypeSubtypeOf(targetType, type)) {
return targetType;
}
}
const facts = assumeTrue ?
getProperty(typeofEQFacts, right.text) || TypeFacts.TypeofEQHostObject :
getProperty(typeofNEFacts, right.text) || TypeFacts.TypeofNEHostObject;
getProperty(typeofEQFacts, literal.text) || TypeFacts.TypeofEQHostObject :
getProperty(typeofNEFacts, literal.text) || TypeFacts.TypeofNEHostObject;
return getTypeWithFacts(type, facts);
}
function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
if (!isMatchingReference(reference, expr.left)) {
const left = getReferenceFromExpression(expr.left);
if (!isMatchingReference(reference, left)) {
// For a reference of the form 'x.y', an 'x instanceof T' type guard resets the
// narrowed type of 'y' to its declared type.
if (containsMatchingReference(reference, expr.left)) {
if (containsMatchingReference(reference, left)) {
return declaredType;
}
return type;
@@ -7977,7 +8035,7 @@ namespace ts {
const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type;
return isTypeAssignableTo(candidate, targetType) ? candidate :
isTypeAssignableTo(type, candidate) ? type :
neverType;
getIntersectionType([type, candidate]);
}
function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {
@@ -8071,6 +8129,26 @@ namespace ts {
return expression;
}
function getControlFlowContainer(node: Node): Node {
while (true) {
node = node.parent;
if (isFunctionLike(node) || node.kind === SyntaxKind.ModuleBlock || node.kind === SyntaxKind.SourceFile || node.kind === SyntaxKind.PropertyDeclaration) {
return node;
}
}
}
function isDeclarationIncludedInFlow(reference: Node, declaration: Declaration, includeOuterFunctions: boolean) {
const declarationContainer = getControlFlowContainer(declaration);
let container = getControlFlowContainer(reference);
while (container !== declarationContainer &&
(container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) &&
(includeOuterFunctions || getImmediatelyInvokedFunctionExpression(<FunctionExpression>container))) {
container = getControlFlowContainer(container);
}
return container === declarationContainer;
}
function checkIdentifier(node: Identifier): Type {
const symbol = getResolvedSymbol(node);
@@ -8127,11 +8205,12 @@ namespace ts {
return type;
}
const declaration = localOrExportSymbol.valueDeclaration;
const includeOuterFunctions = isReadonlySymbol(localOrExportSymbol);
const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration ||
getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) ||
getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node);
const flowType = getFlowTypeOfReference(node, type, assumeInitialized);
if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) {
!isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions);
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions);
if (!assumeInitialized && !(getCombinedTypeFlags(type) & TypeFlags.Undefined) && getCombinedTypeFlags(flowType) & TypeFlags.Undefined) {
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
// Return the declared type to reduce follow-on errors
return type;
@@ -8281,7 +8360,7 @@ namespace ts {
const classInstanceType = <InterfaceType>getDeclaredTypeOfSymbol(classSymbol);
const baseConstructorType = getBaseConstructorTypeOfClass(classInstanceType);
return baseConstructorType === nullType;
return baseConstructorType === nullWideningType;
}
function checkThisExpression(node: Node): Type {
@@ -8351,7 +8430,10 @@ namespace ts {
if (needToCaptureLexicalThis) {
captureLexicalThis(node, container);
}
if (isFunctionLike(container)) {
if (isFunctionLike(container) &&
(!isInParameterInitializerBeforeContainingFunction(node) || getFunctionLikeThisParameter(container))) {
// Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated.
// If this is a function in a JS file, it might be a class method. Check if it's the RHS
// of a x.prototype.y = function [name]() { .... }
if (container.kind === SyntaxKind.FunctionExpression &&
@@ -8379,7 +8461,7 @@ namespace ts {
if (isClassLike(container.parent)) {
const symbol = getSymbolOfNode(container.parent);
const type = container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (<InterfaceType>getDeclaredTypeOfSymbol(symbol)).thisType;
return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true);
return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*includeOuterFunctions*/ true);
}
if (isInJavaScriptFile(node)) {
@@ -8664,20 +8746,6 @@ namespace ts {
return undefined;
}
function getImmediatelyInvokedFunctionExpression(func: FunctionExpression | MethodDeclaration) {
if (isFunctionExpressionOrArrowFunction(func)) {
let prev: Node = func;
let parent: Node = func.parent;
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
prev = parent;
parent = parent.parent;
}
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
return parent as CallExpression;
}
}
}
// In a variable, parameter or property declaration with a type annotation,
// the contextual type of an initializer expression is the type of the variable, parameter or property.
// Otherwise, in a parameter declaration of a contextually typed function expression,
@@ -8719,6 +8787,16 @@ namespace ts {
function getContextualTypeForReturnExpression(node: Expression): Type {
const func = getContainingFunction(node);
if (isAsyncFunctionLike(func)) {
const contextualReturnType = getContextualReturnType(func);
if (contextualReturnType) {
return getPromisedType(contextualReturnType);
}
return undefined;
}
if (func && !func.asteriskToken) {
return getContextualReturnType(func);
}
@@ -9186,7 +9264,7 @@ namespace ts {
}
}
}
return createArrayType(elementTypes.length ? getUnionType(elementTypes) : emptyArrayElementType);
return createArrayType(elementTypes.length ? getUnionType(elementTypes) : strictNullChecks ? neverType : undefinedWideningType);
}
function isNumericName(name: DeclarationName): boolean {
@@ -9927,7 +10005,7 @@ namespace ts {
function checkNonNullExpression(node: Expression | QualifiedName) {
const type = checkExpression(node);
if (strictNullChecks) {
const kind = getNullableKind(type);
const kind = getCombinedTypeFlags(type) & TypeFlags.Nullable;
if (kind) {
error(node, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ?
Diagnostics.Object_is_possibly_null_or_undefined :
@@ -9954,9 +10032,9 @@ namespace ts {
}
const apparentType = getApparentType(getWidenedType(type));
if (apparentType === unknownType) {
// handle cases when type is Type parameter with invalid constraint
return unknownType;
if (apparentType === unknownType || (type.flags & TypeFlags.TypeParameter && isTypeAny(apparentType))) {
// handle cases when type is Type parameter with invalid or any constraint
return apparentType;
}
const prop = getPropertyOfType(apparentType, right.text);
if (!prop) {
@@ -9973,25 +10051,15 @@ namespace ts {
}
const propType = getTypeOfSymbol(prop);
// Only compute control flow type if this is a property access expression that isn't an
// assignment target, and the referenced property was declared as a variable, property,
// accessor, or optional method.
if (node.kind !== SyntaxKind.PropertyAccessExpression || isAssignmentTarget(node) ||
!(propType.flags & TypeFlags.Union) && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor))) {
!(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) &&
!(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
return propType;
}
const leftmostNode = getLeftmostIdentifierOrThis(node);
if (!leftmostNode) {
return propType;
}
if (leftmostNode.kind === SyntaxKind.Identifier) {
const leftmostSymbol = getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(<Identifier>leftmostNode));
if (!leftmostSymbol) {
return propType;
}
const declaration = leftmostSymbol.valueDeclaration;
if (!declaration || declaration.kind !== SyntaxKind.VariableDeclaration && declaration.kind !== SyntaxKind.Parameter && declaration.kind !== SyntaxKind.BindingElement) {
return propType;
}
}
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true);
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*includeOuterFunctions*/ false);
}
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {
@@ -10305,8 +10373,8 @@ namespace ts {
return -1;
}
function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature) {
let adjustedArgCount: number; // Apparent number of arguments we will have in this call
function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature, signatureHelpTrailingComma = false) {
let argCount: number; // Apparent number of arguments we will have in this call
let typeArguments: NodeArray<TypeNode>; // 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;
@@ -10317,7 +10385,7 @@ namespace ts {
// Even if the call is incomplete, we'll have a missing expression as our last argument,
// so we can say the count is just the arg list length
adjustedArgCount = args.length;
argCount = args.length;
typeArguments = undefined;
if (tagExpression.template.kind === SyntaxKind.TemplateExpression) {
@@ -10340,7 +10408,7 @@ namespace ts {
else if (node.kind === SyntaxKind.Decorator) {
isDecorator = true;
typeArguments = undefined;
adjustedArgCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
argCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
}
else {
const callExpression = <CallExpression>node;
@@ -10351,8 +10419,7 @@ namespace ts {
return signature.minArgumentCount === 0;
}
// For IDE scenarios we may have an incomplete call, so a trailing comma is tantamount to adding another argument.
adjustedArgCount = callExpression.arguments.hasTrailingComma ? args.length + 1 : args.length;
argCount = signatureHelpTrailingComma ? args.length + 1 : args.length;
// If we are missing the close paren, the call is incomplete.
callIsIncomplete = (<CallExpression>callExpression).arguments.end === callExpression.end;
@@ -10376,12 +10443,12 @@ namespace ts {
}
// Too many arguments implies incorrect arity.
if (!signature.hasRestParameter && adjustedArgCount > signature.parameters.length) {
if (!signature.hasRestParameter && argCount > signature.parameters.length) {
return false;
}
// If the call is incomplete, we should skip the lower bound check.
const hasEnoughArguments = adjustedArgCount >= signature.minArgumentCount;
const hasEnoughArguments = argCount >= signature.minArgumentCount;
return callIsIncomplete || hasEnoughArguments;
}
@@ -10953,6 +11020,11 @@ namespace ts {
let resultOfFailedInference: InferenceContext;
let result: Signature;
// If we are in signature help, a trailing comma indicates that we intend to provide another argument,
// so we will only accept overloads with arity at least 1 higher than the current number of provided arguments.
const signatureHelpTrailingComma =
candidatesOutArray && node.kind === SyntaxKind.CallExpression && (<CallExpression>node).arguments.hasTrailingComma;
// Section 4.12.1:
// if the candidate list contains one or more signatures for which the type of each argument
// expression is a subtype of each corresponding parameter type, the return type of the first
@@ -10964,14 +11036,14 @@ namespace ts {
// is just important for choosing the best signature. So in the case where there is only one
// signature, the subtype pass is useless. So skipping it is an optimization.
if (candidates.length > 1) {
result = chooseOverload(candidates, subtypeRelation);
result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma);
}
if (!result) {
// Reinitialize these pointers for round two
candidateForArgumentError = undefined;
candidateForTypeArgumentError = undefined;
resultOfFailedInference = undefined;
result = chooseOverload(candidates, assignableRelation);
result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma);
}
if (result) {
return result;
@@ -11042,9 +11114,9 @@ namespace ts {
diagnostics.add(createDiagnosticForNodeFromMessageChain(node, errorInfo));
}
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>) {
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
for (const originalCandidate of candidates) {
if (!hasCorrectArity(node, args, originalCandidate)) {
if (!hasCorrectArity(node, args, originalCandidate, signatureHelpTrailingComma)) {
continue;
}
@@ -11147,7 +11219,9 @@ namespace ts {
// types are provided for the argument expressions, and the result is always of type Any.
// We exclude union types because we may have a union of function types that happen to have
// no common signatures.
if (isTypeAny(funcType) || (!callSignatures.length && !constructSignatures.length && !(funcType.flags & TypeFlags.Union) && isTypeAssignableTo(funcType, globalFunctionType))) {
if (isTypeAny(funcType) ||
(isTypeAny(apparentType) && funcType.flags & TypeFlags.TypeParameter) ||
(!callSignatures.length && !constructSignatures.length && !(funcType.flags & TypeFlags.Union) && isTypeAssignableTo(funcType, globalFunctionType))) {
// The unknownType indicates that an error already occurred (and was reported). No
// need to report another error in this case.
if (funcType !== unknownType && node.typeArguments) {
@@ -11259,7 +11333,7 @@ namespace ts {
const declaringClassDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(declaration.parent.symbol);
const declaringClass = <InterfaceType>getDeclaredTypeOfSymbol(declaration.parent.symbol);
// A private or protected constructor can only be instantiated within it's own class
// A private or protected constructor can only be instantiated within it's own class
if (!isNodeWithinClass(node, declaringClassDeclaration)) {
if (flags & NodeFlags.Private) {
error(node, Diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, typeToString(declaringClass));
@@ -11465,7 +11539,7 @@ namespace ts {
if (strictNullChecks) {
const declaration = symbol.valueDeclaration;
if (declaration && (<VariableLikeDeclaration>declaration).initializer) {
return addNullableKind(type, TypeFlags.Undefined);
return addTypeKind(type, TypeFlags.Undefined);
}
}
return type;
@@ -11984,7 +12058,7 @@ namespace ts {
function checkVoidExpression(node: VoidExpression): Type {
checkExpression(node.expression);
return undefinedType;
return undefinedWideningType;
}
function checkAwaitExpression(node: AwaitExpression): Type {
@@ -12391,7 +12465,7 @@ namespace ts {
case SyntaxKind.InKeyword:
return checkInExpression(left, right, leftType, rightType);
case SyntaxKind.AmpersandAmpersandToken:
return addNullableKind(rightType, getNullableKind(leftType));
return strictNullChecks ? addTypeKind(rightType, getCombinedTypeFlags(leftType) & TypeFlags.Falsy) : rightType;
case SyntaxKind.BarBarToken:
return getUnionType([getNonNullableType(leftType), rightType]);
case SyntaxKind.EqualsToken:
@@ -12658,7 +12732,7 @@ namespace ts {
case SyntaxKind.SuperKeyword:
return checkSuperExpression(node);
case SyntaxKind.NullKeyword:
return nullType;
return nullWideningType;
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return booleanType;
@@ -12716,7 +12790,7 @@ namespace ts {
case SyntaxKind.SpreadElementExpression:
return checkSpreadElementExpression(<SpreadElementExpression>node, contextualMapper);
case SyntaxKind.OmittedExpression:
return undefinedType;
return undefinedWideningType;
case SyntaxKind.YieldExpression:
return checkYieldExpression(<YieldExpression>node);
case SyntaxKind.JsxExpression:
@@ -14527,6 +14601,12 @@ namespace ts {
}
function areDeclarationFlagsIdentical(left: Declaration, right: Declaration) {
if ((left.kind === SyntaxKind.Parameter && right.kind === SyntaxKind.VariableDeclaration) ||
(left.kind === SyntaxKind.VariableDeclaration && right.kind === SyntaxKind.Parameter)) {
// Differences in optionality between parameters and variables are allowed.
return true;
}
if (hasQuestionToken(left) !== hasQuestionToken(right)) {
return false;
}
@@ -15081,7 +15161,7 @@ namespace ts {
// In a 'switch' statement, each 'case' expression must be of a type that is comparable
// to or from the type of the 'switch' expression.
const caseType = checkExpression(caseClause.expression);
if (!isTypeComparableTo(expressionType, caseType)) {
if (!isTypeEqualityComparableTo(expressionType, caseType)) {
// expressionType is not comparable to caseType, try the reversed check and report errors if it fails
checkTypeComparableTo(caseType, expressionType, caseClause.expression, /*headMessage*/ undefined);
}
@@ -15986,7 +16066,7 @@ namespace ts {
// - augmentation for a global scope is always applied
// - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module).
const checkBody = isGlobalAugmentation || (getSymbolOfNode(node).flags & SymbolFlags.Merged);
if (checkBody) {
if (checkBody && node.body) {
// body of ambient external module is always a module block
for (const statement of (<ModuleBlock>node.body).statements) {
checkModuleAugmentationElement(statement, isGlobalAugmentation);
@@ -16013,7 +16093,15 @@ namespace ts {
}
}
}
checkSourceElement(node.body);
if (compilerOptions.noImplicitAny && !node.body) {
// Ambient shorthand module is an implicit any
reportImplicitAnyError(node, anyType);
}
if (node.body) {
checkSourceElement(node.body);
}
}
function checkModuleAugmentationElement(node: Node, isGlobalAugmentation: boolean): void {
@@ -16116,12 +16204,12 @@ namespace ts {
const symbol = getSymbolOfNode(node);
const target = resolveAlias(symbol);
if (target !== unknownSymbol) {
// For external modules symbol represent local symbol for an alias.
// For external modules symbol represent local symbol for an alias.
// This local symbol will merge any other local declarations (excluding other aliases)
// and symbol.flags will contains combined representation for all merged declaration.
// Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have,
// otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export*
// in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names).
// otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export*
// in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names).
const excludedMeanings =
(symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) |
(symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) |
@@ -16198,7 +16286,7 @@ namespace ts {
else {
if (modulekind === ModuleKind.ES6 && !isInAmbientContext(node)) {
// Import equals declaration is deprecated in es6 or above
grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_6_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_2015_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead);
}
}
}
@@ -16284,7 +16372,7 @@ namespace ts {
if (node.isExportEquals && !isInAmbientContext(node)) {
if (modulekind === ModuleKind.ES6) {
// export assignment is not supported in es6 modules
grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_6_modules_Consider_using_export_default_or_another_module_format_instead);
grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_2015_modules_Consider_using_export_default_or_another_module_format_instead);
}
else if (modulekind === ModuleKind.System) {
// system modules does not support export assignment
@@ -16320,7 +16408,7 @@ namespace ts {
continue;
}
const { declarations, flags } = exports[id];
// ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries.
// ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries.
// (TS Exceptions: namespaces, function overloads, enums, and interfaces)
if (flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) {
continue;
@@ -16719,7 +16807,7 @@ namespace ts {
node = node.parent;
}
return node.parent && node.parent.kind === SyntaxKind.TypeReference;
return node.parent && (node.parent.kind === SyntaxKind.TypeReference || node.parent.kind === SyntaxKind.JSDocTypeReference) ;
}
function isHeritageClauseElementIdentifier(entityName: Node): boolean {
@@ -16794,7 +16882,9 @@ namespace ts {
if (entityName.kind !== SyntaxKind.PropertyAccessExpression) {
if (isInRightSideOfImportOrExportAssignment(<EntityName>entityName)) {
// Since we already checked for ExportAssignment, this really could only be an Import
return getSymbolOfPartOfRightHandSideOfImportEquals(<EntityName>entityName);
const importEqualsDeclaration = <ImportEqualsDeclaration>getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration);
Debug.assert(importEqualsDeclaration !== undefined);
return getSymbolOfPartOfRightHandSideOfImportEquals(<EntityName>entityName, importEqualsDeclaration, /*dontResolveAlias*/ true);
}
}
@@ -16832,10 +16922,7 @@ namespace ts {
return getIntrinsicTagSymbol(<JsxOpeningLikeElement>entityName.parent);
}
// Include aliases in the meaning, this ensures that we do not follow aliases to where they point and instead
// return the alias symbol.
const meaning: SymbolFlags = SymbolFlags.Value | SymbolFlags.Alias;
return resolveEntityName(<Identifier>entityName, meaning);
return resolveEntityName(<Identifier>entityName, SymbolFlags.Value, /*ignoreErrors*/ false, /*dontResolveAlias*/ true);
}
else if (entityName.kind === SyntaxKind.PropertyAccessExpression) {
const symbol = getNodeLinks(entityName).resolvedSymbol;
@@ -16853,11 +16940,8 @@ namespace ts {
}
}
else if (isTypeReferenceIdentifier(<EntityName>entityName)) {
let meaning = entityName.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace;
// Include aliases in the meaning, this ensures that we do not follow aliases to where they point and instead
// return the alias symbol.
meaning |= SymbolFlags.Alias;
return resolveEntityName(<EntityName>entityName, meaning);
const meaning = (entityName.parent.kind === SyntaxKind.TypeReference || entityName.parent.kind === SyntaxKind.JSDocTypeReference) ? SymbolFlags.Type : SymbolFlags.Namespace;
return resolveEntityName(<EntityName>entityName, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/true);
}
else if (entityName.parent.kind === SyntaxKind.JsxAttribute) {
return getJsxAttributePropertySymbol(<JsxAttribute>entityName.parent);
@@ -16890,9 +16974,7 @@ namespace ts {
if (node.kind === SyntaxKind.Identifier) {
if (isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
return node.parent.kind === SyntaxKind.ExportAssignment
? getSymbolOfEntityNameOrPropertyAccessExpression(<Identifier>node)
: getSymbolOfPartOfRightHandSideOfImportEquals(<Identifier>node);
return getSymbolOfEntityNameOrPropertyAccessExpression(<Identifier>node);
}
else if (node.parent.kind === SyntaxKind.BindingElement &&
node.parent.parent.kind === SyntaxKind.ObjectBindingPattern &&
@@ -17025,10 +17107,10 @@ namespace ts {
}
// Gets the type of object literal or array literal of destructuring assignment.
// { a } from
// { a } from
// for ( { a } of elems) {
// }
// [ a ] from
// [ a ] from
// [a] = [ some array ...]
function getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(expr: Expression): Type {
Debug.assert(expr.kind === SyntaxKind.ObjectLiteralExpression || expr.kind === SyntaxKind.ArrayLiteralExpression);
@@ -17061,10 +17143,10 @@ namespace ts {
}
// Gets the property symbol corresponding to the property in destructuring assignment
// 'property1' from
// 'property1' from
// for ( { property1: a } of elems) {
// }
// 'property1' at location 'a' from:
// 'property1' at location 'a' from:
// [a] = [ property1, property2 ]
function getPropertySymbolOfDestructuringAssignment(location: Identifier) {
// Get the type of the object or array literal and then look for property of given name in the type
@@ -17603,6 +17685,10 @@ namespace ts {
if (!isExternalOrCommonJsModule(file)) {
mergeSymbolTable(globals, file.locals);
}
if (file.patternAmbientModules && file.patternAmbientModules.length) {
patternAmbientModules = concatenate(patternAmbientModules, file.patternAmbientModules);
}
if (file.moduleAugmentations.length) {
(augmentations || (augmentations = [])).push(file.moduleAugmentations);
}
@@ -17624,7 +17710,7 @@ namespace ts {
// Setup global builtins
addToSymbolTable(globals, builtinGlobals, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0);
getSymbolLinks(undefinedSymbol).type = undefinedType;
getSymbolLinks(undefinedSymbol).type = undefinedWideningType;
getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments");
getSymbolLinks(unknownSymbol).type = unknownType;
@@ -17733,6 +17819,8 @@ namespace ts {
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ExportDeclaration:
case SyntaxKind.ExportAssignment:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.Parameter:
break;
case SyntaxKind.FunctionDeclaration:
@@ -17964,7 +18052,7 @@ namespace ts {
function checkGrammarAsyncModifier(node: Node, asyncModifier: Node): boolean {
if (languageVersion < ScriptTarget.ES6) {
return grammarErrorOnNode(asyncModifier, Diagnostics.Async_functions_are_only_available_when_targeting_ECMAScript_6_and_higher);
return grammarErrorOnNode(asyncModifier, Diagnostics.Async_functions_are_only_available_when_targeting_ECMAScript_2015_or_higher);
}
switch (node.kind) {
@@ -18003,10 +18091,6 @@ namespace ts {
}
function checkGrammarParameterList(parameters: NodeArray<ParameterDeclaration>) {
if (checkGrammarForDisallowedTrailingComma(parameters)) {
return true;
}
let seenOptionalParameter = false;
const parameterCount = parameters.length;
@@ -18125,8 +18209,7 @@ namespace ts {
}
function checkGrammarArguments(node: CallExpression, args: NodeArray<Expression>): boolean {
return checkGrammarForDisallowedTrailingComma(args) ||
checkGrammarForOmittedArgument(node, args);
return checkGrammarForOmittedArgument(node, args);
}
function checkGrammarHeritageClause(node: HeritageClause): boolean {
@@ -18227,7 +18310,7 @@ namespace ts {
return grammarErrorOnNode(node.asteriskToken, Diagnostics.An_overload_signature_cannot_be_declared_as_a_generator);
}
if (languageVersion < ScriptTarget.ES6) {
return grammarErrorOnNode(node.asteriskToken, Diagnostics.Generators_are_only_available_when_targeting_ECMAScript_6_or_higher);
return grammarErrorOnNode(node.asteriskToken, Diagnostics.Generators_are_only_available_when_targeting_ECMAScript_2015_or_higher);
}
}
}
@@ -18449,6 +18532,14 @@ namespace ts {
}
}
function getFunctionLikeThisParameter(func: FunctionLikeDeclaration) {
if (func.parameters.length &&
func.parameters[0].name.kind === SyntaxKind.Identifier &&
(<Identifier>func.parameters[0].name).originalKeywordKind === SyntaxKind.ThisKeyword) {
return func.parameters[0];
}
}
function checkGrammarForNonSymbolComputedProperty(node: DeclarationName, message: DiagnosticMessage) {
if (isDynamicName(node)) {
return grammarErrorOnNode(node, message);
+10 -4
View File
@@ -6,7 +6,7 @@
namespace ts {
/* @internal */
export let optionDeclarations: CommandLineOption[] = [
export const optionDeclarations: CommandLineOption[] = [
{
name: "charset",
type: "string",
@@ -337,8 +337,13 @@ namespace ts {
}
},
{
name: "typesRoot",
type: "string"
name: "typeRoots",
type: "list",
element: {
name: "typeRoots",
type: "string",
isFilePath: true
}
},
{
name: "types",
@@ -401,7 +406,8 @@ namespace ts {
"es2015.symbol": "lib.es2015.symbol.d.ts",
"es2015.symbol.wellknown": "lib.es2015.symbol.wellknown.d.ts",
"es2016.array.include": "lib.es2016.array.include.d.ts",
"es2017.object": "lib.es2017.object.d.ts"
"es2017.object": "lib.es2017.object.d.ts",
"es2017.sharedmemory": "lib.es2017.sharedmemory.d.ts"
},
},
description: Diagnostics.Specify_library_files_to_be_included_in_the_compilation_Colon
+11 -2
View File
@@ -1196,13 +1196,22 @@ namespace ts {
const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"];
export function removeFileExtension(path: string): string {
for (const ext of extensionsToRemove) {
if (fileExtensionIs(path, ext)) {
return path.substr(0, path.length - ext.length);
const extensionless = tryRemoveExtension(path, ext);
if (extensionless !== undefined) {
return extensionless;
}
}
return path;
}
export function tryRemoveExtension(path: string, extension: string): string {
return fileExtensionIs(path, extension) ? path.substring(0, path.length - extension.length) : undefined;
}
export function isJsxOrTsxExtension(ext: string): boolean {
return ext === ".jsx" || ext === ".tsx";
}
export function changeExtension<T extends string | Path>(path: T, newExtension: string): T {
return <T>(removeFileExtension(path) + newExtension);
}
+18 -13
View File
@@ -95,7 +95,7 @@ namespace ts {
// Emit reference in dts, if the file reference was not already emitted
if (referencedFile && !contains(emittedReferencedFiles, referencedFile)) {
// Add a reference to generated dts file,
// global file reference is added only
// global file reference is added only
// - if it is not bundled emit (because otherwise it would be self reference)
// - and it is not already added
if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference)) {
@@ -148,7 +148,7 @@ namespace ts {
if (!isBundledEmit && isExternalModule(sourceFile) && sourceFile.moduleAugmentations.length && !resultHasExternalModuleIndicator) {
// if file was external module with augmentations - this fact should be preserved in .d.ts as well.
// in case if we didn't write any external module specifiers in .d.ts we need to emit something
// in case if we didn't write any external module specifiers in .d.ts we need to emit something
// that will force compiler to think that this file is an external module - 'export {}' is a reasonable choice here.
write("export {};");
writeLine();
@@ -766,7 +766,7 @@ namespace ts {
function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration) {
// emitExternalModuleSpecifier is usually called when we emit something in the.d.ts file that will make it an external module (i.e. import/export declarations).
// the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered
// the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered
// external modules since they are indistinguishable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}'
// so compiler will treat them as external modules.
resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || parent.kind !== SyntaxKind.ModuleDeclaration;
@@ -853,21 +853,26 @@ namespace ts {
writeTextOfNode(currentText, node.name);
}
}
while (node.body.kind !== SyntaxKind.ModuleBlock) {
while (node.body && node.body.kind !== SyntaxKind.ModuleBlock) {
node = <ModuleDeclaration>node.body;
write(".");
writeTextOfNode(currentText, node.name);
}
const prevEnclosingDeclaration = enclosingDeclaration;
enclosingDeclaration = node;
write(" {");
writeLine();
increaseIndent();
emitLines((<ModuleBlock>node.body).statements);
decreaseIndent();
write("}");
writeLine();
enclosingDeclaration = prevEnclosingDeclaration;
if (node.body) {
enclosingDeclaration = node;
write(" {");
writeLine();
increaseIndent();
emitLines((<ModuleBlock>node.body).statements);
decreaseIndent();
write("}");
writeLine();
enclosingDeclaration = prevEnclosingDeclaration;
}
else {
write(";");
}
}
function writeTypeAliasDeclaration(node: TypeAliasDeclaration) {
+17 -9
View File
@@ -627,18 +627,14 @@
"category": "Error",
"code": 1200
},
"Import assignment cannot be used when targeting ECMAScript 6 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 2015 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 6 modules. Consider using 'export default' or another module format instead.": {
"Export assignment cannot be used when targeting ECMAScript 2015 modules. Consider using 'export default' or another module format instead.": {
"category": "Error",
"code": 1203
},
"Cannot compile modules into 'es2015' when targeting 'ES5' or lower.": {
"category": "Error",
"code": 1204
},
"Decorators are not valid here.": {
"category": "Error",
"code": 1206
@@ -687,7 +683,7 @@
"category": "Error",
"code": 1219
},
"Generators are only available when targeting ECMAScript 6 or higher.": {
"Generators are only available when targeting ECMAScript 2015 or higher.": {
"category": "Error",
"code": 1220
},
@@ -819,6 +815,10 @@
"category": "Error",
"code": 1252
},
"'{0}' tag cannot be used independently as a top level JSDoc tag.": {
"category": "Error",
"code": 1253
},
"'with' statements are not allowed in an async function block.": {
"category": "Error",
"code": 1300
@@ -827,7 +827,7 @@
"category": "Error",
"code": 1308
},
"Async functions are only available when targeting ECMAScript 6 and higher.": {
"Async functions are only available when targeting ECMAScript 2015 or higher.": {
"category": "Error",
"code": 1311
},
@@ -1931,6 +1931,10 @@
"category": "Error",
"code": 2687
},
"Cannot find type definition file for '{0}'.": {
"category": "Error",
"code": 2688
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
"code": 4000
@@ -2264,7 +2268,7 @@
"category": "Error",
"code": 5047
},
"Option 'inlineSources' can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided.": {
"Option '{0} can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided.": {
"category": "Error",
"code": 5051
},
@@ -2776,6 +2780,10 @@
"category": "Error",
"code": 6131
},
"File name '{0}' has a '{1}' extension - stripping it": {
"category": "Message",
"code": 6132
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
+105 -30
View File
@@ -2613,11 +2613,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
return isSourceFileLevelDeclarationInSystemJsModule(targetDeclaration, /*isExported*/ true);
}
function isNameOfExportedDeclarationInNonES6Module(node: Node): boolean {
if (modulekind === ModuleKind.System || node.kind !== SyntaxKind.Identifier || nodeIsSynthesized(node)) {
return false;
}
return !exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, (<Identifier>node).text);
}
function emitPrefixUnaryExpression(node: PrefixUnaryExpression) {
const exportChanged = (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) &&
const isPlusPlusOrMinusMinus = (node.operator === SyntaxKind.PlusPlusToken
|| node.operator === SyntaxKind.MinusMinusToken);
const externalExportChanged = isPlusPlusOrMinusMinus &&
isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand);
if (exportChanged) {
if (externalExportChanged) {
// emit
// ++x
// as
@@ -2626,6 +2636,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
emitNodeWithoutSourceMap(node.operand);
write(`", `);
}
const internalExportChanged = isPlusPlusOrMinusMinus &&
isNameOfExportedDeclarationInNonES6Module(node.operand);
if (internalExportChanged) {
emitAliasEqual(<Identifier> node.operand);
}
write(tokenToString(node.operator));
// In some cases, we need to emit a space between the operator and the operand. One obvious case
@@ -2651,14 +2667,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}
emit(node.operand);
if (exportChanged) {
if (externalExportChanged) {
write(")");
}
}
function emitPostfixUnaryExpression(node: PostfixUnaryExpression) {
const exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand);
if (exportChanged) {
const externalExportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand);
const internalExportChanged = isNameOfExportedDeclarationInNonES6Module(node.operand);
if (externalExportChanged) {
// export function returns the value that was passes as the second argument
// however for postfix unary expressions result value should be the value before modification.
// emit 'x++' as '(export('x', ++x) - 1)' and 'x--' as '(export('x', --x) + 1)'
@@ -2676,6 +2694,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
write(") + 1)");
}
}
else if (internalExportChanged) {
emitAliasEqual(<Identifier> node.operand);
emit(node.operand);
if (node.operator === SyntaxKind.PlusPlusToken) {
write(" += 1");
}
else {
write(" -= 1");
}
}
else {
emit(node.operand);
write(tokenToString(node.operator));
@@ -2777,24 +2805,50 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}
}
function emitAliasEqual(name: Identifier): boolean {
for (const specifier of exportSpecifiers[name.text]) {
emitStart(specifier.name);
emitContainingModuleName(specifier);
if (languageVersion === ScriptTarget.ES3 && name.text === "default") {
write('["default"]');
}
else {
write(".");
emitNodeWithCommentsAndWithoutSourcemap(specifier.name);
}
emitEnd(specifier.name);
write(" = ");
}
return true;
}
function emitBinaryExpression(node: BinaryExpression) {
if (languageVersion < ScriptTarget.ES6 && node.operatorToken.kind === SyntaxKind.EqualsToken &&
(node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) {
emitDestructuring(node, node.parent.kind === SyntaxKind.ExpressionStatement);
}
else {
const exportChanged =
node.operatorToken.kind >= SyntaxKind.FirstAssignment &&
node.operatorToken.kind <= SyntaxKind.LastAssignment &&
const isAssignment = isAssignmentOperator(node.operatorToken.kind);
const externalExportChanged = isAssignment &&
isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.left);
if (exportChanged) {
if (externalExportChanged) {
// emit assignment 'x <op> y' as 'exports("x", x <op> y)'
write(`${exportFunctionForFile}("`);
emitNodeWithoutSourceMap(node.left);
write(`", `);
}
const internalExportChanged = isAssignment &&
isNameOfExportedDeclarationInNonES6Module(node.left);
if (internalExportChanged) {
// export { foo }
// emit foo = 2 as exports.foo = foo = 2
emitAliasEqual(<Identifier>node.left);
}
if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken || node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) {
// Downleveled emit exponentiation operator using Math.pow
emitExponentiationOperator(node);
@@ -2815,7 +2869,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
decreaseIndentIf(indentedBeforeOperator, indentedAfterOperator);
}
if (exportChanged) {
if (externalExportChanged) {
write(")");
}
}
@@ -5359,17 +5413,17 @@ const _super = (function (geti, seti) {
//
// TypeScript | Javascript
// --------------------------------|------------------------------------
// @dec | let C_1;
// class C { | let C = C_1 = class C {
// static x() { return C.y; } | static x() { return C_1.y; }
// static y = 1; | }
// @dec | let C_1 = class C {
// class C { | static x() { return C_1.y; }
// static x() { return C.y; } | }
// static y = 1; | let C = C_1;
// } | C.y = 1;
// | C = C_1 = __decorate([dec], C);
// --------------------------------|------------------------------------
// @dec | let C_1;
// export class C { | export let C = C_1 = class C {
// static x() { return C.y; } | static x() { return C_1.y; }
// static y = 1; | }
// @dec | let C_1 = class C {
// export class C { | static x() { return C_1.y; }
// static x() { return C.y; } | }
// static y = 1; | export let C = C_1;
// } | C.y = 1;
// | C = C_1 = __decorate([dec], C);
// ---------------------------------------------------------------------
@@ -5398,10 +5452,10 @@ const _super = (function (geti, seti) {
//
// TypeScript | Javascript
// --------------------------------|------------------------------------
// @dec | let C_1;
// export default class C { | let C = C_1 = class C {
// static x() { return C.y; } | static x() { return C_1.y; }
// static y = 1; | }
// @dec | let C_1 = class C {
// export default class C { | static x() { return C_1.y; }
// static x() { return C.y; } | };
// static y = 1; | let C = C_1;
// } | C.y = 1;
// | C = C_1 = __decorate([dec], C);
// | export default C;
@@ -5410,25 +5464,25 @@ const _super = (function (geti, seti) {
//
// NOTE: we reuse the same rewriting logic for cases when targeting ES6 and module kind is System.
// Because of hoisting top level class declaration need to be emitted as class expressions.
// Because of hoisting top level class declaration need to be emitted as class expressions.
// Double bind case is only required if node is decorated.
if (isDecorated && resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithBodyScopedClassBinding) {
decoratedClassAlias = unescapeIdentifier(makeUniqueName(node.name ? node.name.text : "default"));
decoratedClassAliases[getNodeId(node)] = decoratedClassAlias;
write(`let ${decoratedClassAlias};`);
writeLine();
}
if (isES6ExportedDeclaration(node) && !(node.flags & NodeFlags.Default)) {
if (isES6ExportedDeclaration(node) && !(node.flags & NodeFlags.Default) && decoratedClassAlias === undefined) {
write("export ");
}
if (!isHoistedDeclarationInSystemModule) {
write("let ");
}
emitDeclarationName(node);
if (decoratedClassAlias !== undefined) {
write(` = ${decoratedClassAlias}`);
write(`${decoratedClassAlias}`);
}
else {
emitDeclarationName(node);
}
write(" = ");
@@ -5490,6 +5544,16 @@ const _super = (function (geti, seti) {
emitToken(SyntaxKind.CloseBraceToken, node.members.end);
if (rewriteAsClassExpression) {
if (decoratedClassAlias !== undefined) {
write(";");
writeLine();
if (isES6ExportedDeclaration(node) && !(node.flags & NodeFlags.Default)) {
write("export ");
}
write("let ");
emitDeclarationName(node);
write(` = ${decoratedClassAlias}`);
}
decoratedClassAliases[getNodeId(node)] = undefined;
write(";");
}
@@ -5549,7 +5613,11 @@ const _super = (function (geti, seti) {
}
function emitClassLikeDeclarationBelowES6(node: ClassLikeDeclaration) {
const isES6ExportedClass = isES6ExportedDeclaration(node);
if (node.kind === SyntaxKind.ClassDeclaration) {
if (isES6ExportedClass && !(node.flags & NodeFlags.Default)) {
write("export ");
}
// source file level classes in system modules are hoisted so 'var's for them are already defined
if (!shouldHoistDeclarationInSystemJsModule(node)) {
write("var ");
@@ -5619,9 +5687,15 @@ const _super = (function (geti, seti) {
}
emitEnd(node);
if (node.kind === SyntaxKind.ClassDeclaration) {
if (node.kind === SyntaxKind.ClassDeclaration && !isES6ExportedClass) {
emitExportMemberAssignment(<ClassDeclaration>node);
}
else if (isES6ExportedClass && (node.flags & NodeFlags.Default)) {
writeLine();
write("export default ");
emitDeclarationName(node);
write(";");
}
}
function emitClassMemberPrefix(node: ClassLikeDeclaration, member: Node) {
@@ -6236,7 +6310,7 @@ const _super = (function (geti, seti) {
}
function getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration: ModuleDeclaration): ModuleDeclaration {
if (moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
if (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
const recursiveInnerModule = getInnerMostModuleDeclarationFromDottedModule(<ModuleDeclaration>moduleDeclaration.body);
return recursiveInnerModule || <ModuleDeclaration>moduleDeclaration.body;
}
@@ -6285,6 +6359,7 @@ const _super = (function (geti, seti) {
write(getGeneratedNameForNode(node));
emitEnd(node.name);
write(") ");
Debug.assert(node.body !== undefined); // node.body must exist, as this is a non-ambient module
if (node.body.kind === SyntaxKind.ModuleBlock) {
const saveConvertedLoopState = convertedLoopState;
const saveTempFlags = tempFlags;
+172 -19
View File
@@ -401,6 +401,15 @@ namespace ts {
return visitNode(cbNode, (<JSDocTypeTag>node).typeExpression);
case SyntaxKind.JSDocTemplateTag:
return visitNodes(cbNodes, (<JSDocTemplateTag>node).typeParameters);
case SyntaxKind.JSDocTypedefTag:
return visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression) ||
visitNode(cbNode, (<JSDocTypedefTag>node).name) ||
visitNode(cbNode, (<JSDocTypedefTag>node).jsDocTypeLiteral);
case SyntaxKind.JSDocTypeLiteral:
return visitNodes(cbNodes, (<JSDocTypeLiteral>node).jsDocPropertyTags);
case SyntaxKind.JSDocPropertyTag:
return visitNode(cbNode, (<JSDocPropertyTag>node).typeExpression) ||
visitNode(cbNode, (<JSDocPropertyTag>node).name);
}
}
@@ -431,7 +440,14 @@ namespace ts {
/* @internal */
export function parseIsolatedJSDocComment(content: string, start?: number, length?: number) {
return Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length);
const result = Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length);
if (result && result.jsDocComment) {
// because the jsDocComment was parsed out of the source file, it might
// not be covered by the fixupParentReferences.
Parser.fixupParentReferences(result.jsDocComment);
}
return result;
}
/* @internal */
@@ -628,9 +644,14 @@ namespace ts {
if (comments) {
for (const comment of comments) {
const jsDocComment = JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos);
if (jsDocComment) {
node.jsDocComment = jsDocComment;
if (!jsDocComment) {
continue;
}
if (!node.jsDocComments) {
node.jsDocComments = [];
}
node.jsDocComments.push(jsDocComment);
}
}
}
@@ -638,14 +659,14 @@ namespace ts {
return node;
}
export function fixupParentReferences(sourceFile: Node) {
export function fixupParentReferences(rootNode: Node) {
// normally parent references are set during binding. However, for clients that only need
// a syntax tree, and no semantic features, then the binding process is an unnecessary
// overhead. This functions allows us to set all the parents, without all the expense of
// binding.
let parent: Node = sourceFile;
forEachChild(sourceFile, visitNode);
let parent: Node = rootNode;
forEachChild(rootNode, visitNode);
return;
function visitNode(n: Node): void {
@@ -658,6 +679,13 @@ namespace ts {
const saveParent = parent;
parent = n;
forEachChild(n, visitNode);
if (n.jsDocComments) {
for (const jsDocComment of n.jsDocComments) {
jsDocComment.parent = n;
parent = jsDocComment;
forEachChild(jsDocComment, visitNode);
}
}
parent = saveParent;
}
}
@@ -2704,7 +2732,7 @@ namespace ts {
// 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In]
// 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In]
// Production (1) of AsyncArrowFunctionExpression is parsed in "tryParseAsyncSimpleArrowFunctionExpression".
// And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression".
// And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression".
//
// If we do successfully parse arrow-function, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is
// not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done
@@ -3396,8 +3424,8 @@ namespace ts {
if (sourceFile.languageVariant !== LanguageVariant.JSX) {
return false;
}
// We are in JSX context and the token is part of JSXElement.
// Fall through
// We are in JSX context and the token is part of JSXElement.
// Fall through
default:
return true;
}
@@ -4099,9 +4127,9 @@ namespace ts {
const isAsync = !!(node.flags & NodeFlags.Async);
node.name =
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
parseOptionalIdentifier();
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
parseOptionalIdentifier();
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false);
@@ -5309,7 +5337,14 @@ namespace ts {
else {
node.name = parseLiteralNode(/*internName*/ true);
}
node.body = parseModuleBlock();
if (token === SyntaxKind.OpenBraceToken) {
node.body = parseModuleBlock();
}
else {
parseSemicolon();
}
return finishNode(node);
}
@@ -5891,7 +5926,7 @@ namespace ts {
}
function checkForEmptyTypeArgumentList(typeArguments: NodeArray<Node>) {
if (parseDiagnostics.length === 0 && typeArguments && typeArguments.length === 0) {
if (parseDiagnostics.length === 0 && typeArguments && typeArguments.length === 0) {
const start = typeArguments.pos - "<".length;
const end = skipTrivia(sourceText, typeArguments.end) + ">".length;
return parseErrorAtPosition(start, end - start, Diagnostics.Type_argument_list_cannot_be_empty);
@@ -6052,7 +6087,6 @@ namespace ts {
Debug.assert(end <= content.length);
let tags: NodeArray<JSDocTag>;
let result: JSDocComment;
// Check for /** (JSDoc opening part)
@@ -6159,6 +6193,8 @@ namespace ts {
return handleTemplateTag(atToken, tagName);
case "type":
return handleTypeTag(atToken, tagName);
case "typedef":
return handleTypedefTag(atToken, tagName);
}
}
@@ -6266,6 +6302,122 @@ namespace ts {
return finishNode(result);
}
function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag {
const typeExpression = tryParseTypeExpression();
skipWhitespace();
const name = parseJSDocIdentifierName();
if (!name) {
parseErrorAtPosition(scanner.getStartPos(), /*length*/ 0, Diagnostics.Identifier_expected);
return undefined;
}
const result = <JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
result.name = name;
result.typeExpression = typeExpression;
return finishNode(result);
}
function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag {
const typeExpression = tryParseTypeExpression();
skipWhitespace();
const typedefTag = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, atToken.pos);
typedefTag.atToken = atToken;
typedefTag.tagName = tagName;
typedefTag.name = parseJSDocIdentifierName();
typedefTag.typeExpression = typeExpression;
if (typeExpression) {
if (typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
const jsDocTypeReference = <JSDocTypeReference>typeExpression.type;
if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) {
const name = <Identifier>jsDocTypeReference.name;
if (name.text === "Object") {
typedefTag.jsDocTypeLiteral = scanChildTags();
}
}
}
if (!typedefTag.jsDocTypeLiteral) {
typedefTag.jsDocTypeLiteral = typeExpression.type;
}
}
else {
typedefTag.jsDocTypeLiteral = scanChildTags();
}
return finishNode(typedefTag);
function scanChildTags(): JSDocTypeLiteral {
const jsDocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, scanner.getStartPos());
let resumePos = scanner.getStartPos();
let canParseTag = true;
let seenAsterisk = false;
let parentTagTerminated = false;
while (token !== SyntaxKind.EndOfFileToken && !parentTagTerminated) {
nextJSDocToken();
switch (token) {
case SyntaxKind.AtToken:
if (canParseTag) {
parentTagTerminated = !tryParseChildTag(jsDocTypeLiteral);
}
seenAsterisk = false;
break;
case SyntaxKind.NewLineTrivia:
resumePos = scanner.getStartPos() - 1;
canParseTag = true;
seenAsterisk = false;
break;
case SyntaxKind.AsteriskToken:
if (seenAsterisk) {
canParseTag = false;
}
seenAsterisk = true;
break;
case SyntaxKind.Identifier:
canParseTag = false;
case SyntaxKind.EndOfFileToken:
break;
}
}
scanner.setTextPos(resumePos);
return finishNode(jsDocTypeLiteral);
}
}
function tryParseChildTag(parentTag: JSDocTypeLiteral): boolean {
Debug.assert(token === SyntaxKind.AtToken);
const atToken = createNode(SyntaxKind.AtToken, scanner.getStartPos());
atToken.end = scanner.getTextPos();
nextJSDocToken();
const tagName = parseJSDocIdentifierName();
if (!tagName) {
return false;
}
switch (tagName.text) {
case "type":
if (parentTag.jsDocTypeTag) {
// already has a @type tag, terminate the parent tag now.
return false;
}
parentTag.jsDocTypeTag = handleTypeTag(atToken, tagName);
return true;
case "prop":
case "property":
if (!parentTag.jsDocPropertyTags) {
parentTag.jsDocPropertyTags = <NodeArray<JSDocPropertyTag>>[];
}
const propertyTag = handlePropertyTag(atToken, tagName);
parentTag.jsDocPropertyTags.push(propertyTag);
return true;
}
return false;
}
function handleTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag {
if (forEach(tags, t => t.kind === SyntaxKind.JSDocTemplateTag)) {
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text);
@@ -6435,10 +6587,6 @@ namespace ts {
node._children = undefined;
}
if (node.jsDocComment) {
node.jsDocComment = undefined;
}
node.pos += delta;
node.end += delta;
@@ -6447,6 +6595,11 @@ namespace ts {
}
forEachChild(node, visitNode, visitArray);
if (node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
forEachChild(jsDocComment, visitNode, visitArray);
}
}
checkNodePositions(node, aggressiveChecks);
}
+161 -109
View File
@@ -9,16 +9,11 @@ namespace ts {
/* @internal */ export let ioWriteTime = 0;
/** The version of the TypeScript compiler release */
export const version = "1.9.0";
const emptyArray: any[] = [];
const defaultLibrarySearchPaths = [
"types/",
"node_modules/",
"node_modules/@types/",
];
export const version = "1.9.0";
const defaultTypeRoots = ["node_modules/@types"];
export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean): string {
while (true) {
@@ -95,7 +90,8 @@ namespace ts {
return compilerOptions.traceResolution && host.trace !== undefined;
}
function hasZeroOrOneAsteriskCharacter(str: string): boolean {
/* @internal */
export function hasZeroOrOneAsteriskCharacter(str: string): boolean {
let seenAsterisk = false;
for (let i = 0; i < str.length; i++) {
if (str.charCodeAt(i) === CharacterCodes.asterisk) {
@@ -182,6 +178,11 @@ namespace ts {
const typeReferenceExtensions = [".d.ts"];
function getEffectiveTypeRoots(options: CompilerOptions, host: ModuleResolutionHost) {
return options.typeRoots ||
defaultTypeRoots.map(d => combinePaths(options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory(), d));
}
/**
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.
* This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
@@ -196,24 +197,22 @@ namespace ts {
traceEnabled
};
// use typesRoot and fallback to directory that contains tsconfig or current directory if typesRoot is not set
const rootDir = options.typesRoot || (options.configFilePath ? getDirectoryPath(options.configFilePath) : (host.getCurrentDirectory && host.getCurrentDirectory()));
const typeRoots = getEffectiveTypeRoots(options, host);
if (traceEnabled) {
if (containingFile === undefined) {
if (rootDir === undefined) {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_not_set, typeReferenceDirectiveName);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_1, typeReferenceDirectiveName, rootDir);
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_1, typeReferenceDirectiveName, typeRoots);
}
}
else {
if (rootDir === undefined) {
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_not_set, typeReferenceDirectiveName, containingFile);
}
else {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, rootDir);
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, typeRoots);
}
}
}
@@ -221,14 +220,13 @@ namespace ts {
const failedLookupLocations: string[] = [];
// Check primary library paths
if (rootDir !== undefined) {
const effectivePrimarySearchPaths = options.typesSearchPaths || defaultLibrarySearchPaths;
for (const searchPath of effectivePrimarySearchPaths) {
const primaryPath = combinePaths(rootDir, searchPath);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_with_primary_search_path_0, primaryPath);
}
const candidate = combinePaths(primaryPath, typeReferenceDirectiveName);
if (typeRoots.length) {
if (traceEnabled) {
trace(host, Diagnostics.Resolving_with_primary_search_path_0, typeRoots.join(", "));
}
const primarySearchPaths = typeRoots;
for (const typeRoot of primarySearchPaths) {
const candidate = combinePaths(typeRoot, typeReferenceDirectiveName);
const candidateDirectory = getDirectoryPath(candidate);
const resolvedFile = loadNodeModuleFromDirectory(typeReferenceExtensions, candidate, failedLookupLocations,
!directoryProbablyExists(candidateDirectory, host), moduleResolutionState);
@@ -255,9 +253,6 @@ namespace ts {
if (containingFile) {
initialLocationForSecondaryLookup = getDirectoryPath(containingFile);
}
else {
initialLocationForSecondaryLookup = rootDir;
}
if (initialLocationForSecondaryLookup !== undefined) {
// check secondary locations
@@ -496,48 +491,23 @@ namespace ts {
trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, state.compilerOptions.baseUrl, moduleName);
}
let longestMatchPrefixLength = -1;
let matchedPattern: string;
let matchedStar: string;
// string is for exact match
let matchedPattern: Pattern | string | undefined = undefined;
if (state.compilerOptions.paths) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
}
for (const key in state.compilerOptions.paths) {
const pattern: string = key;
const indexOfStar = pattern.indexOf("*");
if (indexOfStar !== -1) {
const prefix = pattern.substr(0, indexOfStar);
const suffix = pattern.substr(indexOfStar + 1);
if (moduleName.length >= prefix.length + suffix.length &&
startsWith(moduleName, prefix) &&
endsWith(moduleName, suffix)) {
// use length of prefix as betterness criteria
if (prefix.length > longestMatchPrefixLength) {
longestMatchPrefixLength = prefix.length;
matchedPattern = pattern;
matchedStar = moduleName.substr(prefix.length, moduleName.length - suffix.length);
}
}
}
else if (pattern === moduleName) {
// pattern was matched as is - no need to search further
matchedPattern = pattern;
matchedStar = undefined;
break;
}
}
matchedPattern = matchPatternOrExact(getKeys(state.compilerOptions.paths), moduleName);
}
if (matchedPattern) {
const matchedStar = typeof matchedPattern === "string" ? undefined : matchedText(matchedPattern, moduleName);
const matchedPatternText = typeof matchedPattern === "string" ? matchedPattern : patternText(matchedPattern);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPattern);
trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText);
}
for (const subst of state.compilerOptions.paths[matchedPattern]) {
const path = matchedStar ? subst.replace("\*", matchedStar) : subst;
for (const subst of state.compilerOptions.paths[matchedPatternText]) {
const path = matchedStar ? subst.replace("*", matchedStar) : subst;
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, path));
if (state.traceEnabled) {
trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path);
@@ -560,6 +530,75 @@ namespace ts {
}
}
/**
* patternStrings contains both pattern strings (containing "*") and regular strings.
* Return an exact match if possible, or a pattern match, or undefined.
* (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
*/
function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined {
const patterns: Pattern[] = [];
for (const patternString of patternStrings) {
const pattern = tryParsePattern(patternString);
if (pattern) {
patterns.push(pattern);
}
else if (patternString === candidate) {
// pattern was matched as is - no need to search further
return patternString;
}
}
return findBestPatternMatch(patterns, _ => _, candidate);
}
function patternText({prefix, suffix}: Pattern): string {
return `${prefix}*${suffix}`;
}
/**
* Given that candidate matches pattern, returns the text matching the '*'.
* E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar"
*/
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<T>(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined {
let matchedValue: T | undefined = undefined;
// use length of prefix as betterness criteria
let longestMatchPrefixLength = -1;
for (const v of values) {
const pattern = getPattern(v);
if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) {
longestMatchPrefixLength = pattern.prefix.length;
matchedValue = v;
}
}
return matchedValue;
}
function isPatternMatch({prefix, suffix}: Pattern, candidate: string) {
return candidate.length >= prefix.length + suffix.length &&
startsWith(candidate, prefix) &&
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));
const indexOfStar = pattern.indexOf("*");
return indexOfStar === -1 ? undefined : {
prefix: pattern.substr(0, indexOfStar),
suffix: pattern.substr(indexOfStar + 1)
};
}
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
const containingDirectory = getDirectoryPath(containingFile);
const supportedExtensions = getSupportedExtensions(compilerOptions);
@@ -619,8 +658,25 @@ namespace ts {
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
*/
function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
// First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts"
const resolvedByAddingOrKeepingExtension = loadModuleFromFileWorker(candidate, extensions, failedLookupLocation, onlyRecordFailures, state);
if (resolvedByAddingOrKeepingExtension) {
return resolvedByAddingOrKeepingExtension;
}
// Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
if (hasJavaScriptFileExtension(candidate)) {
const extensionless = removeFileExtension(candidate);
if (state.traceEnabled) {
const extension = candidate.substring(extensionless.length);
trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension);
}
return loadModuleFromFileWorker(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state);
}
}
function loadModuleFromFileWorker(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
if (!onlyRecordFailures) {
// check if containig folder exists - if it doesn't then just record failures for all supported extensions without disk probing
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
const directory = getDirectoryPath(candidate);
if (directory) {
onlyRecordFailures = !directoryProbablyExists(directory, state.host);
@@ -629,7 +685,7 @@ namespace ts {
return forEach(extensions, tryLoad);
function tryLoad(ext: string): string {
if (ext === ".tsx" && state.skipTsx) {
if (state.skipTsx && isJsxOrTsxExtension(ext)) {
return undefined;
}
const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext;
@@ -875,19 +931,6 @@ namespace ts {
}
}
function getDefaultTypeDirectiveNames(rootPath: string): string[] {
const localTypes = combinePaths(rootPath, "types");
const npmTypes = combinePaths(rootPath, "node_modules/@types");
let result: string[] = [];
if (sys.directoryExists(localTypes)) {
result = result.concat(sys.getDirectories(localTypes));
}
if (sys.directoryExists(npmTypes)) {
result = result.concat(sys.getDirectories(npmTypes));
}
return result;
}
function getDefaultLibLocation(): string {
return getDirectoryPath(normalizePath(sys.getExecutingFilePath()));
}
@@ -896,7 +939,6 @@ namespace ts {
const realpath = sys.realpath && ((path: string) => sys.realpath(path));
return {
getDefaultTypeDirectiveNames,
getSourceFile,
getDefaultLibLocation,
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
@@ -909,6 +951,7 @@ namespace ts {
readFile: fileName => sys.readFile(fileName),
trace: (s: string) => sys.write(s + newLine),
directoryExists: directoryName => sys.directoryExists(directoryName),
getDirectories: (path: string) => sys.getDirectories(path),
realpath
};
}
@@ -972,21 +1015,35 @@ namespace ts {
return resolutions;
}
export function getDefaultTypeDirectiveNames(options: CompilerOptions, rootFiles: string[], host: CompilerHost): string[] {
function getInferredTypesRoot(options: CompilerOptions, rootFiles: string[], host: CompilerHost) {
return computeCommonSourceDirectoryOfFilenames(rootFiles, host.getCurrentDirectory(), f => host.getCanonicalFileName(f));
}
/**
* Given a set of options and a set of root files, returns the set of type directive names
* that should be included for this program automatically.
* This list could either come from the config file,
* or from enumerating the types root + initial secondary types lookup location.
* More type directives might appear in the program later as a result of loading actual source files;
* this list is only the set of defaults that are implicitly included.
*/
export function getAutomaticTypeDirectiveNames(options: CompilerOptions, rootFiles: string[], host: CompilerHost): string[] {
// Use explicit type list from tsconfig.json
if (options.types) {
return options.types;
}
// or load all types from the automatic type import fields
if (host && host.getDefaultTypeDirectiveNames) {
const commonRoot = computeCommonSourceDirectoryOfFilenames(rootFiles, host.getCurrentDirectory(), f => host.getCanonicalFileName(f));
if (commonRoot) {
return host.getDefaultTypeDirectiveNames(commonRoot);
// Walk the primary type lookup locations
let result: string[] = [];
if (host.directoryExists && host.getDirectories) {
const typeRoots = getEffectiveTypeRoots(options, host);
for (const root of typeRoots) {
if (host.directoryExists(root)) {
result = result.concat(host.getDirectories(root));
}
}
}
return undefined;
return result;
}
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program {
@@ -1038,11 +1095,13 @@ namespace ts {
if (!tryReuseStructureFromOldProgram()) {
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false));
// load type declarations specified via 'types' argument
const typeReferences: string[] = getDefaultTypeDirectiveNames(options, rootNames, host);
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
const typeReferences: string[] = getAutomaticTypeDirectiveNames(options, rootNames, host);
if (typeReferences) {
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, /*containingFile*/ undefined);
const inferredRoot = getInferredTypesRoot(options, rootNames, host);
const containingFilename = combinePaths(inferredRoot, "__inferred type names__.ts");
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename);
for (let i = 0; i < typeReferences.length; i++) {
processTypeReferenceDirective(typeReferences[i], resolutions[i]);
}
@@ -1143,16 +1202,16 @@ namespace ts {
// if any of these properties has changed - structure cannot be reused
const oldOptions = oldProgram.getCompilerOptions();
if ((oldOptions.module !== options.module) ||
(oldOptions.moduleResolution !== options.moduleResolution) ||
(oldOptions.noResolve !== options.noResolve) ||
(oldOptions.target !== options.target) ||
(oldOptions.noLib !== options.noLib) ||
(oldOptions.jsx !== options.jsx) ||
(oldOptions.allowJs !== options.allowJs) ||
(oldOptions.rootDir !== options.rootDir) ||
(oldOptions.typesSearchPaths !== options.typesSearchPaths) ||
(oldOptions.configFilePath !== options.configFilePath) ||
(oldOptions.baseUrl !== options.baseUrl) ||
(oldOptions.typesRoot !== options.typesRoot) ||
!arrayIsEqualTo(oldOptions.typeRoots, oldOptions.typeRoots) ||
!arrayIsEqualTo(oldOptions.rootDirs, options.rootDirs) ||
!mapIsEqualTo(oldOptions.paths, options.paths)) {
return false;
@@ -1712,9 +1771,12 @@ namespace ts {
// The StringLiteral must specify a top - level external module name.
// Relative external module names are not permitted
// NOTE: body of ambient module is always a module block
for (const statement of (<ModuleBlock>(<ModuleDeclaration>node).body).statements) {
collectModuleReferences(statement, /*inAmbientModule*/ true);
// NOTE: body of ambient module is always a module block, if it exists
const body = <ModuleBlock>(<ModuleDeclaration>node).body;
if (body) {
for (const statement of body.statements) {
collectModuleReferences(statement, /*inAmbientModule*/ true);
}
}
}
}
@@ -1904,7 +1966,7 @@ namespace ts {
}
}
else {
fileProcessingDiagnostics.add(createDiagnostic(refFile, refPos, refEnd, Diagnostics.Cannot_find_name_0, typeReferenceDirective));
fileProcessingDiagnostics.add(createDiagnostic(refFile, refPos, refEnd, Diagnostics.Cannot_find_type_definition_file_for_0, typeReferenceDirective));
}
if (saveResolution) {
@@ -2042,12 +2104,12 @@ namespace ts {
}
}
if (options.inlineSources) {
if (!options.sourceMap && !options.inlineSourceMap) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_inlineSources_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided));
if (!options.sourceMap && !options.inlineSourceMap) {
if (options.inlineSources) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources"));
}
if (options.sourceRoot) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceRoot", "inlineSources"));
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot"));
}
}
@@ -2055,14 +2117,9 @@ namespace ts {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile"));
}
if (!options.sourceMap && (options.mapRoot || options.sourceRoot)) {
// Error to specify --mapRoot or --sourceRoot without mapSourceFiles
if (options.mapRoot) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "mapRoot", "sourceMap"));
}
if (options.sourceRoot && !options.inlineSourceMap) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "sourceRoot", "sourceMap"));
}
if (options.mapRoot && !options.sourceMap) {
// Error to specify --mapRoot without --sourcemap
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "mapRoot", "sourceMap"));
}
if (options.declarationDir) {
@@ -2099,11 +2156,6 @@ namespace ts {
programDiagnostics.add(createFileDiagnostic(firstExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_use_imports_exports_or_module_augmentations_when_module_is_none));
}
// Cannot specify module gen target of es6 when below es6
if (options.module === ModuleKind.ES6 && languageVersion < ScriptTarget.ES6) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_compile_modules_into_es2015_when_targeting_ES5_or_lower));
}
// Cannot specify module gen that isn't amd or system with --out
if (outFile) {
if (options.module && !(options.module === ModuleKind.AMD || options.module === ModuleKind.System)) {
+4 -1
View File
@@ -434,7 +434,7 @@ namespace ts {
}
/* @internal */
export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean): number {
export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean, stopAtComments = false): number {
// Using ! with a greater than test is a fast way of testing the following conditions:
// pos === undefined || pos === null || isNaN(pos) || pos < 0;
if (!(pos >= 0)) {
@@ -462,6 +462,9 @@ namespace ts {
pos++;
continue;
case CharacterCodes.slash:
if (stopAtComments) {
break;
}
if (text.charCodeAt(pos + 1) === CharacterCodes.slash) {
pos += 2;
while (pos < text.length) {
+1 -1
View File
@@ -168,7 +168,7 @@ namespace ts {
sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] :
defaultLastEncodedSourceMapSpan;
// TODO: Update lastEncodedNameIndex
// TODO: Update lastEncodedNameIndex
// Since we dont support this any more, lets not worry about it right now.
// When we start supporting nameIndex, we will get back to this
+1 -1
View File
@@ -2,7 +2,7 @@
namespace ts {
export type FileWatcherCallback = (fileName: string, removed?: boolean) => void;
export type DirectoryWatcherCallback = (directoryName: string) => void;
export type DirectoryWatcherCallback = (fileName: string) => void;
export interface WatchedFile {
fileName: string;
callback: FileWatcherCallback;
+103 -51
View File
@@ -343,6 +343,9 @@ namespace ts {
JSDocReturnTag,
JSDocTypeTag,
JSDocTemplateTag,
JSDocTypedefTag,
JSDocPropertyTag,
JSDocTypeLiteral,
// Synthesized list
SyntaxList,
@@ -372,6 +375,10 @@ namespace ts {
FirstBinaryOperator = LessThanToken,
LastBinaryOperator = CaretEqualsToken,
FirstNode = QualifiedName,
FirstJSDocNode = JSDocTypeExpression,
LastJSDocNode = JSDocTypeLiteral,
FirstJSDocTagNode = JSDocComment,
LastJSDocTagNode = JSDocTypeLiteral
}
export const enum NodeFlags {
@@ -416,6 +423,7 @@ namespace ts {
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,
EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions,
ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags,
// Parsing context flags
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
@@ -448,7 +456,7 @@ namespace ts {
modifiers?: ModifiersArray; // Array of modifiers
/* @internal */ id?: number; // Unique id (used to look up NodeLinks)
parent?: Node; // Parent node (initialized by binding
/* @internal */ jsDocComment?: JSDocComment; // JSDoc for the node, if it has any. Only for .js files.
/* @internal */ jsDocComments?: JSDocComment[]; // JSDoc for the node, if it has any. Only for .js files.
/* @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)
@@ -612,6 +620,7 @@ namespace ts {
// SyntaxKind.PropertyAssignment
// SyntaxKind.ShorthandPropertyAssignment
// SyntaxKind.EnumMember
// SyntaxKind.JSDocPropertyTag
export interface VariableLikeDeclaration extends Declaration {
propertyName?: PropertyName;
dotDotDotToken?: Node;
@@ -1292,7 +1301,7 @@ namespace ts {
// @kind(SyntaxKind.ModuleDeclaration)
export interface ModuleDeclaration extends DeclarationStatement {
name: Identifier | LiteralExpression;
body: ModuleBlock | ModuleDeclaration;
body?: ModuleBlock | ModuleDeclaration;
}
// @kind(SyntaxKind.ModuleBlock)
@@ -1510,6 +1519,25 @@ namespace ts {
typeExpression: JSDocTypeExpression;
}
// @kind(SyntaxKind.JSDocTypedefTag)
export interface JSDocTypedefTag extends JSDocTag, Declaration {
name?: Identifier;
typeExpression?: JSDocTypeExpression;
jsDocTypeLiteral?: JSDocTypeLiteral;
}
// @kind(SyntaxKind.JSDocPropertyTag)
export interface JSDocPropertyTag extends JSDocTag, TypeElement {
name: Identifier;
typeExpression: JSDocTypeExpression;
}
// @kind(SyntaxKind.JSDocTypeLiteral)
export interface JSDocTypeLiteral extends JSDocType {
jsDocPropertyTags?: NodeArray<JSDocPropertyTag>;
jsDocTypeTag?: JSDocTypeTag;
}
// @kind(SyntaxKind.JSDocParameterTag)
export interface JSDocParameterTag extends JSDocTag {
preParameterName?: Identifier;
@@ -1537,6 +1565,13 @@ namespace ts {
id?: number; // Node id used by flow type cache in checker
}
// FlowStart represents the start of a control flow. For a function expression or arrow
// function, the container property references the function (which in turn has a flowNode
// property for the containing control flow).
export interface FlowStart extends FlowNode {
container?: FunctionExpression | ArrowFunction;
}
// FlowLabel represents a junction with multiple possible preceding control flows.
export interface FlowLabel extends FlowNode {
antecedents: FlowNode[];
@@ -1623,6 +1658,7 @@ namespace ts {
/* @internal */ resolvedTypeReferenceDirectiveNames: Map<ResolvedTypeReferenceDirective>;
/* @internal */ imports: LiteralExpression[];
/* @internal */ moduleAugmentations: LiteralExpression[];
/* @internal */ patternAmbientModules?: PatternAmbientModule[];
}
export interface ScriptReferenceHost {
@@ -2108,6 +2144,20 @@ namespace ts {
[index: string]: Symbol;
}
/** Represents a "prefix*suffix" pattern. */
/* @internal */
export interface Pattern {
prefix: string;
suffix: string;
}
/** Used to track a `declare module "foo*"`-like declaration. */
/* @internal */
export interface PatternAmbientModule {
pattern: Pattern;
symbol: Symbol;
}
/* @internal */
export const enum NodeCheckFlags {
TypeChecked = 0x00000001, // Node has been type checked
@@ -2170,7 +2220,7 @@ namespace ts {
/* @internal */
FreshObjectLiteral = 0x00100000, // Fresh object literal type
/* @internal */
ContainsUndefinedOrNull = 0x00200000, // Type is or contains undefined or null type
ContainsWideningType = 0x00200000, // Type is or contains undefined or null widening type
/* @internal */
ContainsObjectLiteral = 0x00400000, // Type is or contains object literal type
/* @internal */
@@ -2182,6 +2232,7 @@ namespace ts {
/* @internal */
Nullable = Undefined | Null,
Falsy = String | Number | Boolean | Void | Undefined | Null,
/* @internal */
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null | Never,
/* @internal */
@@ -2191,11 +2242,14 @@ namespace ts {
ObjectType = Class | Interface | Reference | Tuple | Anonymous,
UnionOrIntersection = Union | Intersection,
StructuredType = ObjectType | Union | Intersection,
Narrowable = Any | ObjectType | Union | TypeParameter,
// 'Narrowable' types are types where narrowing actually narrows.
// This *should* be every type other than null, undefined, void, and never
Narrowable = Any | StructuredType | TypeParameter | StringLike | NumberLike | Boolean | ESSymbol,
/* @internal */
RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral,
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
/* @internal */
PropagatingFlags = ContainsUndefinedOrNull | ContainsObjectLiteral | ContainsAnyFunctionType
PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | ContainsAnyFunctionType
}
export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
@@ -2453,83 +2507,75 @@ namespace ts {
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | TsConfigOnlyOptions;
export interface CompilerOptions {
allowNonTsExtensions?: boolean;
allowJs?: boolean;
/*@internal*/ allowNonTsExtensions?: boolean;
allowSyntheticDefaultImports?: boolean;
allowUnreachableCode?: boolean;
allowUnusedLabels?: boolean;
baseUrl?: string;
charset?: string;
/* @internal */ configFilePath?: string;
declaration?: boolean;
declarationDir?: string;
diagnostics?: boolean;
/* @internal */ diagnostics?: boolean;
emitBOM?: boolean;
help?: boolean;
init?: boolean;
emitDecoratorMetadata?: boolean;
experimentalDecorators?: boolean;
forceConsistentCasingInFileNames?: boolean;
/*@internal*/help?: boolean;
/*@internal*/init?: boolean;
inlineSourceMap?: boolean;
inlineSources?: boolean;
isolatedModules?: boolean;
jsx?: JsxEmit;
reactNamespace?: string;
listFiles?: boolean;
typesSearchPaths?: string[];
lib?: string[];
/*@internal*/listEmittedFiles?: boolean;
/*@internal*/listFiles?: boolean;
locale?: string;
mapRoot?: string;
module?: ModuleKind;
moduleResolution?: ModuleResolutionKind;
newLine?: NewLineKind;
noEmit?: boolean;
noEmitHelpers?: boolean;
noEmitOnError?: boolean;
noErrorTruncation?: boolean;
noFallthroughCasesInSwitch?: boolean;
noImplicitAny?: boolean;
noImplicitReturns?: boolean;
noImplicitThis?: boolean;
noImplicitUseStrict?: boolean;
noLib?: boolean;
noResolve?: boolean;
out?: string;
outFile?: string;
outDir?: string;
outFile?: string;
paths?: PathSubstitutions;
preserveConstEnums?: boolean;
/* @internal */ pretty?: DiagnosticStyle;
project?: string;
/* @internal */ pretty?: DiagnosticStyle;
reactNamespace?: string;
removeComments?: boolean;
rootDir?: string;
rootDirs?: RootPaths;
skipLibCheck?: boolean;
skipDefaultLibCheck?: boolean;
sourceMap?: boolean;
sourceRoot?: string;
strictNullChecks?: boolean;
/* @internal */ stripInternal?: boolean;
suppressExcessPropertyErrors?: boolean;
suppressImplicitAnyIndexErrors?: boolean;
target?: ScriptTarget;
version?: boolean;
watch?: boolean;
isolatedModules?: boolean;
experimentalDecorators?: boolean;
emitDecoratorMetadata?: boolean;
moduleResolution?: ModuleResolutionKind;
allowUnusedLabels?: boolean;
allowUnreachableCode?: boolean;
noImplicitReturns?: boolean;
noFallthroughCasesInSwitch?: boolean;
forceConsistentCasingInFileNames?: boolean;
baseUrl?: string;
paths?: PathSubstitutions;
rootDirs?: RootPaths;
traceResolution?: boolean;
allowSyntheticDefaultImports?: boolean;
allowJs?: boolean;
noImplicitUseStrict?: boolean;
strictNullChecks?: boolean;
skipLibCheck?: boolean;
listEmittedFiles?: boolean;
lib?: string[];
/* @internal */ stripInternal?: boolean;
// Skip checking lib.d.ts to help speed up tests.
/* @internal */ skipDefaultLibCheck?: boolean;
// Do not perform validation of output file name in transpile scenarios
/* @internal */ suppressOutputPathCheck?: boolean;
/* @internal */
// When options come from a config file, its path is recorded here
configFilePath?: string;
/* @internal */
// Path used to used to compute primary search locations
typesRoot?: string;
target?: ScriptTarget;
traceResolution?: boolean;
types?: string[];
/** Paths used to used to compute primary types search locations */
typeRoots?: string[];
typesSearchPaths?: string[];
/*@internal*/ version?: boolean;
/*@internal*/ watch?: boolean;
list?: string[];
[option: string]: CompilerOptionsValue | undefined;
}
@@ -2845,6 +2891,7 @@ namespace ts {
getDefaultTypeDirectiveNames?(rootPath: string): string[];
writeFile: WriteFileCallback;
getCurrentDirectory(): string;
getDirectories(path: string): string[];
getCanonicalFileName(fileName: string): string;
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
@@ -2894,4 +2941,9 @@ namespace ts {
/* @internal */ reattachFileDiagnostics(newFile: SourceFile): void;
}
// SyntaxKind.SyntaxList
export interface SyntaxList extends Node {
_children: Node[];
}
}
+71 -32
View File
@@ -287,16 +287,36 @@ namespace ts {
return !nodeIsMissing(node);
}
export function getTokenPosOfNode(node: Node, sourceFile?: SourceFile): number {
export function getTokenPosOfNode(node: Node, sourceFile?: SourceFile, includeJsDocComment?: boolean): number {
// With nodes that have no width (i.e. 'Missing' nodes), we actually *don't*
// want to skip trivia because this will launch us forward to the next token.
if (nodeIsMissing(node)) {
return node.pos;
}
if (isJSDocNode(node)) {
return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
}
if (includeJsDocComment && node.jsDocComments && node.jsDocComments.length > 0) {
return getTokenPosOfNode(node.jsDocComments[0]);
}
// For a syntax list, it is possible that one of its children has JSDocComment nodes, while
// the syntax list itself considers them as normal trivia. Therefore if we simply skip
// trivia for the list, we may have skipped the JSDocComment as well. So we should process its
// first child to determine the actual position of its first token.
if (node.kind === SyntaxKind.SyntaxList && (<SyntaxList>node)._children.length > 0) {
return getTokenPosOfNode((<SyntaxList>node)._children[0], sourceFile, includeJsDocComment);
}
return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos);
}
export function isJSDocNode(node: Node) {
return node.kind >= SyntaxKind.FirstJSDocNode && node.kind <= SyntaxKind.LastJSDocNode;
}
export function getNonDecoratorTokenPosOfNode(node: Node, sourceFile?: SourceFile): number {
if (nodeIsMissing(node) || !node.decorators) {
return getTokenPosOfNode(node, sourceFile);
@@ -352,6 +372,11 @@ namespace ts {
((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(<ModuleDeclaration>node));
}
export function isShorthandAmbientModule(node: Node): boolean {
// The only kind of module that can be missing a body is a shorthand ambient module.
return node.kind === SyntaxKind.ModuleDeclaration && (!(<ModuleDeclaration>node).body);
}
export function isBlockScopedContainerTopLevel(node: Node): boolean {
return node.kind === SyntaxKind.SourceFile ||
node.kind === SyntaxKind.ModuleDeclaration ||
@@ -583,9 +608,11 @@ namespace ts {
}
export function getJsDocCommentsFromText(node: Node, text: string) {
const commentRanges = (node.kind === SyntaxKind.Parameter || node.kind === SyntaxKind.TypeParameter) ?
concatenate(getTrailingCommentRanges(text, node.pos),
getLeadingCommentRanges(text, node.pos)) :
const commentRanges = (node.kind === SyntaxKind.Parameter ||
node.kind === SyntaxKind.TypeParameter ||
node.kind === SyntaxKind.FunctionExpression ||
node.kind === SyntaxKind.ArrowFunction) ?
concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) :
getLeadingCommentRangesOfNodeFromText(node, text);
return filter(commentRanges, isJsDocComment);
@@ -803,6 +830,8 @@ namespace ts {
case SyntaxKind.ConstructorType:
return true;
}
return false;
}
export function introducesArgumentsExoticObject(node: Node) {
@@ -860,15 +889,6 @@ namespace ts {
}
}
export function getContainingFunctionOrModule(node: Node): Node {
while (true) {
node = node.parent;
if (isFunctionLike(node) || node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.SourceFile) {
return node;
}
}
}
export function getContainingClass(node: Node): ClassLikeDeclaration {
while (true) {
node = node.parent;
@@ -987,6 +1007,20 @@ namespace ts {
}
}
export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression {
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
let prev = func;
let parent = func.parent;
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
prev = parent;
parent = parent.parent;
}
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
return parent as CallExpression;
}
}
}
/**
* Determines whether a node is a property or element access expression for super.
*/
@@ -1322,21 +1356,23 @@ namespace ts {
return undefined;
}
const jsDocComment = getJSDocComment(node, checkParentVariableStatement);
if (!jsDocComment) {
const jsDocComments = getJSDocComments(node, checkParentVariableStatement);
if (!jsDocComments) {
return undefined;
}
for (const tag of jsDocComment.tags) {
if (tag.kind === kind) {
return tag;
for (const jsDocComment of jsDocComments) {
for (const tag of jsDocComment.tags) {
if (tag.kind === kind) {
return tag;
}
}
}
}
function getJSDocComment(node: Node, checkParentVariableStatement: boolean): JSDocComment {
if (node.jsDocComment) {
return node.jsDocComment;
function getJSDocComments(node: Node, checkParentVariableStatement: boolean): JSDocComment[] {
if (node.jsDocComments) {
return node.jsDocComments;
}
// Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
// /**
@@ -1352,7 +1388,7 @@ namespace ts {
const variableStatementNode = isInitializerOfVariableDeclarationInStatement ? node.parent.parent.parent : undefined;
if (variableStatementNode) {
return variableStatementNode.jsDocComment;
return variableStatementNode.jsDocComments;
}
// Also recognize when the node is the RHS of an assignment expression
@@ -1363,12 +1399,12 @@ namespace ts {
(parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken &&
parent.parent.kind === SyntaxKind.ExpressionStatement;
if (isSourceOfAssignmentExpressionStatement) {
return parent.parent.jsDocComment;
return parent.parent.jsDocComments;
}
const isPropertyAssignmentExpression = parent && parent.kind === SyntaxKind.PropertyAssignment;
if (isPropertyAssignmentExpression) {
return parent.jsDocComment;
return parent.jsDocComments;
}
}
@@ -1393,14 +1429,16 @@ namespace ts {
// annotation.
const parameterName = (<Identifier>parameter.name).text;
const jsDocComment = getJSDocComment(parameter.parent, /*checkParentVariableStatement*/ true);
if (jsDocComment) {
for (const tag of jsDocComment.tags) {
if (tag.kind === SyntaxKind.JSDocParameterTag) {
const parameterTag = <JSDocParameterTag>tag;
const name = parameterTag.preParameterName || parameterTag.postParameterName;
if (name.text === parameterName) {
return parameterTag;
const jsDocComments = getJSDocComments(parameter.parent, /*checkParentVariableStatement*/ true);
if (jsDocComments) {
for (const jsDocComment of jsDocComments) {
for (const tag of jsDocComment.tags) {
if (tag.kind === SyntaxKind.JSDocParameterTag) {
const parameterTag = <JSDocParameterTag>tag;
const name = parameterTag.preParameterName || parameterTag.postParameterName;
if (name.text === parameterName) {
return parameterTag;
}
}
}
}
@@ -1525,6 +1563,7 @@ namespace ts {
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.TypeParameter:
case SyntaxKind.VariableDeclaration:
case SyntaxKind.JSDocTypedefTag:
return true;
}
return false;
+12 -3
View File
@@ -1,6 +1,7 @@
/// <reference path="harness.ts" />
/// <reference path="runnerbase.ts" />
/// <reference path="typeWriter.ts" />
// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`.
/* tslint:disable:no-null-keyword */
const enum CompilerTestType {
@@ -11,7 +12,7 @@ const enum CompilerTestType {
class CompilerBaselineRunner extends RunnerBase {
private basePath = "tests/cases";
private testSuiteName: string;
private testSuiteName: TestRunnerKind;
private errors: boolean;
private emit: boolean;
private decl: boolean;
@@ -40,6 +41,14 @@ class CompilerBaselineRunner extends RunnerBase {
this.basePath += "/" + this.testSuiteName;
}
public kind() {
return this.testSuiteName;
}
public enumerateTestFiles() {
return this.enumerateFiles(this.basePath, /\.tsx?$/, { recursive: true });
}
private makeUnitName(name: string, root: string) {
return ts.isRootedDiskPath(name) ? name : ts.combinePaths(root, name);
};
@@ -145,7 +154,7 @@ class CompilerBaselineRunner extends RunnerBase {
it (`Correct module resolution tracing for ${fileName}`, () => {
if (options.traceResolution) {
Harness.Baseline.runBaseline("Correct sourcemap content for " + fileName, justName.replace(/\.tsx?$/, ".trace.json"), () => {
Harness.Baseline.runBaseline("Correct module resolution tracing for " + fileName, justName.replace(/\.tsx?$/, ".trace.json"), () => {
return JSON.stringify(result.traceResults || [], undefined, 4);
});
}
@@ -390,7 +399,7 @@ class CompilerBaselineRunner extends RunnerBase {
// this will set up a series of describe/it blocks to run between the setup and cleanup phases
if (this.tests.length === 0) {
const testFiles = this.enumerateFiles(this.basePath, /\.tsx?$/, { recursive: true });
const testFiles = this.enumerateTestFiles();
testFiles.forEach(fn => {
fn = fn.replace(/\\/g, "/");
this.checkTestCodeOutput(fn);
+1 -2
View File
@@ -172,8 +172,7 @@ declare module chai {
function lengthOf(object: any[], length: number, message?: string): void;
function isTrue(value: any, message?: string): void;
function isFalse(value: any, message?: string): void;
function isNull(value: any, message?: string): void;
function isNotNull(value: any, message?: string): void;
function isOk(actual: any, message?: string): void;
function isUndefined(value: any, message?: string): void;
function isDefined(value: any, message?: string): void;
}
+68 -116
View File
@@ -18,7 +18,6 @@
/// <reference path="harnessLanguageService.ts" />
/// <reference path="harness.ts" />
/// <reference path="fourslashRunner.ts" />
/* tslint:disable:no-null-keyword */
namespace FourSlash {
ts.disableIncrementalParsing = false;
@@ -198,7 +197,7 @@ namespace FourSlash {
public lastKnownMarker: string = "";
// The file that's currently 'opened'
public activeFile: FourSlashFile = null;
public activeFile: FourSlashFile;
// Whether or not we should format on keystrokes
public enableFormatting = true;
@@ -247,8 +246,8 @@ namespace FourSlash {
// Create a new Services Adapter
this.cancellationToken = new TestCancellationToken();
const compilationOptions = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions);
if (compilationOptions.typesRoot) {
compilationOptions.typesRoot = ts.getNormalizedAbsolutePath(compilationOptions.typesRoot, this.basePath);
if (compilationOptions.typeRoots) {
compilationOptions.typeRoots = compilationOptions.typeRoots.map(p => ts.getNormalizedAbsolutePath(p, this.basePath));
}
const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions);
@@ -747,7 +746,7 @@ namespace FourSlash {
}
const missingItem = { fileName: fileName, start: start, end: end, isWriteAccess: isWriteAccess };
this.raiseError(`verifyReferencesAtPositionListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(references, undefined, 2)})`);
this.raiseError(`verifyReferencesAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(references)})`);
}
public verifyReferencesCountIs(count: number, localFilesOnly = true) {
@@ -801,7 +800,7 @@ namespace FourSlash {
private testDiagnostics(expected: string, diagnostics: ts.Diagnostic[]) {
const realized = ts.realizeDiagnostics(diagnostics, "\r\n");
const actual = JSON.stringify(realized, undefined, 2);
const actual = stringify(realized);
assert.equal(actual, expected);
}
@@ -876,7 +875,7 @@ namespace FourSlash {
}
if (ranges.length !== references.length) {
this.raiseError("Rename location count does not match result.\n\nExpected: " + JSON.stringify(ranges, undefined, 2) + "\n\nActual:" + JSON.stringify(references, undefined, 2));
this.raiseError("Rename location count does not match result.\n\nExpected: " + stringify(ranges) + "\n\nActual:" + stringify(references));
}
ranges = ranges.sort((r1, r2) => r1.start - r2.start);
@@ -889,7 +888,7 @@ namespace FourSlash {
if (reference.textSpan.start !== range.start ||
ts.textSpanEnd(reference.textSpan) !== range.end) {
this.raiseError("Rename location results do not match.\n\nExpected: " + JSON.stringify(ranges, undefined, 2) + "\n\nActual:" + JSON.stringify(references, undefined, 2));
this.raiseError("Rename location results do not match.\n\nExpected: " + stringify(ranges) + "\n\nActual:" + JSON.stringify(references));
}
}
}
@@ -922,7 +921,7 @@ namespace FourSlash {
public verifyCurrentParameterIsletiable(isVariable: boolean) {
const signature = this.getActiveSignatureHelpItem();
assert.isNotNull(signature);
assert.isOk(signature);
assert.equal(isVariable, signature.isVariadic);
}
@@ -973,7 +972,7 @@ namespace FourSlash {
}
else {
if (actual) {
this.raiseError(`Expected no signature help, but got "${JSON.stringify(actual, undefined, 2)}"`);
this.raiseError(`Expected no signature help, but got "${stringify(actual)}"`);
}
}
}
@@ -1177,7 +1176,7 @@ namespace FourSlash {
public printCurrentParameterHelp() {
const help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition);
Harness.IO.log(JSON.stringify(help, undefined, 2));
Harness.IO.log(stringify(help));
}
public printCurrentQuickInfo() {
@@ -1219,7 +1218,7 @@ namespace FourSlash {
public printCurrentSignatureHelp() {
const sigHelp = this.getActiveSignatureHelpItem();
Harness.IO.log(JSON.stringify(sigHelp, undefined, 2));
Harness.IO.log(stringify(sigHelp));
}
public printMemberListMembers() {
@@ -1249,7 +1248,7 @@ namespace FourSlash {
public printReferences() {
const references = this.getReferencesAtCaret();
ts.forEach(references, entry => {
Harness.IO.log(JSON.stringify(entry, undefined, 2));
Harness.IO.log(stringify(entry));
});
}
@@ -1487,6 +1486,12 @@ namespace FourSlash {
this.fixCaretPosition();
}
public formatOnType(pos: number, key: string) {
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeOptions);
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
this.fixCaretPosition();
}
private updateMarkersForEdit(fileName: string, minChar: number, limChar: number, text: string) {
for (let i = 0; i < this.testData.markers.length; i++) {
const marker = this.testData.markers[i];
@@ -1740,8 +1745,8 @@ namespace FourSlash {
function jsonMismatchString() {
return Harness.IO.newLine() +
"expected: '" + Harness.IO.newLine() + JSON.stringify(expected, undefined, 2) + "'" + Harness.IO.newLine() +
"actual: '" + Harness.IO.newLine() + JSON.stringify(actual, undefined, 2) + "'";
"expected: '" + Harness.IO.newLine() + stringify(expected) + "'" + Harness.IO.newLine() +
"actual: '" + Harness.IO.newLine() + stringify(actual) + "'";
}
}
@@ -1911,7 +1916,7 @@ namespace FourSlash {
public verifyNavigationItemsCount(expected: number, searchValue: string, matchKind?: string) {
const items = this.languageService.getNavigateToItems(searchValue);
let actual = 0;
let item: ts.NavigateToItem = null;
let item: ts.NavigateToItem;
// Count only the match that match the same MatchKind
for (let i = 0; i < items.length; i++) {
@@ -1956,73 +1961,27 @@ namespace FourSlash {
// if there was an explicit match kind specified, then it should be validated.
if (matchKind !== undefined) {
const missingItem = { name: name, kind: kind, searchValue: searchValue, matchKind: matchKind, fileName: fileName, parentName: parentName };
this.raiseError(`verifyNavigationItemsListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(items, undefined, 2)})`);
this.raiseError(`verifyNavigationItemsListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(items)})`);
}
}
public verifyNavigationBarCount(expected: number) {
public verifyNavigationBar(json: any) {
const items = this.languageService.getNavigationBarItems(this.activeFile.fileName);
const actual = this.getNavigationBarItemsCount(items);
if (expected !== actual) {
this.raiseError(`verifyNavigationBarCount failed - found: ${actual} navigation items, expected: ${expected}.`);
}
}
private getNavigationBarItemsCount(items: ts.NavigationBarItem[]) {
let result = 0;
if (items) {
for (let i = 0, n = items.length; i < n; i++) {
result++;
result += this.getNavigationBarItemsCount(items[i].childItems);
}
if (JSON.stringify(items, replacer) !== JSON.stringify(json)) {
this.raiseError(`verifyNavigationBar failed - expected: ${stringify(json)}, got: ${stringify(items, replacer)}`);
}
return result;
}
public verifyNavigationBarContains(name: string, kind: string, fileName?: string, parentName?: string, isAdditionalSpan?: boolean, markerPosition?: number) {
fileName = fileName || this.activeFile.fileName;
const items = this.languageService.getNavigationBarItems(fileName);
if (!items || items.length === 0) {
this.raiseError("verifyNavigationBarContains failed - found 0 navigation items, expected at least one.");
}
if (this.navigationBarItemsContains(items, name, kind, parentName)) {
return;
}
const missingItem = { name, kind, parentName };
this.raiseError(`verifyNavigationBarContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(items, undefined, 2)})`);
}
private navigationBarItemsContains(items: ts.NavigationBarItem[], name: string, kind: string, parentName?: string) {
function recur(items: ts.NavigationBarItem[], curParentName: string) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item && item.text === name && item.kind === kind && (!parentName || curParentName === parentName)) {
return true;
}
if (recur(item.childItems, item.text)) {
return true;
}
}
return false;
}
return recur(items, "");
}
public verifyNavigationBarChildItem(parent: string, name: string, kind: string) {
const items = this.languageService.getNavigationBarItems(this.activeFile.fileName);
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.text === parent) {
if (this.navigationBarItemsContains(item.childItems, name, kind))
return;
const missingItem = { name, kind };
this.raiseError(`verifyNavigationBarChildItem failed - could not find the item: ${JSON.stringify(missingItem)} in the children list: (${JSON.stringify(item.childItems, undefined, 2)})`);
// Make the data easier to read.
function replacer(key: string, value: any) {
switch (key) {
case "spans":
// We won't ever check this.
return undefined;
case "childItems":
return value.length === 0 ? undefined : value;
default:
// Omit falsy values, those are presumed to be the default.
return value || undefined;
}
}
}
@@ -2072,7 +2031,7 @@ namespace FourSlash {
}
const missingItem = { fileName: fileName, start: start, end: end, isWriteAccess: isWriteAccess };
this.raiseError(`verifyOccurrencesAtPositionListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(occurrences, undefined, 2)})`);
this.raiseError(`verifyOccurrencesAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(occurrences)})`);
}
public verifyOccurrencesAtPositionListCount(expectedCount: number) {
@@ -2111,7 +2070,7 @@ namespace FourSlash {
}
const missingItem = { fileName: fileName, start: start, end: end, kind: kind };
this.raiseError(`verifyDocumentHighlightsAtPositionListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(documentHighlights, undefined, 2)})`);
this.raiseError(`verifyDocumentHighlightsAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(documentHighlights)})`);
}
public verifyDocumentHighlightsAtPositionListCount(expectedCount: number, fileNamesToSearch: string[]) {
@@ -2177,13 +2136,13 @@ namespace FourSlash {
}
}
const itemsString = items.map((item) => JSON.stringify({ name: item.name, kind: item.kind }, undefined, 2)).join(",\n");
const itemsString = items.map(item => stringify({ name: item.name, kind: item.kind })).join(",\n");
this.raiseError(`Expected "${JSON.stringify({ name, text, documentation, kind }, undefined, 2)}" to be in list [${itemsString}]`);
this.raiseError(`Expected "${stringify({ name, text, documentation, kind })}" to be in list [${itemsString}]`);
}
private findFile(indexOrName: any) {
let result: FourSlashFile = null;
let result: FourSlashFile;
if (typeof indexOrName === "number") {
const index = <number>indexOrName;
if (index >= this.testData.files.length) {
@@ -2352,10 +2311,16 @@ ${code}
const ranges: Range[] = [];
// Stuff related to the subfile we're parsing
let currentFileContent: string = null;
let currentFileContent: string = undefined;
let currentFileName = fileName;
let currentFileOptions: { [s: string]: string } = {};
function resetLocalData() {
currentFileContent = undefined;
currentFileOptions = {};
currentFileName = fileName;
}
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
const lineLength = line.length;
@@ -2368,7 +2333,7 @@ ${code}
// Subfile content line
// Append to the current subfile content, inserting a newline needed
if (currentFileContent === null) {
if (currentFileContent === undefined) {
currentFileContent = "";
}
else {
@@ -2400,10 +2365,7 @@ ${code}
// Store result file
files.push(file);
// Reset local data
currentFileContent = null;
currentFileOptions = {};
currentFileName = fileName;
resetLocalData();
}
currentFileName = basePath + "/" + match[2];
@@ -2430,10 +2392,7 @@ ${code}
// Store result file
files.push(file);
// Reset local data
currentFileContent = null;
currentFileOptions = {};
currentFileName = fileName;
resetLocalData();
}
}
}
@@ -2498,7 +2457,7 @@ ${code}
if (markerValue === undefined) {
reportError(fileName, location.sourceLine, location.sourceColumn, "Object markers can not be empty");
return null;
return undefined;
}
const marker: Marker = {
@@ -2527,7 +2486,7 @@ ${code}
if (markerMap[name] !== undefined) {
const message = "Marker '" + name + "' is duplicated in the source file contents.";
reportError(marker.fileName, location.sourceLine, location.sourceColumn, message);
return null;
return undefined;
}
else {
markerMap[name] = marker;
@@ -2546,7 +2505,7 @@ ${code}
let output = "";
/// The current marker (or maybe multi-line comment?) we're parsing, possibly
let openMarker: LocationInformation = null;
let openMarker: LocationInformation = undefined;
/// A stack of the open range markers that are still unclosed
const openRanges: RangeLocationInformation[] = [];
@@ -2654,7 +2613,7 @@ ${code}
difference += i + 1 - openMarker.sourcePosition;
// Reset the state
openMarker = null;
openMarker = undefined;
state = State.none;
}
break;
@@ -2676,7 +2635,7 @@ ${code}
difference += i + 1 - openMarker.sourcePosition;
// Reset the state
openMarker = null;
openMarker = undefined;
state = State.none;
}
else if (validMarkerChars.indexOf(currentChar) < 0) {
@@ -2688,7 +2647,7 @@ ${code}
// Bail out the text we've gathered so far back into the output
flush(i);
lastNormalCharPosition = i;
openMarker = null;
openMarker = undefined;
state = State.none;
}
@@ -2719,7 +2678,7 @@ ${code}
reportError(fileName, openRange.sourceLine, openRange.sourceColumn, "Unterminated range.");
}
if (openMarker !== null) {
if (openMarker) {
reportError(fileName, openMarker.sourceLine, openMarker.sourceColumn, "Unterminated marker.");
}
@@ -2742,6 +2701,10 @@ ${code}
}
return result;
}
function stringify(data: any, replacer?: (key: string, value: any) => any): string {
return JSON.stringify(data, replacer, 2);
}
}
namespace FourSlashInterface {
@@ -3043,23 +3006,8 @@ namespace FourSlashInterface {
this.DocCommentTemplate(/*expectedText*/ undefined, /*expectedOffset*/ undefined, /*empty*/ true);
}
public navigationBarCount(count: number) {
this.state.verifyNavigationBarCount(count);
}
// TODO: figure out what to do with the unused arguments.
public navigationBarContains(
name: string,
kind: string,
fileName?: string,
parentName?: string,
isAdditionalSpan?: boolean,
markerPosition?: number) {
this.state.verifyNavigationBarContains(name, kind, fileName, parentName, isAdditionalSpan, markerPosition);
}
public navigationBarChildItem(parent: string, name: string, kind: string) {
this.state.verifyNavigationBarChildItem(parent, name, kind);
public navigationBar(json: any) {
this.state.verifyNavigationBar(json);
}
public navigationItemsListCount(count: number, searchValue: string, matchKind?: string) {
@@ -3285,6 +3233,10 @@ namespace FourSlashInterface {
this.state.formatSelection(this.state.getMarkerByName(startMarker).position, this.state.getMarkerByName(endMarker).position);
}
public onType(posMarker: string, key: string) {
this.state.formatOnType(this.state.getMarkerByName(posMarker).position, key);
}
public setOption(name: string, value: number): void;
public setOption(name: string, value: string): void;
public setOption(name: string, value: boolean): void;
+10 -3
View File
@@ -1,7 +1,6 @@
///<reference path="fourslash.ts" />
///<reference path="harness.ts"/>
///<reference path="runnerbase.ts" />
/* tslint:disable:no-null-keyword */
const enum FourSlashTestType {
Native,
@@ -12,7 +11,7 @@ const enum FourSlashTestType {
class FourSlashRunner extends RunnerBase {
protected basePath: string;
protected testSuiteName: string;
protected testSuiteName: TestRunnerKind;
constructor(private testType: FourSlashTestType) {
super();
@@ -36,9 +35,17 @@ class FourSlashRunner extends RunnerBase {
}
}
public enumerateTestFiles() {
return this.enumerateFiles(this.basePath, /\.ts/i, { recursive: false });
}
public kind() {
return this.testSuiteName;
}
public initializeTests() {
if (this.tests.length === 0) {
this.tests = this.enumerateFiles(this.basePath, /\.ts/i, { recursive: false });
this.tests = this.enumerateTestFiles();
}
describe(this.testSuiteName + " tests", () => {
+71 -43
View File
@@ -24,7 +24,6 @@
/// <reference path="sourceMapRecorder.ts"/>
/// <reference path="runnerbase.ts"/>
/// <reference path="virtualFileSystem.ts" />
/* tslint:disable:no-null-keyword */
// Block scoped definitions work poorly for global variables, temporarily enable var
/* tslint:disable:no-var-keyword */
@@ -33,7 +32,7 @@
var _chai: typeof chai = require("chai");
var assert: typeof _chai.assert = _chai.assert;
declare var __dirname: string; // Node-specific
var global = <any>Function("return this").call(null);
var global = <any>Function("return this").call(undefined);
/* tslint:enable:no-var-keyword */
namespace Utils {
@@ -223,6 +222,19 @@ namespace Utils {
return k;
}
// For some markers in SyntaxKind, we should print its original syntax name instead of
// the marker name in tests.
if (k === (<any>ts).SyntaxKind.FirstJSDocNode ||
k === (<any>ts).SyntaxKind.LastJSDocNode ||
k === (<any>ts).SyntaxKind.FirstJSDocTagNode ||
k === (<any>ts).SyntaxKind.LastJSDocTagNode) {
for (const kindName in (<any>ts).SyntaxKind) {
if ((<any>ts).SyntaxKind[kindName] === k) {
return kindName;
}
}
}
return (<any>ts).SyntaxKind[k];
}
@@ -421,6 +433,7 @@ namespace Harness {
readFile(path: string): string;
writeFile(path: string, contents: string): void;
directoryName(path: string): string;
getDirectories(path: string): string[];
createDirectory(path: string): void;
fileExists(fileName: string): boolean;
directoryExists(path: string): boolean;
@@ -466,6 +479,7 @@ namespace Harness {
export const readFile: typeof IO.readFile = path => ts.sys.readFile(path);
export const writeFile: typeof IO.writeFile = (path, content) => ts.sys.writeFile(path, content);
export const directoryName: typeof IO.directoryName = fso.GetParentFolderName;
export const getDirectories: typeof IO.getDirectories = dir => ts.sys.getDirectories(dir);
export const directoryExists: typeof IO.directoryExists = fso.FolderExists;
export const fileExists: typeof IO.fileExists = fso.FileExists;
export const log: typeof IO.log = global.WScript && global.WScript.StdOut.WriteLine;
@@ -532,6 +546,7 @@ namespace Harness {
export const args = () => ts.sys.args;
export const getExecutingFilePath = () => ts.sys.getExecutingFilePath();
export const exit = (exitCode: number) => ts.sys.exit(exitCode);
export const getDirectories: typeof IO.getDirectories = path => ts.sys.getDirectories(path);
export const readFile: typeof IO.readFile = path => ts.sys.readFile(path);
export const writeFile: typeof IO.writeFile = (path, content) => ts.sys.writeFile(path, content);
@@ -559,15 +574,9 @@ namespace Harness {
}
export function directoryName(path: string) {
let dirPath = pathModule.dirname(path);
const dirPath = pathModule.dirname(path);
// Node will just continue to repeat the root path, rather than return null
if (dirPath === path) {
dirPath = null;
}
else {
return dirPath;
}
return dirPath === path ? undefined : dirPath;
}
export let listFiles: typeof IO.listFiles = (path, spec?, options?) => {
@@ -611,6 +620,7 @@ namespace Harness {
export const args = () => <string[]>[];
export const getExecutingFilePath = () => "";
export const exit = (exitCode: number) => { };
export const getDirectories = () => <string[]>[];
export let log = (s: string) => console.log(s);
@@ -635,7 +645,7 @@ namespace Harness {
xhr.send();
}
catch (e) {
return { status: 404, responseText: null };
return { status: 404, responseText: undefined };
}
return waitForXHR(xhr);
@@ -652,7 +662,7 @@ namespace Harness {
}
catch (e) {
log(`XHR Error: ${e}`);
return { status: 500, responseText: null };
return { status: 500, responseText: undefined };
}
return waitForXHR(xhr);
@@ -664,7 +674,7 @@ namespace Harness {
}
export function deleteFile(path: string) {
Http.writeToServerSync(serverRoot + path, "DELETE", null);
Http.writeToServerSync(serverRoot + path, "DELETE");
}
export function directoryExists(path: string): boolean {
@@ -675,7 +685,7 @@ namespace Harness {
let dirPath = path;
// root of the server
if (dirPath.match(/localhost:\d+$/) || dirPath.match(/localhost:\d+\/$/)) {
dirPath = null;
dirPath = undefined;
// path + fileName
}
else if (dirPath.indexOf(".") === -1) {
@@ -723,7 +733,7 @@ namespace Harness {
return response.responseText;
}
else {
return null;
return undefined;
}
}
@@ -870,7 +880,7 @@ namespace Harness {
// Local get canonical file name function, that depends on passed in parameter for useCaseSensitiveFileNames
const getCanonicalFileName = ts.createGetCanonicalFileName(useCaseSensitiveFileNames);
let realPathMap: ts.FileMap<string>;
const realPathMap: ts.FileMap<string> = ts.createFileMap<string>();
const fileMap: ts.FileMap<() => ts.SourceFile> = ts.createFileMap<() => ts.SourceFile>();
for (const file of inputFiles) {
if (file.content !== undefined) {
@@ -879,9 +889,6 @@ namespace Harness {
if (file.fileOptions && file.fileOptions["symlink"]) {
const link = file.fileOptions["symlink"];
const linkPath = ts.toPath(link, currentDirectory, getCanonicalFileName);
if (!realPathMap) {
realPathMap = ts.createFileMap<string>();
}
realPathMap.set(linkPath, fileName);
fileMap.set(path, (): ts.SourceFile => { throw new Error("Symlinks should always be resolved to a realpath first"); });
}
@@ -915,20 +922,6 @@ namespace Harness {
return {
getDefaultTypeDirectiveNames: (path: string) => {
const results: string[] = [];
fileMap.forEachValue((key, value) => {
const rx = /node_modules\/@types\/(\w+)/;
const typeNameResult = rx.exec(key);
if (typeNameResult) {
const typeName = typeNameResult[1];
if (results.indexOf(typeName) < 0) {
results.push(typeName);
}
}
});
return results;
},
getCurrentDirectory: () => currentDirectory,
getSourceFile,
getDefaultLibFileName,
@@ -946,7 +939,37 @@ namespace Harness {
realpath: realPathMap && ((f: string) => {
const path = ts.toPath(f, currentDirectory, getCanonicalFileName);
return realPathMap.contains(path) ? realPathMap.get(path) : path;
})
}),
directoryExists: dir => {
let path = ts.toPath(dir, currentDirectory, getCanonicalFileName);
// Strip trailing /, which may exist if the path is a drive root
if (path[path.length - 1] === "/") {
path = <ts.Path>path.substr(0, path.length - 1);
}
let exists = false;
fileMap.forEachValue(key => {
if (key.indexOf(path) === 0 && key[path.length] === "/") {
exists = true;
}
});
return exists;
},
getDirectories: d => {
const path = ts.toPath(d, currentDirectory, getCanonicalFileName);
const result: string[] = [];
fileMap.forEachValue((key, value) => {
if (key.indexOf(path) === 0 && key.lastIndexOf("/") > path.length) {
let dirName = key.substr(path.length, key.indexOf("/", path.length + 1) - path.length);
if (dirName[0] === "/") {
dirName = dirName.substr(1);
}
if (result.indexOf(dirName) < 0) {
result.push(dirName);
}
}
});
return result;
}
};
}
@@ -1045,7 +1068,9 @@ namespace Harness {
options.noErrorTruncation = true;
options.skipDefaultLibCheck = true;
currentDirectory = currentDirectory || Harness.IO.getCurrentDirectory();
if (typeof currentDirectory === "undefined") {
currentDirectory = Harness.IO.getCurrentDirectory();
}
// Parse settings
if (harnessSettings) {
@@ -1433,7 +1458,9 @@ namespace Harness {
const opts: CompilerSettings = {};
let match: RegExpExecArray;
while ((match = optionRegex.exec(content)) != null) {
/* tslint:disable:no-null-keyword */
while ((match = optionRegex.exec(content)) !== null) {
/* tslint:enable:no-null-keyword */
opts[match[1]] = match[2];
}
@@ -1450,9 +1477,9 @@ namespace Harness {
const lines = Utils.splitContentByNewlines(code);
// Stuff related to the subfile we're parsing
let currentFileContent: string = null;
let currentFileContent: string = undefined;
let currentFileOptions: any = {};
let currentFileName: any = null;
let currentFileName: any = undefined;
let refs: string[] = [];
for (let i = 0; i < lines.length; i++) {
@@ -1480,7 +1507,7 @@ namespace Harness {
testUnitData.push(newTestFile);
// Reset local data
currentFileContent = null;
currentFileContent = undefined;
currentFileOptions = {};
currentFileName = testMetaData[2];
refs = [];
@@ -1493,7 +1520,7 @@ namespace Harness {
else {
// Subfile content line
// Append to the current subfile content, inserting a newline needed
if (currentFileContent === null) {
if (currentFileContent === undefined) {
currentFileContent = "";
}
else {
@@ -1618,7 +1645,9 @@ namespace Harness {
// Store the content in the 'local' folder so we
// can accept it later (manually)
/* tslint:disable:no-null-keyword */
if (actual !== null) {
/* tslint:enable:no-null-keyword */
IO.writeFile(actualFileName, actual);
}
@@ -1635,7 +1664,9 @@ namespace Harness {
const refFileName = referencePath(relativeFileName, opts && opts.Baselinefolder, opts && opts.Subfolder);
/* tslint:disable:no-null-keyword */
if (actual === null) {
/* tslint:enable:no-null-keyword */
actual = "<no content>";
}
@@ -1697,6 +1728,3 @@ namespace Harness {
if (Error) (<any>Error).stackTraceLimit = 1;
}
// TODO: not sure why Utils.evalFile isn't working with this, eventually will concat it like old compiler instead of eval
eval(Harness.tcServicesFile);
+14 -9
View File
@@ -172,7 +172,7 @@ namespace Harness.LanguageService {
*/
public positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter {
const script: ScriptInfo = this.fileNameToScript[fileName];
assert.isNotNull(script);
assert.isOk(script);
return ts.computeLineAndCharacterOfPosition(script.getLineMap(), position);
}
@@ -246,16 +246,21 @@ namespace Harness.LanguageService {
};
this.getTypeReferenceDirectiveResolutionsForFile = (fileName) => {
const scriptInfo = this.getScriptInfo(fileName);
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false);
const resolutions: ts.Map<ts.ResolvedTypeReferenceDirective> = {};
const settings = this.nativeHost.getCompilationSettings();
for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) {
const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost);
if (resolutionInfo.resolvedTypeReferenceDirective.resolvedFileName) {
resolutions[typeReferenceDirective.fileName] = resolutionInfo.resolvedTypeReferenceDirective;
if (scriptInfo) {
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false);
const resolutions: ts.Map<ts.ResolvedTypeReferenceDirective> = {};
const settings = this.nativeHost.getCompilationSettings();
for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) {
const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost);
if (resolutionInfo.resolvedTypeReferenceDirective.resolvedFileName) {
resolutions[typeReferenceDirective.fileName] = resolutionInfo.resolvedTypeReferenceDirective;
}
}
return JSON.stringify(resolutions);
}
else {
return "[]";
}
return JSON.stringify(resolutions);
};
}
}
+4 -10
View File
@@ -1,7 +1,6 @@
/// <reference path="..\..\src\compiler\sys.ts" />
/// <reference path="..\..\src\harness\harness.ts" />
/// <reference path="..\..\src\harness\runnerbase.ts" />
/* tslint:disable:no-null-keyword */
interface FileInformation {
contents: string;
@@ -95,7 +94,7 @@ namespace Playback {
return lookup[s] = func(s);
});
run.reset = () => {
lookup = null;
lookup = undefined;
};
return run;
@@ -171,7 +170,8 @@ namespace Playback {
path => callAndRecord(underlying.fileExists(path), recordLog.fileExists, { path }),
memoize(path => {
// If we read from the file, it must exist
if (findResultByPath(wrapper, replayLog.filesRead, path, null) !== null) {
const noResult = {};
if (findResultByPath(wrapper, replayLog.filesRead, path, noResult) !== noResult) {
return true;
}
else {
@@ -227,13 +227,7 @@ namespace Playback {
(path, extension, exclude) => findResultByPath(wrapper,
replayLog.directoriesRead.filter(
d => {
if (d.extension === extension) {
if (d.exclude) {
return ts.arrayIsEqualTo(d.exclude, exclude);
}
return true;
}
return false;
return d.extension === extension;
}
), path));
+13 -5
View File
@@ -1,6 +1,5 @@
///<reference path="harness.ts" />
///<reference path="runnerbase.ts" />
/* tslint:disable:no-null-keyword */
// Test case is json of below type in tests/cases/project/
interface ProjectRunnerTestCase {
@@ -37,11 +36,19 @@ interface BatchCompileProjectTestCaseResult extends CompileProjectFilesResult {
}
class ProjectRunner extends RunnerBase {
public enumerateTestFiles() {
return this.enumerateFiles("tests/cases/project", /\.json$/, { recursive: true });
}
public kind(): TestRunnerKind {
return "project";
}
public initializeTests() {
if (this.tests.length === 0) {
const testFiles = this.enumerateFiles("tests/cases/project", /\.json$/, { recursive: true });
const testFiles = this.enumerateTestFiles();
testFiles.forEach(fn => {
fn = fn.replace(/\\/g, "/");
this.runProjectTestCase(fn);
});
}
@@ -53,7 +60,7 @@ class ProjectRunner extends RunnerBase {
private runProjectTestCase(testCaseFileName: string) {
let testCase: ProjectRunnerTestCase & ts.CompilerOptions;
let testFileText: string = null;
let testFileText: string;
try {
testFileText = Harness.IO.readFile(testCaseFileName);
}
@@ -178,7 +185,8 @@ class ProjectRunner extends RunnerBase {
useCaseSensitiveFileNames: () => Harness.IO.useCaseSensitiveFileNames(),
getNewLine: () => Harness.IO.newLine(),
fileExists: fileName => fileName === Harness.Compiler.defaultLibFileName || getSourceFileText(fileName) !== undefined,
readFile: fileName => Harness.IO.readFile(fileName)
readFile: fileName => Harness.IO.readFile(fileName),
getDirectories: path => Harness.IO.getDirectories(path)
};
}
}
+118 -11
View File
@@ -20,8 +20,6 @@
/// <reference path="rwcRunner.ts" />
/// <reference path="harness.ts" />
/* tslint:disable:no-null-keyword */
let runners: RunnerBase[] = [];
let iterations = 1;
@@ -33,18 +31,90 @@ function runTests(runners: RunnerBase[]) {
}
}
// users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options
let mytestconfig = "mytest.config";
let testconfig = "test.config";
let testConfigFile =
Harness.IO.fileExists(mytestconfig) ? Harness.IO.readFile(mytestconfig) :
(Harness.IO.fileExists(testconfig) ? Harness.IO.readFile(testconfig) : "");
function tryGetConfig(args: string[]) {
const prefix = "--config=";
const configPath = ts.forEach(args, arg => arg.lastIndexOf(prefix, 0) === 0 && arg.substr(prefix.length));
// strip leading and trailing quotes from the path (necessary on Windows since shell does not do it automatically)
return configPath && configPath.replace(/(^[\"'])|([\"']$)/g, "");
}
if (testConfigFile !== "") {
const testConfig = JSON.parse(testConfigFile);
function createRunner(kind: TestRunnerKind): RunnerBase {
switch (kind) {
case "conformance":
return new CompilerBaselineRunner(CompilerTestType.Conformance);
case "compiler":
return new CompilerBaselineRunner(CompilerTestType.Regressions);
case "fourslash":
return new FourSlashRunner(FourSlashTestType.Native);
case "fourslash-shims":
return new FourSlashRunner(FourSlashTestType.Shims);
case "fourslash-shims-pp":
return new FourSlashRunner(FourSlashTestType.ShimsWithPreprocess);
case "fourslash-server":
return new FourSlashRunner(FourSlashTestType.Server);
case "project":
return new ProjectRunner();
case "rwc":
return new RWCRunner();
case "test262":
return new Test262BaselineRunner();
}
}
// users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options
const mytestconfigFileName = "mytest.config";
const testconfigFileName = "test.config";
const customConfig = tryGetConfig(Harness.IO.args());
let testConfigContent =
customConfig && Harness.IO.fileExists(customConfig)
? Harness.IO.readFile(customConfig)
: Harness.IO.fileExists(mytestconfigFileName)
? Harness.IO.readFile(mytestconfigFileName)
: Harness.IO.fileExists(testconfigFileName) ? Harness.IO.readFile(testconfigFileName) : "";
let taskConfigsFolder: string;
let workerCount: number;
let runUnitTests = true;
interface TestConfig {
light?: boolean;
taskConfigsFolder?: string;
workerCount?: number;
tasks?: TaskSet[];
test?: string[];
runUnitTests?: boolean;
}
interface TaskSet {
runner: TestRunnerKind;
files: string[];
}
if (testConfigContent !== "") {
const testConfig = <TestConfig>JSON.parse(testConfigContent);
if (testConfig.light) {
Harness.lightMode = true;
}
if (testConfig.taskConfigsFolder) {
taskConfigsFolder = testConfig.taskConfigsFolder;
}
if (testConfig.runUnitTests !== undefined) {
runUnitTests = testConfig.runUnitTests;
}
if (testConfig.workerCount) {
workerCount = testConfig.workerCount;
}
if (testConfig.tasks) {
for (const taskSet of testConfig.tasks) {
const runner = createRunner(taskSet.runner);
for (const file of taskSet.files) {
runner.addTest(file);
}
runners.push(runner);
}
}
if (testConfig.test && testConfig.test.length > 0) {
for (const option of testConfig.test) {
@@ -108,4 +178,41 @@ if (runners.length === 0) {
// runners.push(new GeneratedFourslashRunner());
}
runTests(runners);
if (taskConfigsFolder) {
// this instance of mocha should only partition work but not run actual tests
runUnitTests = false;
const workerConfigs: TestConfig[] = [];
for (let i = 0; i < workerCount; i++) {
// pass light mode settings to workers
workerConfigs.push({ light: Harness.lightMode, tasks: [] });
}
for (const runner of runners) {
const files = runner.enumerateTestFiles();
const chunkSize = Math.floor(files.length / workerCount) + 1; // add extra 1 to prevent missing tests due to rounding
for (let i = 0; i < workerCount; i++) {
const startPos = i * chunkSize;
const len = Math.min(chunkSize, files.length - startPos);
if (len !== 0) {
workerConfigs[i].tasks.push({
runner: runner.kind(),
files: files.slice(startPos, startPos + len)
});
}
}
}
for (let i = 0; i < workerCount; i++) {
const config = workerConfigs[i];
// use last worker to run unit tests
config.runUnitTests = i === workerCount - 1;
Harness.IO.writeFile(ts.combinePaths(taskConfigsFolder, `task-config${i}.json`), JSON.stringify(workerConfigs[i]));
}
}
else {
runTests(runners);
}
if (!runUnitTests) {
// patch `describe` to skip unit tests
describe = <any>describe.skip;
}
+11 -2
View File
@@ -1,5 +1,10 @@
/// <reference path="harness.ts" />
type TestRunnerKind = CompilerTestKind | FourslashTestKind | "project" | "rwc" | "test262";
type CompilerTestKind = "conformance" | "compiler";
type FourslashTestKind = "fourslash" | "fourslash-shims" | "fourslash-shims-pp" | "fourslash-server";
abstract class RunnerBase {
constructor() { }
@@ -12,10 +17,14 @@ abstract class RunnerBase {
}
public enumerateFiles(folder: string, regex?: RegExp, options?: { recursive: boolean }): string[] {
return Harness.IO.listFiles(Harness.userSpecifiedRoot + folder, regex, { recursive: (options ? options.recursive : false) });
return ts.map(Harness.IO.listFiles(Harness.userSpecifiedRoot + folder, regex, { recursive: (options ? options.recursive : false) }), ts.normalizeSlashes);
}
/** Setup the runner's tests so that they are ready to be executed by the harness
abstract kind(): TestRunnerKind;
abstract enumerateTestFiles(): string[];
/** Setup the runner's tests so that they are ready to be executed by the harness
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
*/
public abstract initializeTests(): void;
+12 -3
View File
@@ -2,6 +2,7 @@
/// <reference path="runnerbase.ts" />
/// <reference path="loggedIO.ts" />
/// <reference path="..\compiler\commandLineParser.ts"/>
// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`.
/* tslint:disable:no-null-keyword */
namespace RWC {
@@ -128,7 +129,7 @@ namespace RWC {
opts.options.noLib = true;
// Emit the results
compilerOptions = null;
compilerOptions = undefined;
const output = Harness.Compiler.compileFiles(
inputFiles,
otherFiles,
@@ -144,7 +145,7 @@ namespace RWC {
function getHarnessCompilerInputUnit(fileName: string): Harness.Compiler.TestFile {
const unitName = ts.normalizeSlashes(Harness.IO.resolvePath(fileName));
let content: string = null;
let content: string;
try {
content = Harness.IO.readFile(unitName);
}
@@ -228,12 +229,20 @@ namespace RWC {
class RWCRunner extends RunnerBase {
private static sourcePath = "internal/cases/rwc/";
public enumerateTestFiles() {
return Harness.IO.listFiles(RWCRunner.sourcePath, /.+\.json$/);
}
public kind(): TestRunnerKind {
return "rwc";
}
/** Setup the runner's tests so that they are ready to be executed by the harness
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
*/
public initializeTests(): void {
// Read in and evaluate the test list
const testList = Harness.IO.listFiles(RWCRunner.sourcePath, /.+\.json$/);
const testList = this.enumerateTestFiles();
for (let i = 0; i < testList.length; i++) {
this.runTest(testList[i]);
}
+11 -2
View File
@@ -1,5 +1,6 @@
/// <reference path="harness.ts" />
/// <reference path="runnerbase.ts" />
// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`.
/* tslint:disable:no-null-keyword */
class Test262BaselineRunner extends RunnerBase {
@@ -97,12 +98,20 @@ class Test262BaselineRunner extends RunnerBase {
});
}
public kind(): TestRunnerKind {
return "test262";
}
public enumerateTestFiles() {
return ts.map(this.enumerateFiles(Test262BaselineRunner.basePath, Test262BaselineRunner.testFileExtensionRegex, { recursive: true }), ts.normalizePath);
}
public initializeTests() {
// this will set up a series of describe/it blocks to run between the setup and cleanup phases
if (this.tests.length === 0) {
const testFiles = this.enumerateFiles(Test262BaselineRunner.basePath, Test262BaselineRunner.testFileExtensionRegex, { recursive: true });
const testFiles = this.enumerateTestFiles();
testFiles.forEach(fn => {
this.runTest(ts.normalizePath(fn));
this.runTest(fn);
});
}
else {
+2 -1
View File
@@ -1,2 +1,3 @@
/// <reference path="lib.es2016.d.ts" />
/// <reference path="lib.es2017.object.d.ts" />
/// <reference path="lib.es2017.object.d.ts" />
/// <reference path="lib.es2017.sharedmemory.d.ts" />
+27
View File
@@ -0,0 +1,27 @@
/// <reference path="lib.es2015.symbol.d.ts" />
/// <reference path="lib.es2015.symbol.wellknown.d.ts" />
interface SharedArrayBuffer {
/**
* Read-only. The length of the ArrayBuffer (in bytes).
*/
readonly byteLength: number;
/*
* The SharedArrayBuffer constructor's length property whose value is 1.
*/
length: number;
/**
* Returns a section of an SharedArrayBuffer.
*/
slice(begin:number, end?:number): SharedArrayBuffer;
readonly [Symbol.species]: SharedArrayBuffer;
readonly [Symbol.toStringTag]: "SharedArrayBuffer";
}
interface SharedArrayBufferConstructor {
readonly prototype: SharedArrayBuffer;
new (byteLength: number): SharedArrayBuffer;
}
declare var SharedArrayBuffer: SharedArrayBufferConstructor;
+13 -1
View File
@@ -136,12 +136,24 @@ interface ObjectConstructor {
*/
getOwnPropertyNames(o: any): string[];
/**
* Creates an object that has null prototype.
* @param o Object to use as a prototype. May be null
*/
create(o: null): any;
/**
* Creates an object that has the specified prototype, and that optionally contains specified properties.
* @param o Object to use as a prototype. May be null
*/
create<T>(o: T): T;
/**
* Creates an object that has the specified prototype, and that optionally contains specified properties.
* @param o Object to use as a prototype. May be null
* @param properties JavaScript object that contains one or more property descriptors.
*/
create(o: any, properties?: PropertyDescriptorMap): any;
create(o: any, properties: PropertyDescriptorMap): any;
/**
* Adds a property to an object, or modifies attributes of an existing property.
+1 -1
View File
@@ -459,7 +459,7 @@ namespace ts.server {
kindModifiers: item.kindModifiers || "",
spans: item.spans.map(span => createTextSpanFromBounds(this.lineOffsetToPosition(fileName, span.start), this.lineOffsetToPosition(fileName, span.end))),
childItems: this.decodeNavigationBarItems(item.childItems, fileName),
indent: 0,
indent: item.indent,
bolded: false,
grayed: false
}));
+6 -2
View File
@@ -268,7 +268,7 @@ namespace ts.server {
}
removeRoot(info: ScriptInfo) {
if (!this.filenameToScript.contains(info.path)) {
if (this.filenameToScript.contains(info.path)) {
this.filenameToScript.remove(info.path);
this.roots = copyListRemovingItem(info, this.roots);
this.resolvedModuleNames.remove(info.path);
@@ -315,6 +315,10 @@ namespace ts.server {
return this.host.directoryExists(path);
}
getDirectories(path: string): string[] {
return this.host.getDirectories(path);
}
/**
* @param line 1 based index
*/
@@ -1142,7 +1146,7 @@ namespace ts.server {
else {
this.log("No config files found.");
}
return {};
return configFileName ? { configFileName } : {};
}
/**
+5
View File
@@ -1242,6 +1242,11 @@ declare namespace ts.server.protocol {
* Optional children.
*/
childItems?: NavigationBarItem[];
/**
* Number of levels deep this item should appear.
*/
indent: number;
}
export interface NavBarResponse extends Response {
+7 -2
View File
@@ -872,7 +872,8 @@ namespace ts.server {
start: compilerService.host.positionToLineOffset(fileName, span.start),
end: compilerService.host.positionToLineOffset(fileName, ts.textSpanEnd(span))
})),
childItems: this.decorateNavigationBarItem(project, fileName, item.childItems)
childItems: this.decorateNavigationBarItem(project, fileName, item.childItems),
indent: item.indent
}));
}
@@ -1206,7 +1207,11 @@ namespace ts.server {
// Handle cancellation exceptions
}
this.logError(err, message);
this.output(undefined, request ? request.command : CommandNames.Unknown, request ? request.seq : 0, "Error processing request. " + err.message);
this.output(
undefined,
request ? request.command : CommandNames.Unknown,
request ? request.seq : 0,
"Error processing request. " + (<StackTraceError>err).message + "\n" + (<StackTraceError>err).stack);
}
}
}
+10 -10
View File
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0.
// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0.
// See LICENSE.txt in the project root for complete license information.
/// <reference path='services.ts' />
@@ -19,8 +19,8 @@ namespace ts.BreakpointResolver {
if (sourceFile.getLineAndCharacterOfPosition(tokenAtLocation.getStart(sourceFile)).line > lineOfPosition) {
// Get previous token if the token is returned starts on new line
// eg: let x =10; |--- cursor is here
// let y = 10;
// token at position will return let keyword on second line as the token but we would like to use
// let y = 10;
// token at position will return let keyword on second line as the token but we would like to use
// token on same line if trailing trivia (comments or white spaces on same line) part of the last token on that line
tokenAtLocation = findPrecedingToken(tokenAtLocation.pos, sourceFile);
@@ -261,7 +261,7 @@ namespace ts.BreakpointResolver {
}
// Set breakpoint on identifier element of destructuring pattern
// a or ...c or d: x from
// a or ...c or d: x from
// [a, b, ...c] or { a, b } or { d: x } from destructuring pattern
if ((node.kind === SyntaxKind.Identifier ||
node.kind == SyntaxKind.SpreadElementExpression ||
@@ -275,7 +275,7 @@ namespace ts.BreakpointResolver {
const binaryExpression = <BinaryExpression>node;
// Set breakpoint in destructuring pattern if its destructuring assignment
// [a, b, c] or {a, b, c} of
// [a, b, c] = expression or
// [a, b, c] = expression or
// {a, b, c} = expression
if (isArrayLiteralOrObjectLiteralDestructuringPattern(binaryExpression.left)) {
return spanInArrayLiteralOrObjectLiteralDestructuringPattern(
@@ -285,8 +285,8 @@ namespace ts.BreakpointResolver {
if (binaryExpression.operatorToken.kind === SyntaxKind.EqualsToken &&
isArrayLiteralOrObjectLiteralDestructuringPattern(binaryExpression.parent)) {
// Set breakpoint on assignment expression element of destructuring pattern
// a = expression of
// [a = expression, b, c] = someExpression or
// a = expression of
// [a = expression, b, c] = someExpression or
// { a = expression, b, c } = someExpression
return textSpan(node);
}
@@ -403,7 +403,7 @@ namespace ts.BreakpointResolver {
const declarations = variableDeclaration.parent.declarations;
if (declarations && declarations[0] !== variableDeclaration) {
// If we cannot set breakpoint on this declaration, set it on previous one
// Because the variable declaration may be binding pattern and
// Because the variable declaration may be binding pattern and
// we would like to set breakpoint in last binding element if that's the case,
// use preceding token instead
return spanInNode(findPrecedingToken(variableDeclaration.pos, sourceFile, variableDeclaration.parent));
@@ -549,7 +549,7 @@ namespace ts.BreakpointResolver {
return spanInNode(firstBindingElement);
}
// Could be ArrayLiteral from destructuring assignment or
// Could be ArrayLiteral from destructuring assignment or
// just nested element in another destructuring assignment
// set breakpoint on assignment when parent is destructuring assignment
// Otherwise set breakpoint for this element
@@ -722,5 +722,5 @@ namespace ts.BreakpointResolver {
return spanInNode(node.parent);
}
}
}
}
}
+6
View File
@@ -81,6 +81,12 @@ namespace ts.formatting {
while (isWhiteSpace(sourceFile.text.charCodeAt(endOfFormatSpan)) && !isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) {
endOfFormatSpan--;
}
// if the character at the end of the span is a line break, we shouldn't include it, because it indicates we don't want to
// touch the current line at all. Also, on some OSes the line break consists of two characters (\r\n), we should test if the
// previous character before the end of format span is line break character as well.
if (isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) {
endOfFormatSpan--;
}
const span = {
// get start position for the previous line
pos: getStartPositionOfLine(line - 1, sourceFile),
+2 -2
View File
@@ -268,9 +268,9 @@ namespace ts.formatting {
return startPos < endPos && current !== SyntaxKind.EndOfFileToken && !isTrivia(current);
}
// when containing node in the tree is token
// when containing node in the tree is token
// but its kind differs from the kind that was returned by the scanner,
// then kind needs to be fixed. This might happen in cases
// then kind needs to be fixed. This might happen in cases
// when parser interprets token differently, i.e keyword treated as identifier
function fixTokenKind(tokenInfo: TokenInfo, container: Node): TokenInfo {
if (isToken(container) && tokenInfo.token.kind !== container.kind) {
+3 -3
View File
@@ -554,17 +554,17 @@ namespace ts.formatting {
static IsSameLineTokenOrBeforeMultilineBlockContext(context: FormattingContext): boolean {
//// This check is mainly used inside SpaceBeforeOpenBraceInControl and SpaceBeforeOpenBraceInFunction.
////
//// Ex:
//// Ex:
//// if (1) { ....
//// * ) and { are on the same line so apply the rule. Here we don't care whether it's same or multi block context
////
//// Ex:
//// Ex:
//// if (1)
//// { ... }
//// * ) and { are on different lines. We only need to format if the block is multiline context. So in this case we don't format.
////
//// Ex:
//// if (1)
//// if (1)
//// { ...
//// }
//// * ) and { are on different lines. We only need to format if the block is multiline context. So in this case we format.
+2 -2
View File
@@ -95,9 +95,9 @@ namespace ts.formatting {
//// 4- Context rules with any token combination
//// 5- Non-context rules with specific token combination
//// 6- Non-context rules with any token combination
////
////
//// The member rulesInsertionIndexBitmap is used to describe the number of rules
//// in each sub-bucket (above) hence can be used to know the index of where to insert
//// in each sub-bucket (above) hence can be used to know the index of where to insert
//// the next rule. It's a bitmap which contains 6 different sections each is given 5 bits.
////
//// Example:
+3 -3
View File
@@ -9,7 +9,7 @@ namespace ts.NavigateTo {
// This means "compare in a case insensitive manner."
const baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" };
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
forEach(program.getSourceFiles(), sourceFile => {
cancellationToken.throwIfCancellationRequested();
@@ -17,7 +17,7 @@ namespace ts.NavigateTo {
for (const name in nameToDeclarations) {
const declarations = getProperty(nameToDeclarations, name);
if (declarations) {
// First do a quick check to see if the name of the declaration matches the
// First do a quick check to see if the name of the declaration matches the
// last portion of the (possibly) dotted name they're searching for.
let matches = patternMatcher.getMatchesForLastSegmentOfPattern(name);
@@ -26,7 +26,7 @@ namespace ts.NavigateTo {
}
for (const declaration of declarations) {
// It was a match! If the pattern has dots in it, then also see if the
// It was a match! If the pattern has dots in it, then also see if the
// declaration container matches as well.
if (patternMatcher.patternContainsDots) {
const containers = getContainers(declaration);
+77 -37
View File
@@ -9,16 +9,10 @@ namespace ts.NavigationBar {
return getJsNavigationBarItems(sourceFile, compilerOptions);
}
// If the source file has any child items, then it included in the tree
// and takes lexical ownership of all other top-level items.
let hasGlobalNode = false;
return getItemsWorker(getTopLevelNodes(sourceFile), createTopLevelItem);
function getIndent(node: Node): number {
// If we have a global node in the tree,
// then it adds an extra layer of depth to all subnodes.
let indent = hasGlobalNode ? 1 : 0;
let indent = 1; // Global node is the only one with indent 0.
let current = node.parent;
while (current) {
@@ -104,6 +98,7 @@ namespace ts.NavigationBar {
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
case SyntaxKind.TypeAliasDeclaration:
childNodes.push(node);
break;
}
@@ -140,7 +135,7 @@ namespace ts.NavigationBar {
function sortNodes(nodes: Node[]): Node[] {
return nodes.slice(0).sort((n1: Declaration, n2: Declaration) => {
if (n1.name && n2.name) {
return getPropertyNameForPropertyNameNode(n1.name).localeCompare(getPropertyNameForPropertyNameNode(n2.name));
return localeCompareFix(getPropertyNameForPropertyNameNode(n1.name), getPropertyNameForPropertyNameNode(n2.name));
}
else if (n1.name) {
return 1;
@@ -152,6 +147,16 @@ namespace ts.NavigationBar {
return n1.kind - n2.kind;
}
});
// node 0.10 treats "a" as greater than "B".
// For consistency, sort alphabetically, falling back to which is lower-case.
function localeCompareFix(a: string, b: string) {
const cmp = a.toLowerCase().localeCompare(b.toLowerCase());
if (cmp !== 0)
return cmp;
// Return the *opposite* of the `<` operator, which works the same in node 0.10 and 6.0.
return a < b ? 1 : a > b ? -1 : 0;
}
}
function addTopLevelNodes(nodes: Node[], topLevelNodes: Node[]): void {
@@ -183,7 +188,10 @@ namespace ts.NavigationBar {
case SyntaxKind.ModuleDeclaration:
let moduleDeclaration = <ModuleDeclaration>node;
topLevelNodes.push(node);
addTopLevelNodes((<Block>getInnermostModule(moduleDeclaration).body).statements, topLevelNodes);
const inner = getInnermostModule(moduleDeclaration);
if (inner.body) {
addTopLevelNodes((<Block>inner.body).statements, topLevelNodes);
}
break;
case SyntaxKind.FunctionDeclaration:
@@ -320,9 +328,15 @@ namespace ts.NavigationBar {
case SyntaxKind.EnumMember:
return createItem(node, getTextOfNode((<EnumMember>node).name), ts.ScriptElementKind.memberVariableElement);
case SyntaxKind.ModuleDeclaration:
return createItem(node, getModuleName(<ModuleDeclaration>node), ts.ScriptElementKind.moduleElement);
case SyntaxKind.InterfaceDeclaration:
return createItem(node, getTextOfNode((<InterfaceDeclaration>node).name), ts.ScriptElementKind.interfaceElement);
case SyntaxKind.TypeAliasDeclaration:
return createItem(node, getTextOfNode((<TypeAliasDeclaration>node).name), ts.ScriptElementKind.typeElement);
case SyntaxKind.CallSignature:
return createItem(node, "()", ts.ScriptElementKind.callSignatureElement);
@@ -333,6 +347,9 @@ namespace ts.NavigationBar {
case SyntaxKind.PropertySignature:
return createItem(node, getTextOfNode((<PropertyDeclaration>node).name), ts.ScriptElementKind.memberVariableElement);
case SyntaxKind.ClassDeclaration:
return createItem(node, getTextOfNode((<ClassDeclaration>node).name), ts.ScriptElementKind.classElement);
case SyntaxKind.FunctionDeclaration:
return createItem(node, getTextOfNode((<FunctionLikeDeclaration>node).name), ts.ScriptElementKind.functionElement);
@@ -436,30 +453,11 @@ namespace ts.NavigationBar {
return undefined;
function getModuleName(moduleDeclaration: ModuleDeclaration): string {
// We want to maintain quotation marks.
if (isAmbientModule(moduleDeclaration)) {
return getTextOfNode(moduleDeclaration.name);
}
// Otherwise, we need to aggregate each identifier to build up the qualified name.
const result: string[] = [];
result.push(moduleDeclaration.name.text);
while (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
moduleDeclaration = <ModuleDeclaration>moduleDeclaration.body;
result.push(moduleDeclaration.name.text);
}
return result.join(".");
}
function createModuleItem(node: ModuleDeclaration): NavigationBarItem {
const moduleName = getModuleName(node);
const childItems = getItemsWorker(getChildNodes((<Block>getInnermostModule(node).body).statements), createChildItem);
const body = <Block>getInnermostModule(node).body;
const childItems = body ? getItemsWorker(getChildNodes(body.statements), createChildItem) : [];
return getNavigationBarItem(moduleName,
ts.ScriptElementKind.moduleElement,
@@ -521,11 +519,6 @@ namespace ts.NavigationBar {
function createSourceFileItem(node: SourceFile): ts.NavigationBarItem {
const childItems = getItemsWorker(getChildNodes(node.statements), createChildItem);
if (childItems === undefined || childItems.length === 0) {
return undefined;
}
hasGlobalNode = true;
const rootName = isExternalModule(node)
? "\"" + escapeString(getBaseFileName(removeFileExtension(normalizePath(node.fileName)))) + "\""
: "<global>";
@@ -590,6 +583,26 @@ namespace ts.NavigationBar {
}
}
function getModuleName(moduleDeclaration: ModuleDeclaration): string {
// We want to maintain quotation marks.
if (isAmbientModule(moduleDeclaration)) {
return getTextOfNode(moduleDeclaration.name);
}
// Otherwise, we need to aggregate each identifier to build up the qualified name.
const result: string[] = [];
result.push(moduleDeclaration.name.text);
while (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
moduleDeclaration = <ModuleDeclaration>moduleDeclaration.body;
result.push(moduleDeclaration.name.text);
}
return result.join(".");
}
function removeComputedProperties(node: EnumDeclaration): Declaration[] {
return filter<Declaration>(node.members, member => member.name === undefined || member.name.kind !== SyntaxKind.ComputedPropertyName);
}
@@ -602,7 +615,7 @@ namespace ts.NavigationBar {
}
function getInnermostModule(node: ModuleDeclaration): ModuleDeclaration {
while (node.body.kind === SyntaxKind.ModuleDeclaration) {
while (node.body && node.body.kind === SyntaxKind.ModuleDeclaration) {
node = <ModuleDeclaration>node.body;
}
@@ -643,6 +656,12 @@ namespace ts.NavigationBar {
topItem.childItems.push(newItem);
}
if (node.jsDocComments && node.jsDocComments.length > 0) {
for (const jsDocComment of node.jsDocComments) {
visitNode(jsDocComment);
}
}
// Add a level if traversing into a container
if (newItem && (isFunctionLike(node) || isClassLike(node))) {
const lastTop = topItem;
@@ -722,6 +741,27 @@ namespace ts.NavigationBar {
}
const declName = declarationNameToString(decl.name);
return getNavBarItem(declName, ScriptElementKind.constElement, [getNodeSpan(node)]);
case SyntaxKind.JSDocTypedefTag:
if ((<JSDocTypedefTag>node).name) {
return getNavBarItem(
(<JSDocTypedefTag>node).name.text,
ScriptElementKind.typeElement,
[getNodeSpan(node)]);
}
else {
const parentNode = node.parent && node.parent.parent;
if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) {
if ((<VariableStatement>parentNode).declarationList.declarations.length > 0) {
const nameIdentifier = (<VariableStatement>parentNode).declarationList.declarations[0].name;
if (nameIdentifier.kind === SyntaxKind.Identifier) {
return getNavBarItem(
(<Identifier>nameIdentifier).text,
ScriptElementKind.typeElement,
[getNodeSpan(node)]);
}
}
}
}
default:
return undefined;
}
@@ -792,7 +832,7 @@ namespace ts.NavigationBar {
}
function getNodeSpan(node: Node) {
return node.kind === SyntaxKind.SourceFile
return node.kind === SyntaxKind.SourceFile
? createTextSpanFromBounds(node.getFullStart(), node.getEnd())
: createTextSpanFromBounds(node.getStart(), node.getEnd());
}
+217 -50
View File
@@ -20,7 +20,7 @@ namespace ts {
getChildCount(sourceFile?: SourceFile): number;
getChildAt(index: number, sourceFile?: SourceFile): Node;
getChildren(sourceFile?: SourceFile): Node[];
getStart(sourceFile?: SourceFile): number;
getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number;
getFullStart(): number;
getEnd(): number;
getWidth(sourceFile?: SourceFile): number;
@@ -172,6 +172,9 @@ namespace ts {
"static",
"throws",
"type",
"typedef",
"property",
"prop",
"version"
];
let jsDocCompletionEntries: CompletionEntry[];
@@ -189,6 +192,7 @@ namespace ts {
public end: number;
public flags: NodeFlags;
public parent: Node;
public jsDocComments: JSDocComment[];
private _children: Node[];
constructor(kind: SyntaxKind, pos: number, end: number) {
@@ -203,8 +207,8 @@ namespace ts {
return getSourceFileOfNode(this);
}
public getStart(sourceFile?: SourceFile): number {
return getTokenPosOfNode(this, sourceFile);
public getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number {
return getTokenPosOfNode(this, sourceFile, includeJsDocComment);
}
public getFullStart(): number {
@@ -235,12 +239,14 @@ namespace ts {
return (sourceFile || this.getSourceFile()).text.substring(this.getStart(), this.getEnd());
}
private addSyntheticNodes(nodes: Node[], pos: number, end: number): number {
private addSyntheticNodes(nodes: Node[], pos: number, end: number, useJSDocScanner?: boolean): number {
scanner.setTextPos(pos);
while (pos < end) {
const token = scanner.scan();
const token = useJSDocScanner ? scanner.scanJSDocToken() : scanner.scan();
const textPos = scanner.getTextPos();
nodes.push(createNode(token, pos, textPos, 0, this));
if (textPos <= end) {
nodes.push(createNode(token, pos, textPos, 0, this));
}
pos = textPos;
}
return pos;
@@ -270,20 +276,27 @@ namespace ts {
scanner.setText((sourceFile || this.getSourceFile()).text);
children = [];
let pos = this.pos;
const useJSDocScanner = this.kind >= SyntaxKind.FirstJSDocTagNode && this.kind <= SyntaxKind.LastJSDocTagNode;
const processNode = (node: Node) => {
if (pos < node.pos) {
pos = this.addSyntheticNodes(children, pos, node.pos);
pos = this.addSyntheticNodes(children, pos, node.pos, useJSDocScanner);
}
children.push(node);
pos = node.end;
};
const processNodes = (nodes: NodeArray<Node>) => {
if (pos < nodes.pos) {
pos = this.addSyntheticNodes(children, pos, nodes.pos);
pos = this.addSyntheticNodes(children, pos, nodes.pos, useJSDocScanner);
}
children.push(this.createSyntaxList(<NodeArray<Node>>nodes));
pos = nodes.end;
};
// jsDocComments need to be the first children
if (this.jsDocComments) {
for (const jsDocComment of this.jsDocComments) {
processNode(jsDocComment);
}
}
forEachChild(this, processNode, processNodes);
if (pos < this.end) {
this.addSyntheticNodes(children, pos, this.end);
@@ -393,37 +406,56 @@ namespace ts {
const sourceFileOfDeclaration = getSourceFileOfNode(declaration);
// If it is parameter - try and get the jsDoc comment with @param tag from function declaration's jsDoc comments
if (canUseParsedParamTagComments && declaration.kind === SyntaxKind.Parameter) {
ts.forEach(getJsDocCommentTextRange(declaration.parent, sourceFileOfDeclaration), jsDocCommentTextRange => {
const cleanedParamJsDocComment = getCleanedParamJsDocComment(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration);
if (cleanedParamJsDocComment) {
addRange(jsDocCommentParts, cleanedParamJsDocComment);
}
});
if ((declaration.parent.kind === SyntaxKind.FunctionExpression || declaration.parent.kind === SyntaxKind.ArrowFunction) &&
declaration.parent.parent.kind === SyntaxKind.VariableDeclaration) {
addCommentParts(declaration.parent.parent.parent, sourceFileOfDeclaration, getCleanedParamJsDocComment);
}
addCommentParts(declaration.parent, sourceFileOfDeclaration, getCleanedParamJsDocComment);
}
// If this is left side of dotted module declaration, there is no doc comments associated with this node
if (declaration.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>declaration).body.kind === SyntaxKind.ModuleDeclaration) {
if (declaration.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>declaration).body && (<ModuleDeclaration>declaration).body.kind === SyntaxKind.ModuleDeclaration) {
return;
}
if ((declaration.kind === SyntaxKind.FunctionExpression || declaration.kind === SyntaxKind.ArrowFunction) &&
declaration.parent.kind === SyntaxKind.VariableDeclaration) {
addCommentParts(declaration.parent.parent, sourceFileOfDeclaration, getCleanedJsDocComment);
}
// If this is dotted module name, get the doc comments from the parent
while (declaration.kind === SyntaxKind.ModuleDeclaration && declaration.parent.kind === SyntaxKind.ModuleDeclaration) {
declaration = <ModuleDeclaration>declaration.parent;
}
addCommentParts(declaration.kind === SyntaxKind.VariableDeclaration ? declaration.parent.parent : declaration,
sourceFileOfDeclaration,
getCleanedJsDocComment);
// Get the cleaned js doc comment text from the declaration
ts.forEach(getJsDocCommentTextRange(
declaration.kind === SyntaxKind.VariableDeclaration ? declaration.parent.parent : declaration, sourceFileOfDeclaration), jsDocCommentTextRange => {
const cleanedJsDocComment = getCleanedJsDocComment(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration);
if (cleanedJsDocComment) {
addRange(jsDocCommentParts, cleanedJsDocComment);
}
});
if (declaration.kind === SyntaxKind.VariableDeclaration) {
const init = (declaration as VariableDeclaration).initializer;
if (init && (init.kind === SyntaxKind.FunctionExpression || init.kind === SyntaxKind.ArrowFunction)) {
// Get the cleaned js doc comment text from the initializer
addCommentParts(init, sourceFileOfDeclaration, getCleanedJsDocComment);
}
}
}
});
return jsDocCommentParts;
function addCommentParts(commented: Node,
sourceFileOfDeclaration: SourceFile,
getCommentPart: (pos: number, end: number, file: SourceFile) => SymbolDisplayPart[]): void {
const ranges = getJsDocCommentTextRange(commented, sourceFileOfDeclaration);
// Get the cleaned js doc comment text from the declaration
ts.forEach(ranges, jsDocCommentTextRange => {
const cleanedComment = getCommentPart(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration);
if (cleanedComment) {
addRange(jsDocCommentParts, cleanedComment);
}
});
}
function getJsDocCommentTextRange(node: Node, sourceFile: SourceFile): TextRange[] {
return ts.map(getJsDocComments(node, sourceFile),
jsDocComment => {
@@ -1032,10 +1064,10 @@ namespace ts {
getCancellationToken?(): HostCancellationToken;
getCurrentDirectory(): string;
getDefaultLibFileName(options: CompilerOptions): string;
log? (s: string): void;
trace? (s: string): void;
error? (s: string): void;
useCaseSensitiveFileNames? (): boolean;
log?(s: string): void;
trace?(s: string): void;
error?(s: string): void;
useCaseSensitiveFileNames?(): boolean;
/*
* LS host can optionally implement this method if it wants to be completely in charge of module name resolution.
@@ -1045,6 +1077,7 @@ namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
directoryExists?(directoryName: string): boolean;
getDirectories?(directoryName: string): string[];
}
//
@@ -1907,6 +1940,40 @@ namespace ts {
sourceMapText?: string;
}
let commandLineOptionsStringToEnum: CommandLineOptionOfCustomType[];
/** JS users may pass in string values for enum compiler options (such as ModuleKind), so convert. */
function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions {
// Lazily create this value to fix module loading errors.
commandLineOptionsStringToEnum = commandLineOptionsStringToEnum || <CommandLineOptionOfCustomType[]>filter(optionDeclarations, o =>
typeof o.type === "object" && !forEachValue(<Map<any>> o.type, v => typeof v !== "number"));
options = clone(options);
for (const opt of commandLineOptionsStringToEnum) {
if (!hasProperty(options, opt.name)) {
continue;
}
const value = options[opt.name];
// Value should be a key of opt.type
if (typeof value === "string") {
// If value is not a string, this will fail
options[opt.name] = parseCustomTypeOption(opt, value, diagnostics);
}
else {
if (!forEachValue(opt.type, v => v === value)) {
// Supplied value isn't a valid enum value.
diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt));
}
}
}
return options;
}
/*
* This function will compile source text from 'input' argument using specified compiler options.
* If not options are provided - it will use a set of default compiler options.
@@ -1917,7 +1984,9 @@ namespace ts {
* - noResolve = true
*/
export function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput {
const options = transpileOptions.compilerOptions ? clone(transpileOptions.compilerOptions) : getDefaultCompilerOptions();
const diagnostics: Diagnostic[] = [];
const options: CompilerOptions = transpileOptions.compilerOptions ? fixupCompilerOptions(transpileOptions.compilerOptions, diagnostics) : getDefaultCompilerOptions();
options.isolatedModules = true;
@@ -1970,14 +2039,13 @@ namespace ts {
getNewLine: () => newLine,
fileExists: (fileName): boolean => fileName === inputFileName,
readFile: (fileName): string => "",
directoryExists: directoryExists => true
directoryExists: directoryExists => true,
getDirectories: (path: string) => []
};
const program = createProgram([inputFileName], options, compilerHost);
let diagnostics: Diagnostic[];
if (transpileOptions.reportDiagnostics) {
diagnostics = [];
addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile));
addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics());
}
@@ -2073,7 +2141,7 @@ namespace ts {
const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey {
return <DocumentRegistryBucketKey>`_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${settings.typesRoot}|${settings.typesSearchPaths}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`;
return <DocumentRegistryBucketKey>`_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${JSON.stringify(settings.typeRoots)}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`;
}
function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
@@ -2467,7 +2535,7 @@ namespace ts {
}
// should be start of dependency list
if (token !== SyntaxKind.OpenBracketToken) {
if (token !== SyntaxKind.OpenBracketToken) {
return true;
}
@@ -2644,7 +2712,7 @@ namespace ts {
return false;
}
/**
/**
* Returns the containing object literal property declaration given a possible name node, e.g. "a" in x = { "a": 1 }
*/
function getContainingObjectLiteralElement(node: Node): ObjectLiteralElement {
@@ -2906,6 +2974,7 @@ namespace ts {
const changesInCompilationSettingsAffectSyntax = oldSettings &&
(oldSettings.target !== newSettings.target ||
oldSettings.module !== newSettings.module ||
oldSettings.moduleResolution !== newSettings.moduleResolution ||
oldSettings.noResolve !== newSettings.noResolve ||
oldSettings.jsx !== newSettings.jsx ||
oldSettings.allowJs !== newSettings.allowJs);
@@ -2932,8 +3001,10 @@ namespace ts {
return entry && entry.scriptSnapshot.getText(0, entry.scriptSnapshot.getLength());
},
directoryExists: directoryName => {
Debug.assert(!host.resolveModuleNames || !host.resolveTypeReferenceDirectives);
return directoryProbablyExists(directoryName, host);
},
getDirectories: path => {
return host.getDirectories ? host.getDirectories(path) : [];
}
};
if (host.trace) {
@@ -3999,10 +4070,15 @@ namespace ts {
}
}
function getCompletionsAtPosition(fileName: string, position: number): CompletionInfo {
synchronizeHostData();
const sourceFile = getValidSourceFile(fileName);
if (isInString(sourceFile, position)) {
return getStringLiteralCompletionEntries(sourceFile, position);
}
const completionData = getCompletionData(fileName, position);
if (!completionData) {
return undefined;
@@ -4015,12 +4091,10 @@ namespace ts {
return { isMemberCompletion: false, isNewIdentifierLocation: false, entries: getAllJsDocCompletionEntries() };
}
const sourceFile = getValidSourceFile(fileName);
const entries: CompletionEntry[] = [];
if (isSourceFileJavaScript(sourceFile)) {
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries);
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ false);
addRange(entries, getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames));
}
else {
@@ -4044,7 +4118,7 @@ namespace ts {
}
}
getCompletionEntriesFromSymbols(symbols, entries);
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true);
}
// Add keywords if this is not a member completion list
@@ -4094,11 +4168,11 @@ namespace ts {
}));
}
function createCompletionEntry(symbol: Symbol, location: Node): CompletionEntry {
function createCompletionEntry(symbol: Symbol, location: Node, performCharacterChecks: boolean): CompletionEntry {
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
// We would like to only show things that can be added after a dot, so for instance numeric properties can
// not be accessed with a dot (a.1 <- invalid)
const displayName = getCompletionEntryDisplayNameForSymbol(symbol, program.getCompilerOptions().target, /*performCharacterChecks*/ true, location);
const displayName = getCompletionEntryDisplayNameForSymbol(symbol, program.getCompilerOptions().target, performCharacterChecks, location);
if (!displayName) {
return undefined;
}
@@ -4119,12 +4193,12 @@ namespace ts {
};
}
function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[]): Map<string> {
function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[], location: Node, performCharacterChecks: boolean): Map<string> {
const start = new Date().getTime();
const uniqueNames: Map<string> = {};
if (symbols) {
for (const symbol of symbols) {
const entry = createCompletionEntry(symbol, location);
const entry = createCompletionEntry(symbol, location, performCharacterChecks);
if (entry) {
const id = escapeIdentifier(entry.name);
if (!lookUp(uniqueNames, id)) {
@@ -4138,6 +4212,93 @@ namespace ts {
log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (new Date().getTime() - start));
return uniqueNames;
}
function getStringLiteralCompletionEntries(sourceFile: SourceFile, position: number) {
const node = findPrecedingToken(position, sourceFile);
if (!node || node.kind !== SyntaxKind.StringLiteral) {
return undefined;
}
const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile);
if (argumentInfo) {
// Get string literal completions from specialized signatures of the target
return getStringLiteralCompletionEntriesFromCallExpression(argumentInfo);
}
else if (isElementAccessExpression(node.parent) && node.parent.argumentExpression === node) {
// Get all names of properties on the expression
return getStringLiteralCompletionEntriesFromElementAccess(node.parent);
}
else {
// Otherwise, get the completions from the contextual type if one exists
return getStringLiteralCompletionEntriesFromContextualType(<StringLiteral>node);
}
}
function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo) {
const typeChecker = program.getTypeChecker();
const candidates: Signature[] = [];
const entries: CompletionEntry[] = [];
typeChecker.getResolvedSignature(argumentInfo.invocation, candidates);
for (const candidate of candidates) {
if (candidate.parameters.length > argumentInfo.argumentIndex) {
const parameter = candidate.parameters[argumentInfo.argumentIndex];
addStringLiteralCompletionsFromType(typeChecker.getTypeAtLocation(parameter.valueDeclaration), entries);
}
}
if (entries.length) {
return { isMemberCompletion: false, isNewIdentifierLocation: true, entries };
}
return undefined;
}
function getStringLiteralCompletionEntriesFromElementAccess(node: ElementAccessExpression) {
const typeChecker = program.getTypeChecker();
const type = typeChecker.getTypeAtLocation(node.expression);
const entries: CompletionEntry[] = [];
if (type) {
getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, node, /*performCharacterChecks*/false);
if (entries.length) {
return { isMemberCompletion: true, isNewIdentifierLocation: true, entries };
}
}
return undefined;
}
function getStringLiteralCompletionEntriesFromContextualType(node: StringLiteral) {
const typeChecker = program.getTypeChecker();
const type = typeChecker.getContextualType(node);
if (type) {
const entries: CompletionEntry[] = [];
addStringLiteralCompletionsFromType(type, entries);
if (entries.length) {
return { isMemberCompletion: false, isNewIdentifierLocation: false, entries };
}
}
return undefined;
}
function addStringLiteralCompletionsFromType(type: Type, result: CompletionEntry[]): void {
if (!type) {
return;
}
if (type.flags & TypeFlags.Union) {
forEach((<UnionType>type).types, t => addStringLiteralCompletionsFromType(t, result));
}
else {
if (type.flags & TypeFlags.StringLiteral) {
result.push({
name: (<StringLiteralType>type).text,
kindModifiers: ScriptElementKindModifier.none,
kind: ScriptElementKind.variableElement,
sortText: "0"
});
}
}
}
}
function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
@@ -4302,7 +4463,7 @@ namespace ts {
// try get the call/construct signature from the type if it matches
let callExpression: CallExpression;
if (location.kind === SyntaxKind.CallExpression || location.kind === SyntaxKind.NewExpression) {
callExpression = <CallExpression> location;
callExpression = <CallExpression>location;
}
else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) {
callExpression = <CallExpression>location.parent;
@@ -5637,7 +5798,7 @@ namespace ts {
const sourceFile = getValidSourceFile(fileName);
const node = getTouchingPropertyName(sourceFile, position);
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
if (node === sourceFile) {
return undefined;
}
@@ -5999,7 +6160,8 @@ namespace ts {
const sourceFile = container.getSourceFile();
const tripleSlashDirectivePrefixRegex = /^\/\/\/\s*</;
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, container.getStart(), container.getEnd());
const start = findInComments ? container.getFullStart() : container.getStart();
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, start, container.getEnd());
if (possiblePositions.length) {
// Build the set of symbols to search for, initially it has only the current symbol
@@ -7590,11 +7752,11 @@ namespace ts {
function isValidBraceCompletionAtPostion(fileName: string, position: number, openingBrace: number): boolean {
// '<' is currently not supported, figuring out if we're in a Generic Type vs. a comparison is too
// '<' is currently not supported, figuring out if we're in a Generic Type vs. a comparison is too
// expensive to do during typing scenarios
// i.e. whether we're dealing with:
// var x = new foo<| ( with class foo<T>{} )
// or
// or
// var y = 3 <|
if (openingBrace === CharacterCodes.lessThan) {
return false;
@@ -7829,7 +7991,7 @@ namespace ts {
const defaultLibFileName = host.getDefaultLibFileName(host.getCompilationSettings());
const canonicalDefaultLibName = getCanonicalFileName(ts.normalizePath(defaultLibFileName));
const node = getTouchingWord(sourceFile, position);
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ true);
// Can only rename an identifier.
if (node) {
@@ -7999,6 +8161,11 @@ namespace ts {
break;
default:
forEachChild(node, walk);
if (node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
forEachChild(jsDocComment, walk);
}
}
}
}
}
+383 -380
View File
@@ -3,9 +3,9 @@
namespace ts.SignatureHelp {
// A partially written generic type expression is not guaranteed to have the correct syntax tree. the expression could be parsed as less than/greater than expression or a comma expression
// or some other combination depending on what the user has typed so far. For the purposes of signature help we need to consider any location after "<" as a possible generic type reference.
// To do this, the method will back parse the expression starting at the position required. it will try to parse the current expression as a generic type expression, if it did succeed it
// will return the generic identifier that started the expression (e.g. "foo" in "foo<any, |"). It is then up to the caller to ensure that this is a valid generic expression through
// or some other combination depending on what the user has typed so far. For the purposes of signature help we need to consider any location after "<" as a possible generic type reference.
// To do this, the method will back parse the expression starting at the position required. it will try to parse the current expression as a generic type expression, if it did succeed it
// will return the generic identifier that started the expression (e.g. "foo" in "foo<any, |"). It is then up to the caller to ensure that this is a valid generic expression through
// looking up the type. The method will also keep track of the parameter index inside the expression.
// public static isInPartiallyWrittenTypeArgumentList(syntaxTree: TypeScript.SyntaxTree, position: number): any {
// let token = Syntax.findTokenOnLeft(syntaxTree.sourceUnit(), position, /*includeSkippedTokens*/ true);
@@ -162,15 +162,16 @@ namespace ts.SignatureHelp {
// // Did not find matching token
// return null;
// }
const emptyArray: any[] = [];
const enum ArgumentListKind {
export const enum ArgumentListKind {
TypeArguments,
CallArguments,
TaggedTemplateArguments
}
interface ArgumentListInfo {
export interface ArgumentListInfo {
kind: ArgumentListKind;
invocation: CallLikeExpression;
argumentsSpan: TextSpan;
@@ -188,7 +189,8 @@ namespace ts.SignatureHelp {
return undefined;
}
const argumentInfo = getContainingArgumentInfo(startingToken);
const argumentInfo = getContainingArgumentInfo(startingToken, position, sourceFile);
cancellationToken.throwIfCancellationRequested();
// Semantic filtering of signature help
@@ -202,434 +204,435 @@ namespace ts.SignatureHelp {
cancellationToken.throwIfCancellationRequested();
if (!candidates.length) {
// We didn't have any sig help items produced by the TS compiler. If this is a JS
// We didn't have any sig help items produced by the TS compiler. If this is a JS
// file, then see if we can figure out anything better.
if (isSourceFileJavaScript(sourceFile)) {
return createJavaScriptSignatureHelpItems(argumentInfo);
return createJavaScriptSignatureHelpItems(argumentInfo, program);
}
return undefined;
}
return createSignatureHelpItems(candidates, resolvedSignature, argumentInfo);
return createSignatureHelpItems(candidates, resolvedSignature, argumentInfo, typeChecker);
}
function createJavaScriptSignatureHelpItems(argumentInfo: ArgumentListInfo): SignatureHelpItems {
if (argumentInfo.invocation.kind !== SyntaxKind.CallExpression) {
return undefined;
}
function createJavaScriptSignatureHelpItems(argumentInfo: ArgumentListInfo, program: Program): SignatureHelpItems {
if (argumentInfo.invocation.kind !== SyntaxKind.CallExpression) {
return undefined;
}
// See if we can find some symbol with the call expression name that has call signatures.
const callExpression = <CallExpression>argumentInfo.invocation;
const expression = callExpression.expression;
const name = expression.kind === SyntaxKind.Identifier
? <Identifier> expression
: expression.kind === SyntaxKind.PropertyAccessExpression
? (<PropertyAccessExpression>expression).name
: undefined;
// See if we can find some symbol with the call expression name that has call signatures.
const callExpression = <CallExpression>argumentInfo.invocation;
const expression = callExpression.expression;
const name = expression.kind === SyntaxKind.Identifier
? <Identifier>expression
: expression.kind === SyntaxKind.PropertyAccessExpression
? (<PropertyAccessExpression>expression).name
: undefined;
if (!name || !name.text) {
return undefined;
}
if (!name || !name.text) {
return undefined;
}
const typeChecker = program.getTypeChecker();
for (const sourceFile of program.getSourceFiles()) {
const nameToDeclarations = sourceFile.getNamedDeclarations();
const declarations = getProperty(nameToDeclarations, name.text);
const typeChecker = program.getTypeChecker();
for (const sourceFile of program.getSourceFiles()) {
const nameToDeclarations = sourceFile.getNamedDeclarations();
const declarations = getProperty(nameToDeclarations, name.text);
if (declarations) {
for (const declaration of declarations) {
const symbol = declaration.symbol;
if (symbol) {
const type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration);
if (type) {
const callSignatures = type.getCallSignatures();
if (callSignatures && callSignatures.length) {
return createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo);
}
if (declarations) {
for (const declaration of declarations) {
const symbol = declaration.symbol;
if (symbol) {
const type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration);
if (type) {
const callSignatures = type.getCallSignatures();
if (callSignatures && callSignatures.length) {
return createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo, typeChecker);
}
}
}
}
}
}
}
/**
* Returns relevant information for the argument list and the current argument if we are
* in the argument of an invocation; returns undefined otherwise.
*/
function getImmediatelyContainingArgumentInfo(node: Node): ArgumentListInfo {
if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) {
const callExpression = <CallExpression>node.parent;
// There are 3 cases to handle:
// 1. The token introduces a list, and should begin a sig help session
// 2. The token is either not associated with a list, or ends a list, so the session should end
// 3. The token is buried inside a list, and should give sig help
//
// The following are examples of each:
//
// Case 1:
// foo<#T, U>(#a, b) -> The token introduces a list, and should begin a sig help session
// Case 2:
// fo#o<T, U>#(a, b)# -> The token is either not associated with a list, or ends a list, so the session should end
// Case 3:
// foo<T#, U#>(a#, #b#) -> The token is buried inside a list, and should give sig help
// Find out if 'node' is an argument, a type argument, or neither
if (node.kind === SyntaxKind.LessThanToken ||
node.kind === SyntaxKind.OpenParenToken) {
// Find the list that starts right *after* the < or ( token.
// If the user has just opened a list, consider this item 0.
const list = getChildListThatStartsWithOpenerToken(callExpression, node, sourceFile);
const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos;
Debug.assert(list !== undefined);
return {
kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments,
invocation: callExpression,
argumentsSpan: getApplicableSpanForArguments(list),
argumentIndex: 0,
argumentCount: getArgumentCount(list)
};
}
// findListItemInfo can return undefined if we are not in parent's argument list
// or type argument list. This includes cases where the cursor is:
// - To the right of the closing paren, non-substitution template, or template tail.
// - Between the type arguments and the arguments (greater than token)
// - On the target of the call (parent.func)
// - On the 'new' keyword in a 'new' expression
const listItemInfo = findListItemInfo(node);
if (listItemInfo) {
const list = listItemInfo.list;
const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos;
const argumentIndex = getArgumentIndex(list, node);
const argumentCount = getArgumentCount(list);
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount,
`argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
return {
kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments,
invocation: callExpression,
argumentsSpan: getApplicableSpanForArguments(list),
argumentIndex: argumentIndex,
argumentCount: argumentCount
};
}
}
else if (node.kind === SyntaxKind.NoSubstitutionTemplateLiteral && node.parent.kind === SyntaxKind.TaggedTemplateExpression) {
// Check if we're actually inside the template;
// otherwise we'll fall out and return undefined.
if (isInsideTemplateLiteral(<LiteralExpression>node, position)) {
return getArgumentListInfoForTemplate(<TaggedTemplateExpression>node.parent, /*argumentIndex*/ 0);
}
}
else if (node.kind === SyntaxKind.TemplateHead && node.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) {
const templateExpression = <TemplateExpression>node.parent;
const tagExpression = <TaggedTemplateExpression>templateExpression.parent;
Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression);
const argumentIndex = isInsideTemplateLiteral(<LiteralExpression>node, position) ? 0 : 1;
return getArgumentListInfoForTemplate(tagExpression, argumentIndex);
}
else if (node.parent.kind === SyntaxKind.TemplateSpan && node.parent.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) {
const templateSpan = <TemplateSpan>node.parent;
const templateExpression = <TemplateExpression>templateSpan.parent;
const tagExpression = <TaggedTemplateExpression>templateExpression.parent;
Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression);
// If we're just after a template tail, don't show signature help.
if (node.kind === SyntaxKind.TemplateTail && !isInsideTemplateLiteral(<LiteralExpression>node, position)) {
return undefined;
}
const spanIndex = templateExpression.templateSpans.indexOf(templateSpan);
const argumentIndex = getArgumentIndexForTemplatePiece(spanIndex, node);
return getArgumentListInfoForTemplate(tagExpression, argumentIndex);
}
return undefined;
}
function getArgumentIndex(argumentsList: Node, node: Node) {
// The list we got back can include commas. In the presence of errors it may
// also just have nodes without commas. For example "Foo(a b c)" will have 3
// args without commas. We want to find what index we're at. So we count
// forward until we hit ourselves, only incrementing the index if it isn't a
// comma.
/**
* Returns relevant information for the argument list and the current argument if we are
* in the argument of an invocation; returns undefined otherwise.
*/
function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo {
if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) {
const callExpression = <CallExpression>node.parent;
// There are 3 cases to handle:
// 1. The token introduces a list, and should begin a sig help session
// 2. The token is either not associated with a list, or ends a list, so the session should end
// 3. The token is buried inside a list, and should give sig help
//
// Note: the subtlety around trailing commas (in getArgumentCount) does not apply
// here. That's because we're only walking forward until we hit the node we're
// on. In that case, even if we're after the trailing comma, we'll still see
// that trailing comma in the list, and we'll have generated the appropriate
// arg index.
let argumentIndex = 0;
const listChildren = argumentsList.getChildren();
for (const child of listChildren) {
if (child === node) {
break;
}
if (child.kind !== SyntaxKind.CommaToken) {
argumentIndex++;
}
// The following are examples of each:
//
// Case 1:
// foo<#T, U>(#a, b) -> The token introduces a list, and should begin a sig help session
// Case 2:
// fo#o<T, U>#(a, b)# -> The token is either not associated with a list, or ends a list, so the session should end
// Case 3:
// foo<T#, U#>(a#, #b#) -> The token is buried inside a list, and should give sig help
// Find out if 'node' is an argument, a type argument, or neither
if (node.kind === SyntaxKind.LessThanToken ||
node.kind === SyntaxKind.OpenParenToken) {
// Find the list that starts right *after* the < or ( token.
// If the user has just opened a list, consider this item 0.
const list = getChildListThatStartsWithOpenerToken(callExpression, node, sourceFile);
const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos;
Debug.assert(list !== undefined);
return {
kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments,
invocation: callExpression,
argumentsSpan: getApplicableSpanForArguments(list, sourceFile),
argumentIndex: 0,
argumentCount: getArgumentCount(list)
};
}
return argumentIndex;
}
// findListItemInfo can return undefined if we are not in parent's argument list
// or type argument list. This includes cases where the cursor is:
// - To the right of the closing paren, non-substitution template, or template tail.
// - Between the type arguments and the arguments (greater than token)
// - On the target of the call (parent.func)
// - On the 'new' keyword in a 'new' expression
const listItemInfo = findListItemInfo(node);
if (listItemInfo) {
const list = listItemInfo.list;
const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos;
function getArgumentCount(argumentsList: Node) {
// The argument count for a list is normally the number of non-comma children it has.
// For example, if you have "Foo(a,b)" then there will be three children of the arg
// list 'a' '<comma>' 'b'. So, in this case the arg count will be 2. However, there
// is a small subtlety. If you have "Foo(a,)", then the child list will just have
// 'a' '<comma>'. So, in the case where the last child is a comma, we increase the
// arg count by one to compensate.
//
// Note: this subtlety only applies to the last comma. If you had "Foo(a,," then
// we'll have: 'a' '<comma>' '<missing>'
// That will give us 2 non-commas. We then add one for the last comma, givin us an
// arg count of 3.
const listChildren = argumentsList.getChildren();
const argumentIndex = getArgumentIndex(list, node);
const argumentCount = getArgumentCount(list);
let argumentCount = countWhere(listChildren, arg => arg.kind !== SyntaxKind.CommaToken);
if (listChildren.length > 0 && lastOrUndefined(listChildren).kind === SyntaxKind.CommaToken) {
argumentCount++;
}
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount,
`argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
return argumentCount;
}
// spanIndex is either the index for a given template span.
// This does not give appropriate results for a NoSubstitutionTemplateLiteral
function getArgumentIndexForTemplatePiece(spanIndex: number, node: Node): number {
// Because the TemplateStringsArray is the first argument, we have to offset each substitution expression by 1.
// There are three cases we can encounter:
// 1. We are precisely in the template literal (argIndex = 0).
// 2. We are in or to the right of the substitution expression (argIndex = spanIndex + 1).
// 3. We are directly to the right of the template literal, but because we look for the token on the left,
// not enough to put us in the substitution expression; we should consider ourselves part of
// the *next* span's expression by offsetting the index (argIndex = (spanIndex + 1) + 1).
//
// Example: f `# abcd $#{# 1 + 1# }# efghi ${ #"#hello"# } # `
// ^ ^ ^ ^ ^ ^ ^ ^ ^
// Case: 1 1 3 2 1 3 2 2 1
Debug.assert(position >= node.getStart(), "Assumed 'position' could not occur before node.");
if (isTemplateLiteralKind(node.kind)) {
if (isInsideTemplateLiteral(<LiteralExpression>node, position)) {
return 0;
}
return spanIndex + 2;
}
return spanIndex + 1;
}
function getArgumentListInfoForTemplate(tagExpression: TaggedTemplateExpression, argumentIndex: number): ArgumentListInfo {
// argumentCount is either 1 or (numSpans + 1) to account for the template strings array argument.
const argumentCount = tagExpression.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral
? 1
: (<TemplateExpression>tagExpression.template).templateSpans.length + 1;
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
return {
kind: ArgumentListKind.TaggedTemplateArguments,
invocation: tagExpression,
argumentsSpan: getApplicableSpanForTaggedTemplate(tagExpression),
argumentIndex: argumentIndex,
argumentCount: argumentCount
};
}
function getApplicableSpanForArguments(argumentsList: Node): TextSpan {
// We use full start and skip trivia on the end because we want to include trivia on
// both sides. For example,
//
// foo( /*comment */ a, b, c /*comment*/ )
// | |
//
// The applicable span is from the first bar to the second bar (inclusive,
// but not including parentheses)
const applicableSpanStart = argumentsList.getFullStart();
const applicableSpanEnd = skipTrivia(sourceFile.text, argumentsList.getEnd(), /*stopAfterLineBreak*/ false);
return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart);
}
function getApplicableSpanForTaggedTemplate(taggedTemplate: TaggedTemplateExpression): TextSpan {
const template = taggedTemplate.template;
const applicableSpanStart = template.getStart();
let applicableSpanEnd = template.getEnd();
// We need to adjust the end position for the case where the template does not have a tail.
// Otherwise, we will not show signature help past the expression.
// For example,
//
// ` ${ 1 + 1 foo(10)
// | |
//
// This is because a Missing node has no width. However, what we actually want is to include trivia
// leading up to the next token in case the user is about to type in a TemplateMiddle or TemplateTail.
if (template.kind === SyntaxKind.TemplateExpression) {
const lastSpan = lastOrUndefined((<TemplateExpression>template).templateSpans);
if (lastSpan.literal.getFullWidth() === 0) {
applicableSpanEnd = skipTrivia(sourceFile.text, applicableSpanEnd, /*stopAfterLineBreak*/ false);
}
}
return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart);
}
function getContainingArgumentInfo(node: Node): ArgumentListInfo {
for (let n = node; n.kind !== SyntaxKind.SourceFile; n = n.parent) {
if (isFunctionBlock(n)) {
return undefined;
}
// If the node is not a subspan of its parent, this is a big problem.
// There have been crashes that might be caused by this violation.
if (n.pos < n.parent.pos || n.end > n.parent.end) {
Debug.fail("Node of kind " + n.kind + " is not a subspan of its parent of kind " + n.parent.kind);
}
const argumentInfo = getImmediatelyContainingArgumentInfo(n);
if (argumentInfo) {
return argumentInfo;
}
// TODO: Handle generic call with incomplete syntax
return {
kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments,
invocation: callExpression,
argumentsSpan: getApplicableSpanForArguments(list, sourceFile),
argumentIndex: argumentIndex,
argumentCount: argumentCount
};
}
return undefined;
}
function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node {
const children = parent.getChildren(sourceFile);
const indexOfOpenerToken = children.indexOf(openerToken);
Debug.assert(indexOfOpenerToken >= 0 && children.length > indexOfOpenerToken + 1);
return children[indexOfOpenerToken + 1];
else if (node.kind === SyntaxKind.NoSubstitutionTemplateLiteral && node.parent.kind === SyntaxKind.TaggedTemplateExpression) {
// Check if we're actually inside the template;
// otherwise we'll fall out and return undefined.
if (isInsideTemplateLiteral(<LiteralExpression>node, position)) {
return getArgumentListInfoForTemplate(<TaggedTemplateExpression>node.parent, /*argumentIndex*/ 0, sourceFile);
}
}
else if (node.kind === SyntaxKind.TemplateHead && node.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) {
const templateExpression = <TemplateExpression>node.parent;
const tagExpression = <TaggedTemplateExpression>templateExpression.parent;
Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression);
/**
* The selectedItemIndex could be negative for several reasons.
* 1. There are too many arguments for all of the overloads
* 2. None of the overloads were type compatible
* The solution here is to try to pick the best overload by picking
* either the first one that has an appropriate number of parameters,
* or the one with the most parameters.
*/
function selectBestInvalidOverloadIndex(candidates: Signature[], argumentCount: number): number {
let maxParamsSignatureIndex = -1;
let maxParams = -1;
for (let i = 0; i < candidates.length; i++) {
const candidate = candidates[i];
const argumentIndex = isInsideTemplateLiteral(<LiteralExpression>node, position) ? 0 : 1;
if (candidate.hasRestParameter || candidate.parameters.length >= argumentCount) {
return i;
}
return getArgumentListInfoForTemplate(tagExpression, argumentIndex, sourceFile);
}
else if (node.parent.kind === SyntaxKind.TemplateSpan && node.parent.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) {
const templateSpan = <TemplateSpan>node.parent;
const templateExpression = <TemplateExpression>templateSpan.parent;
const tagExpression = <TaggedTemplateExpression>templateExpression.parent;
Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression);
if (candidate.parameters.length > maxParams) {
maxParams = candidate.parameters.length;
maxParamsSignatureIndex = i;
}
// If we're just after a template tail, don't show signature help.
if (node.kind === SyntaxKind.TemplateTail && !isInsideTemplateLiteral(<LiteralExpression>node, position)) {
return undefined;
}
return maxParamsSignatureIndex;
const spanIndex = templateExpression.templateSpans.indexOf(templateSpan);
const argumentIndex = getArgumentIndexForTemplatePiece(spanIndex, node, position);
return getArgumentListInfoForTemplate(tagExpression, argumentIndex, sourceFile);
}
function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListInfo: ArgumentListInfo): SignatureHelpItems {
const applicableSpan = argumentListInfo.argumentsSpan;
const isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments;
return undefined;
}
const invocation = argumentListInfo.invocation;
const callTarget = getInvokedExpression(invocation);
const callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget);
const callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined);
const items: SignatureHelpItem[] = map(candidates, candidateSignature => {
let signatureHelpParameters: SignatureHelpParameter[];
const prefixDisplayParts: SymbolDisplayPart[] = [];
const suffixDisplayParts: SymbolDisplayPart[] = [];
function getArgumentIndex(argumentsList: Node, node: Node) {
// The list we got back can include commas. In the presence of errors it may
// also just have nodes without commas. For example "Foo(a b c)" will have 3
// args without commas. We want to find what index we're at. So we count
// forward until we hit ourselves, only incrementing the index if it isn't a
// comma.
//
// Note: the subtlety around trailing commas (in getArgumentCount) does not apply
// here. That's because we're only walking forward until we hit the node we're
// on. In that case, even if we're after the trailing comma, we'll still see
// that trailing comma in the list, and we'll have generated the appropriate
// arg index.
let argumentIndex = 0;
const listChildren = argumentsList.getChildren();
for (const child of listChildren) {
if (child === node) {
break;
}
if (child.kind !== SyntaxKind.CommaToken) {
argumentIndex++;
}
}
if (callTargetDisplayParts) {
addRange(prefixDisplayParts, callTargetDisplayParts);
}
return argumentIndex;
}
if (isTypeParameterList) {
prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken));
const typeParameters = candidateSignature.typeParameters;
signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray;
suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken));
const parameterParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisType, candidateSignature.parameters, writer, invocation));
addRange(suffixDisplayParts, parameterParts);
}
else {
const typeParameterParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildDisplayForTypeParametersAndDelimiters(candidateSignature.typeParameters, writer, invocation));
addRange(prefixDisplayParts, typeParameterParts);
prefixDisplayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
function getArgumentCount(argumentsList: Node) {
// The argument count for a list is normally the number of non-comma children it has.
// For example, if you have "Foo(a,b)" then there will be three children of the arg
// list 'a' '<comma>' 'b'. So, in this case the arg count will be 2. However, there
// is a small subtlety. If you have "Foo(a,)", then the child list will just have
// 'a' '<comma>'. So, in the case where the last child is a comma, we increase the
// arg count by one to compensate.
//
// Note: this subtlety only applies to the last comma. If you had "Foo(a,," then
// we'll have: 'a' '<comma>' '<missing>'
// That will give us 2 non-commas. We then add one for the last comma, givin us an
// arg count of 3.
const listChildren = argumentsList.getChildren();
const parameters = candidateSignature.parameters;
signatureHelpParameters = parameters.length > 0 ? map(parameters, createSignatureHelpParameterForParameter) : emptyArray;
suffixDisplayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
}
let argumentCount = countWhere(listChildren, arg => arg.kind !== SyntaxKind.CommaToken);
if (listChildren.length > 0 && lastOrUndefined(listChildren).kind === SyntaxKind.CommaToken) {
argumentCount++;
}
const returnTypeParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildReturnTypeDisplay(candidateSignature, writer, invocation));
addRange(suffixDisplayParts, returnTypeParts);
return argumentCount;
}
return {
isVariadic: candidateSignature.hasRestParameter,
prefixDisplayParts,
suffixDisplayParts,
separatorDisplayParts: [punctuationPart(SyntaxKind.CommaToken), spacePart()],
parameters: signatureHelpParameters,
documentation: candidateSignature.getDocumentationComment()
};
});
// spanIndex is either the index for a given template span.
// This does not give appropriate results for a NoSubstitutionTemplateLiteral
function getArgumentIndexForTemplatePiece(spanIndex: number, node: Node, position: number): number {
// Because the TemplateStringsArray is the first argument, we have to offset each substitution expression by 1.
// There are three cases we can encounter:
// 1. We are precisely in the template literal (argIndex = 0).
// 2. We are in or to the right of the substitution expression (argIndex = spanIndex + 1).
// 3. We are directly to the right of the template literal, but because we look for the token on the left,
// not enough to put us in the substitution expression; we should consider ourselves part of
// the *next* span's expression by offsetting the index (argIndex = (spanIndex + 1) + 1).
//
// Example: f `# abcd $#{# 1 + 1# }# efghi ${ #"#hello"# } # `
// ^ ^ ^ ^ ^ ^ ^ ^ ^
// Case: 1 1 3 2 1 3 2 2 1
Debug.assert(position >= node.getStart(), "Assumed 'position' could not occur before node.");
if (isTemplateLiteralKind(node.kind)) {
if (isInsideTemplateLiteral(<LiteralExpression>node, position)) {
return 0;
}
return spanIndex + 2;
}
return spanIndex + 1;
}
const argumentIndex = argumentListInfo.argumentIndex;
function getArgumentListInfoForTemplate(tagExpression: TaggedTemplateExpression, argumentIndex: number, sourceFile: SourceFile): ArgumentListInfo {
// argumentCount is either 1 or (numSpans + 1) to account for the template strings array argument.
const argumentCount = tagExpression.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral
? 1
: (<TemplateExpression>tagExpression.template).templateSpans.length + 1;
// argumentCount is the *apparent* number of arguments.
const argumentCount = argumentListInfo.argumentCount;
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
let selectedItemIndex = candidates.indexOf(bestSignature);
if (selectedItemIndex < 0) {
selectedItemIndex = selectBestInvalidOverloadIndex(candidates, argumentCount);
return {
kind: ArgumentListKind.TaggedTemplateArguments,
invocation: tagExpression,
argumentsSpan: getApplicableSpanForTaggedTemplate(tagExpression, sourceFile),
argumentIndex: argumentIndex,
argumentCount: argumentCount
};
}
function getApplicableSpanForArguments(argumentsList: Node, sourceFile: SourceFile): TextSpan {
// We use full start and skip trivia on the end because we want to include trivia on
// both sides. For example,
//
// foo( /*comment */ a, b, c /*comment*/ )
// | |
//
// The applicable span is from the first bar to the second bar (inclusive,
// but not including parentheses)
const applicableSpanStart = argumentsList.getFullStart();
const applicableSpanEnd = skipTrivia(sourceFile.text, argumentsList.getEnd(), /*stopAfterLineBreak*/ false);
return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart);
}
function getApplicableSpanForTaggedTemplate(taggedTemplate: TaggedTemplateExpression, sourceFile: SourceFile): TextSpan {
const template = taggedTemplate.template;
const applicableSpanStart = template.getStart();
let applicableSpanEnd = template.getEnd();
// We need to adjust the end position for the case where the template does not have a tail.
// Otherwise, we will not show signature help past the expression.
// For example,
//
// ` ${ 1 + 1 foo(10)
// | |
//
// This is because a Missing node has no width. However, what we actually want is to include trivia
// leading up to the next token in case the user is about to type in a TemplateMiddle or TemplateTail.
if (template.kind === SyntaxKind.TemplateExpression) {
const lastSpan = lastOrUndefined((<TemplateExpression>template).templateSpans);
if (lastSpan.literal.getFullWidth() === 0) {
applicableSpanEnd = skipTrivia(sourceFile.text, applicableSpanEnd, /*stopAfterLineBreak*/ false);
}
}
return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart);
}
export function getContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo {
for (let n = node; n.kind !== SyntaxKind.SourceFile; n = n.parent) {
if (isFunctionBlock(n)) {
return undefined;
}
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
// If the node is not a subspan of its parent, this is a big problem.
// There have been crashes that might be caused by this violation.
if (n.pos < n.parent.pos || n.end > n.parent.end) {
Debug.fail("Node of kind " + n.kind + " is not a subspan of its parent of kind " + n.parent.kind);
}
const argumentInfo = getImmediatelyContainingArgumentInfo(n, position, sourceFile);
if (argumentInfo) {
return argumentInfo;
}
// TODO: Handle generic call with incomplete syntax
}
return undefined;
}
function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node {
const children = parent.getChildren(sourceFile);
const indexOfOpenerToken = children.indexOf(openerToken);
Debug.assert(indexOfOpenerToken >= 0 && children.length > indexOfOpenerToken + 1);
return children[indexOfOpenerToken + 1];
}
/**
* The selectedItemIndex could be negative for several reasons.
* 1. There are too many arguments for all of the overloads
* 2. None of the overloads were type compatible
* The solution here is to try to pick the best overload by picking
* either the first one that has an appropriate number of parameters,
* or the one with the most parameters.
*/
function selectBestInvalidOverloadIndex(candidates: Signature[], argumentCount: number): number {
let maxParamsSignatureIndex = -1;
let maxParams = -1;
for (let i = 0; i < candidates.length; i++) {
const candidate = candidates[i];
if (candidate.hasRestParameter || candidate.parameters.length >= argumentCount) {
return i;
}
if (candidate.parameters.length > maxParams) {
maxParams = candidate.parameters.length;
maxParamsSignatureIndex = i;
}
}
return maxParamsSignatureIndex;
}
function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListInfo: ArgumentListInfo, typeChecker: TypeChecker): SignatureHelpItems {
const applicableSpan = argumentListInfo.argumentsSpan;
const isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments;
const invocation = argumentListInfo.invocation;
const callTarget = getInvokedExpression(invocation);
const callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget);
const callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined);
const items: SignatureHelpItem[] = map(candidates, candidateSignature => {
let signatureHelpParameters: SignatureHelpParameter[];
const prefixDisplayParts: SymbolDisplayPart[] = [];
const suffixDisplayParts: SymbolDisplayPart[] = [];
if (callTargetDisplayParts) {
addRange(prefixDisplayParts, callTargetDisplayParts);
}
if (isTypeParameterList) {
prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken));
const typeParameters = candidateSignature.typeParameters;
signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray;
suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken));
const parameterParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisType, candidateSignature.parameters, writer, invocation));
addRange(suffixDisplayParts, parameterParts);
}
else {
const typeParameterParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildDisplayForTypeParametersAndDelimiters(candidateSignature.typeParameters, writer, invocation));
addRange(prefixDisplayParts, typeParameterParts);
prefixDisplayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
const parameters = candidateSignature.parameters;
signatureHelpParameters = parameters.length > 0 ? map(parameters, createSignatureHelpParameterForParameter) : emptyArray;
suffixDisplayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
}
const returnTypeParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildReturnTypeDisplay(candidateSignature, writer, invocation));
addRange(suffixDisplayParts, returnTypeParts);
return {
items,
applicableSpan,
selectedItemIndex,
argumentIndex,
argumentCount
isVariadic: candidateSignature.hasRestParameter,
prefixDisplayParts,
suffixDisplayParts,
separatorDisplayParts: [punctuationPart(SyntaxKind.CommaToken), spacePart()],
parameters: signatureHelpParameters,
documentation: candidateSignature.getDocumentationComment()
};
});
function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter {
const displayParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildParameterDisplay(parameter, writer, invocation));
const argumentIndex = argumentListInfo.argumentIndex;
return {
name: parameter.name,
documentation: parameter.getDocumentationComment(),
displayParts,
isOptional: typeChecker.isOptionalParameter(<ParameterDeclaration>parameter.valueDeclaration)
};
}
// argumentCount is the *apparent* number of arguments.
const argumentCount = argumentListInfo.argumentCount;
function createSignatureHelpParameterForTypeParameter(typeParameter: TypeParameter): SignatureHelpParameter {
const displayParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildTypeParameterDisplay(typeParameter, writer, invocation));
let selectedItemIndex = candidates.indexOf(bestSignature);
if (selectedItemIndex < 0) {
selectedItemIndex = selectBestInvalidOverloadIndex(candidates, argumentCount);
}
return {
name: typeParameter.symbol.name,
documentation: emptyArray,
displayParts,
isOptional: false
};
}
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
return {
items,
applicableSpan,
selectedItemIndex,
argumentIndex,
argumentCount
};
function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter {
const displayParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildParameterDisplay(parameter, writer, invocation));
return {
name: parameter.name,
documentation: parameter.getDocumentationComment(),
displayParts,
isOptional: typeChecker.isOptionalParameter(<ParameterDeclaration>parameter.valueDeclaration)
};
}
function createSignatureHelpParameterForTypeParameter(typeParameter: TypeParameter): SignatureHelpParameter {
const displayParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildTypeParameterDisplay(typeParameter, writer, invocation));
return {
name: typeParameter.symbol.name,
documentation: emptyArray,
displayParts,
isOptional: false
};
}
}
}
+66 -21
View File
@@ -234,29 +234,29 @@ namespace ts {
/* Gets the token whose text has range [start, end) and
* position >= start and (position < end or (position === end && token is keyword or identifier))
*/
export function getTouchingWord(sourceFile: SourceFile, position: number): Node {
return getTouchingToken(sourceFile, position, n => isWord(n.kind));
export function getTouchingWord(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node {
return getTouchingToken(sourceFile, position, n => isWord(n.kind), includeJsDocComment);
}
/* Gets the token whose text has range [start, end) and position >= start
* and (position < end or (position === end && token is keyword or identifier or numeric/string literal))
*/
export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node {
return getTouchingToken(sourceFile, position, n => isPropertyName(n.kind));
export function getTouchingPropertyName(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node {
return getTouchingToken(sourceFile, position, n => isPropertyName(n.kind), includeJsDocComment);
}
/** Returns the token if position is in [start, end) or if position === end and includeItemAtEndPosition(token) === true */
export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition);
export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean, includeJsDocComment = false): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition, includeJsDocComment);
}
/** Returns a token if position is in [start-of-leading-trivia, end) */
export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includeItemAtEndPosition*/ undefined);
export function getTokenAtPosition(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includeItemAtEndPosition*/ undefined, includeJsDocComment);
}
/** Get the token whose text contains the position */
function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean): Node {
function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean, includeJsDocComment = false): Node {
let current: Node = sourceFile;
outer: while (true) {
if (isToken(current)) {
@@ -264,13 +264,34 @@ namespace ts {
return current;
}
if (includeJsDocComment) {
const jsDocChildren = ts.filter(current.getChildren(), isJSDocNode);
for (const jsDocChild of jsDocChildren) {
const start = allowPositionInLeadingTrivia ? jsDocChild.getFullStart() : jsDocChild.getStart(sourceFile, includeJsDocComment);
if (start <= position) {
const end = jsDocChild.getEnd();
if (position < end || (position === end && jsDocChild.kind === SyntaxKind.EndOfFileToken)) {
current = jsDocChild;
continue outer;
}
else if (includeItemAtEndPosition && end === position) {
const previousToken = findPrecedingToken(position, sourceFile, jsDocChild);
if (previousToken && includeItemAtEndPosition(previousToken)) {
return previousToken;
}
}
}
}
}
// find the child that contains 'position'
for (let i = 0, n = current.getChildCount(sourceFile); i < n; i++) {
const child = current.getChildAt(i);
if (position < child.getFullStart() || position > child.getEnd()) {
// all jsDocComment nodes were already visited
if (isJSDocNode(child)) {
continue;
}
const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile);
const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, includeJsDocComment);
if (start <= position) {
const end = child.getEnd();
if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) {
@@ -285,6 +306,7 @@ namespace ts {
}
}
}
return current;
}
}
@@ -404,9 +426,27 @@ namespace ts {
}
}
export function isInString(sourceFile: SourceFile, position: number) {
const token = getTokenAtPosition(sourceFile, position);
return token && (token.kind === SyntaxKind.StringLiteral || token.kind === SyntaxKind.StringLiteralType) && position > token.getStart(sourceFile);
export function isInString(sourceFile: SourceFile, position: number): boolean {
const previousToken = findPrecedingToken(position, sourceFile);
if (previousToken &&
(previousToken.kind === SyntaxKind.StringLiteral || previousToken.kind === SyntaxKind.StringLiteralType)) {
const start = previousToken.getStart();
const end = previousToken.getEnd();
// To be "in" one of these literals, the position has to be:
// 1. entirely within the token text.
// 2. at the end position of an unterminated token.
// 3. at the end of a regular expression (due to trailing flags like '/foo/g').
if (start < position && position < end) {
return true;
}
if (position === end) {
return !!(<LiteralExpression>previousToken).isUnterminated;
}
}
return false;
}
export function isInComment(sourceFile: SourceFile, position: number) {
@@ -423,6 +463,10 @@ namespace ts {
return false;
}
if (token.kind === SyntaxKind.JsxText) {
return true;
}
// <div>Hello |</div>
if (token.kind === SyntaxKind.LessThanToken && token.parent.kind === SyntaxKind.JsxText) {
return true;
@@ -433,7 +477,7 @@ namespace ts {
return true;
}
// <div> {
// <div> {
// |
// } < /div>
if (token && token.kind === SyntaxKind.CloseBraceToken && token.parent.kind === SyntaxKind.JsxExpression) {
@@ -518,11 +562,12 @@ namespace ts {
}
if (node) {
const jsDocComment = node.jsDocComment;
if (jsDocComment) {
for (const tag of jsDocComment.tags) {
if (tag.pos <= position && position <= tag.end) {
return tag;
if (node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
for (const tag of jsDocComment.tags) {
if (tag.pos <= position && position <= tag.end) {
return tag;
}
}
}
}
@@ -646,7 +691,7 @@ namespace ts {
// [a, b, c] of
// [x, [a, b, c] ] = someExpression
// or
// or
// {x, a: {a, b, c} } = someExpression
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.kind === SyntaxKind.PropertyAssignment ? node.parent.parent : node.parent)) {
return true;
@@ -1,12 +0,0 @@
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts(1,13): error TS2304: Cannot find name 'b'.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts(1,14): error TS1009: Trailing comma not allowed.
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts (2 errors) ====
var v = (a: b,) => {
~
!!! error TS2304: Cannot find name 'b'.
~
!!! error TS1009: Trailing comma not allowed.
};
@@ -1,8 +0,0 @@
//// [ArrowFunction2.ts]
var v = (a: b,) => {
};
//// [ArrowFunction2.js]
var v = function (a) {
};
@@ -1,8 +1,8 @@
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration11_es6.ts(1,10): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration11_es6.ts(1,10): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration11_es6.ts (1 errors) ====
function * yield() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
}
@@ -1,11 +1,11 @@
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration13_es6.ts(1,10): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration13_es6.ts(1,10): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration13_es6.ts(3,11): error TS2304: Cannot find name 'yield'.
==== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration13_es6.ts (2 errors) ====
function * foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
// Legal to use 'yield' in a type context.
var v: yield;
~~~~~
@@ -1,8 +1,8 @@
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration1_es6.ts(1,10): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration1_es6.ts(1,10): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration1_es6.ts (1 errors) ====
function * foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
}
@@ -1,11 +1,11 @@
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration6_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration6_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration6_es6.ts(1,18): error TS2523: 'yield' expressions cannot be used in a parameter initializer.
==== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration6_es6.ts (2 errors) ====
function*foo(a = yield) {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
~~~~~
!!! error TS2523: 'yield' expressions cannot be used in a parameter initializer.
}
@@ -1,16 +1,16 @@
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration7_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration7_es6.ts(3,11): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration7_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration7_es6.ts(3,11): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration7_es6.ts(3,20): error TS2523: 'yield' expressions cannot be used in a parameter initializer.
==== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration7_es6.ts (3 errors) ====
function*bar() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
// 'yield' here is an identifier, and not a yield expression.
function*foo(a = yield) {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
~~~~~
!!! error TS2523: 'yield' expressions cannot be used in a parameter initializer.
}
@@ -1,9 +1,9 @@
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration9_es6.ts(1,10): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration9_es6.ts(1,10): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration9_es6.ts (1 errors) ====
function * foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
var v = { [yield]: foo }
}
@@ -1,7 +1,7 @@
tests/cases/conformance/es6/functionExpressions/FunctionExpression1_es6.ts(1,18): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/functionExpressions/FunctionExpression1_es6.ts(1,18): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/functionExpressions/FunctionExpression1_es6.ts (1 errors) ====
var v = function * () { }
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
@@ -1,7 +1,7 @@
tests/cases/conformance/es6/functionExpressions/FunctionExpression2_es6.ts(1,18): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/functionExpressions/FunctionExpression2_es6.ts(1,18): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/functionExpressions/FunctionExpression2_es6.ts (1 errors) ====
var v = function * foo() { }
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
@@ -1,7 +1,7 @@
tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments1_es6.ts(1,11): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments1_es6.ts(1,11): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments1_es6.ts (1 errors) ====
var v = { *foo() { } }
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
@@ -1,10 +1,10 @@
tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments5_es6.ts(1,11): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments5_es6.ts(1,11): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments5_es6.ts(1,13): error TS2304: Cannot find name 'foo'.
==== tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments5_es6.ts (2 errors) ====
var v = { *[foo()]() { } }
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
~~~
!!! error TS2304: Cannot find name 'foo'.
@@ -1,9 +1,9 @@
tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration1_es6.ts(2,4): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration1_es6.ts(2,4): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration1_es6.ts (1 errors) ====
class C {
*foo() { }
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
}
@@ -1,9 +1,9 @@
tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration2_es6.ts(2,11): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration2_es6.ts(2,11): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration2_es6.ts (1 errors) ====
class C {
public * foo() { }
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
}
@@ -1,4 +1,4 @@
tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration3_es6.ts(2,4): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration3_es6.ts(2,4): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration3_es6.ts(2,6): error TS2304: Cannot find name 'foo'.
@@ -6,7 +6,7 @@ tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration
class C {
*[foo]() { }
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
~~~
!!! error TS2304: Cannot find name 'foo'.
}
@@ -1,9 +1,9 @@
tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration7_es6.ts(2,4): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration7_es6.ts(2,4): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/memberFunctionDeclarations/MemberFunctionDeclaration7_es6.ts (1 errors) ====
class C {
*foo<T>() { }
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
}
@@ -1,11 +1,11 @@
tests/cases/conformance/es6/yieldExpressions/YieldExpression10_es6.ts(1,11): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression10_es6.ts(1,11): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression10_es6.ts(2,11): error TS2304: Cannot find name 'foo'.
==== tests/cases/conformance/es6/yieldExpressions/YieldExpression10_es6.ts (2 errors) ====
var v = { * foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
yield(foo);
~~~
!!! error TS2304: Cannot find name 'foo'.
@@ -1,4 +1,4 @@
tests/cases/conformance/es6/yieldExpressions/YieldExpression11_es6.ts(2,3): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression11_es6.ts(2,3): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression11_es6.ts(3,11): error TS2663: Cannot find name 'foo'. Did you mean the instance member 'this.foo'?
@@ -6,7 +6,7 @@ tests/cases/conformance/es6/yieldExpressions/YieldExpression11_es6.ts(3,11): err
class C {
*foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
yield(foo);
~~~
!!! error TS2663: Cannot find name 'foo'. Did you mean the instance member 'this.foo'?
@@ -1,7 +1,7 @@
tests/cases/conformance/es6/yieldExpressions/YieldExpression13_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression13_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/yieldExpressions/YieldExpression13_es6.ts (1 errors) ====
function* foo() { yield }
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
@@ -1,11 +1,11 @@
tests/cases/conformance/es6/yieldExpressions/YieldExpression16_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression16_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression16_es6.ts(3,5): error TS1163: A 'yield' expression is only allowed in a generator body.
==== tests/cases/conformance/es6/yieldExpressions/YieldExpression16_es6.ts (2 errors) ====
function* foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
function bar() {
yield foo;
~~~~~
@@ -1,15 +1,15 @@
tests/cases/conformance/es6/yieldExpressions/YieldExpression19_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression19_es6.ts(3,13): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression19_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression19_es6.ts(3,13): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/yieldExpressions/YieldExpression19_es6.ts (2 errors) ====
function*foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
function bar() {
function* quux() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
yield(foo);
}
}
@@ -1,10 +1,10 @@
tests/cases/conformance/es6/yieldExpressions/YieldExpression3_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression3_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/yieldExpressions/YieldExpression3_es6.ts (1 errors) ====
function* foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
yield
yield
}
@@ -1,10 +1,10 @@
tests/cases/conformance/es6/yieldExpressions/YieldExpression4_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression4_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/yieldExpressions/YieldExpression4_es6.ts (1 errors) ====
function* foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
yield;
yield;
}
@@ -1,11 +1,11 @@
tests/cases/conformance/es6/yieldExpressions/YieldExpression6_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression6_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression6_es6.ts(2,9): error TS2488: Type must have a '[Symbol.iterator]()' method that returns an iterator.
==== tests/cases/conformance/es6/yieldExpressions/YieldExpression6_es6.ts (2 errors) ====
function* foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
yield*foo
~~~
!!! error TS2488: Type must have a '[Symbol.iterator]()' method that returns an iterator.
@@ -1,9 +1,9 @@
tests/cases/conformance/es6/yieldExpressions/YieldExpression7_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression7_es6.ts(1,9): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/yieldExpressions/YieldExpression7_es6.ts (1 errors) ====
function* foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
yield foo
}
@@ -1,5 +1,5 @@
tests/cases/conformance/es6/yieldExpressions/YieldExpression8_es6.ts(1,1): error TS2304: Cannot find name 'yield'.
tests/cases/conformance/es6/yieldExpressions/YieldExpression8_es6.ts(2,9): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression8_es6.ts(2,9): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
==== tests/cases/conformance/es6/yieldExpressions/YieldExpression8_es6.ts (2 errors) ====
@@ -8,6 +8,6 @@ tests/cases/conformance/es6/yieldExpressions/YieldExpression8_es6.ts(2,9): error
!!! error TS2304: Cannot find name 'yield'.
function* foo() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
yield(foo);
}
@@ -1,11 +1,11 @@
tests/cases/conformance/es6/yieldExpressions/YieldExpression9_es6.ts(1,17): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression9_es6.ts(1,17): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldExpression9_es6.ts(2,9): error TS2304: Cannot find name 'foo'.
==== tests/cases/conformance/es6/yieldExpressions/YieldExpression9_es6.ts (2 errors) ====
var v = function*() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
yield(foo);
~~~
!!! error TS2304: Cannot find name 'foo'.
@@ -1,11 +1,11 @@
tests/cases/conformance/es6/yieldExpressions/YieldStarExpression4_es6.ts(1,10): error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldStarExpression4_es6.ts(1,10): error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
tests/cases/conformance/es6/yieldExpressions/YieldStarExpression4_es6.ts(2,13): error TS2488: Type must have a '[Symbol.iterator]()' method that returns an iterator.
==== tests/cases/conformance/es6/yieldExpressions/YieldStarExpression4_es6.ts (2 errors) ====
function *g() {
~
!!! error TS1220: Generators are only available when targeting ECMAScript 6 or higher.
!!! error TS1220: Generators are only available when targeting ECMAScript 2015 or higher.
yield * [];
~~
!!! error TS2488: Type must have a '[Symbol.iterator]()' method that returns an iterator.
@@ -13,5 +13,5 @@ module M {
import r = M.X;
>r : Symbol(r, Decl(acceptableAlias1.ts, 4, 1))
>M : Symbol(M, Decl(acceptableAlias1.ts, 0, 0))
>X : Symbol(r, Decl(acceptableAlias1.ts, 0, 10))
>X : Symbol(M.X, Decl(acceptableAlias1.ts, 2, 5))
@@ -0,0 +1,44 @@
//// [tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts] ////
//// [declarations.d.ts]
declare module "foo*baz" {
export function foo(s: string): void;
}
// Augmentations still work
declare module "foo*baz" {
export const baz: string;
}
// Longest prefix wins
declare module "foos*" {
export const foos: string;
}
declare module "*!text" {
const x: string;
export default x;
}
//// [user.ts]
///<reference path="declarations.d.ts" />
import {foo, baz} from "foobarbaz";
foo(baz);
import {foos} from "foosball";
foo(foos);
// Works with relative file name
import fileText from "./file!text";
foo(fileText);
//// [user.js]
"use strict";
///<reference path="declarations.d.ts" />
var foobarbaz_1 = require("foobarbaz");
foobarbaz_1.foo(foobarbaz_1.baz);
var foosball_1 = require("foosball");
foobarbaz_1.foo(foosball_1.foos);
// Works with relative file name
var file_text_1 = require("./file!text");
foobarbaz_1.foo(file_text_1["default"]);
@@ -0,0 +1,51 @@
=== tests/cases/conformance/ambient/user.ts ===
///<reference path="declarations.d.ts" />
import {foo, baz} from "foobarbaz";
>foo : Symbol(foo, Decl(user.ts, 1, 8))
>baz : Symbol(baz, Decl(user.ts, 1, 12))
foo(baz);
>foo : Symbol(foo, Decl(user.ts, 1, 8))
>baz : Symbol(baz, Decl(user.ts, 1, 12))
import {foos} from "foosball";
>foos : Symbol(foos, Decl(user.ts, 4, 8))
foo(foos);
>foo : Symbol(foo, Decl(user.ts, 1, 8))
>foos : Symbol(foos, Decl(user.ts, 4, 8))
// Works with relative file name
import fileText from "./file!text";
>fileText : Symbol(fileText, Decl(user.ts, 8, 6))
foo(fileText);
>foo : Symbol(foo, Decl(user.ts, 1, 8))
>fileText : Symbol(fileText, Decl(user.ts, 8, 6))
=== tests/cases/conformance/ambient/declarations.d.ts ===
declare module "foo*baz" {
export function foo(s: string): void;
>foo : Symbol(foo, Decl(declarations.d.ts, 0, 26))
>s : Symbol(s, Decl(declarations.d.ts, 1, 24))
}
// Augmentations still work
declare module "foo*baz" {
export const baz: string;
>baz : Symbol(baz, Decl(declarations.d.ts, 5, 16))
}
// Longest prefix wins
declare module "foos*" {
export const foos: string;
>foos : Symbol(foos, Decl(declarations.d.ts, 10, 16))
}
declare module "*!text" {
const x: string;
>x : Symbol(x, Decl(declarations.d.ts, 14, 9))
export default x;
>x : Symbol(x, Decl(declarations.d.ts, 14, 9))
}
@@ -0,0 +1,54 @@
=== tests/cases/conformance/ambient/user.ts ===
///<reference path="declarations.d.ts" />
import {foo, baz} from "foobarbaz";
>foo : (s: string) => void
>baz : string
foo(baz);
>foo(baz) : void
>foo : (s: string) => void
>baz : string
import {foos} from "foosball";
>foos : string
foo(foos);
>foo(foos) : void
>foo : (s: string) => void
>foos : string
// Works with relative file name
import fileText from "./file!text";
>fileText : string
foo(fileText);
>foo(fileText) : void
>foo : (s: string) => void
>fileText : string
=== tests/cases/conformance/ambient/declarations.d.ts ===
declare module "foo*baz" {
export function foo(s: string): void;
>foo : (s: string) => void
>s : string
}
// Augmentations still work
declare module "foo*baz" {
export const baz: string;
>baz : string
}
// Longest prefix wins
declare module "foos*" {
export const foos: string;
>foos : string
}
declare module "*!text" {
const x: string;
>x : string
export default x;
>x : string
}
@@ -0,0 +1,8 @@
tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts(1,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character
==== tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts (1 errors) ====
declare module "too*many*asterisks" { }
~~~~~~~~~~~~~~~~~~~~
!!! error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character
@@ -0,0 +1,5 @@
//// [ambientDeclarationsPatterns_tooManyAsterisks.ts]
declare module "too*many*asterisks" { }
//// [ambientDeclarationsPatterns_tooManyAsterisks.js]
@@ -0,0 +1,22 @@
//// [tests/cases/conformance/ambient/ambientShorthand.ts] ////
//// [declarations.d.ts]
declare module "jquery"
// Semicolon is optional
declare module "fs";
//// [user.ts]
///<reference path="declarations.d.ts"/>
import foo, {bar} from "jquery";
import * as baz from "fs";
import boom = require("jquery");
foo(bar, baz, boom);
//// [user.js]
"use strict";
///<reference path="declarations.d.ts"/>
var jquery_1 = require("jquery");
var baz = require("fs");
var boom = require("jquery");
jquery_1["default"](jquery_1.bar, baz, boom);
@@ -0,0 +1,24 @@
=== tests/cases/conformance/ambient/user.ts ===
///<reference path="declarations.d.ts"/>
import foo, {bar} from "jquery";
>foo : Symbol(foo, Decl(user.ts, 1, 6))
>bar : Symbol(bar, Decl(user.ts, 1, 13))
import * as baz from "fs";
>baz : Symbol(baz, Decl(user.ts, 2, 6))
import boom = require("jquery");
>boom : Symbol(boom, Decl(user.ts, 2, 26))
foo(bar, baz, boom);
>foo : Symbol(foo, Decl(user.ts, 1, 6))
>bar : Symbol(bar, Decl(user.ts, 1, 13))
>baz : Symbol(baz, Decl(user.ts, 2, 6))
>boom : Symbol(boom, Decl(user.ts, 2, 26))
=== tests/cases/conformance/ambient/declarations.d.ts ===
declare module "jquery"
No type information for this code.// Semicolon is optional
No type information for this code.declare module "fs";
No type information for this code.
No type information for this code.
@@ -0,0 +1,25 @@
=== tests/cases/conformance/ambient/user.ts ===
///<reference path="declarations.d.ts"/>
import foo, {bar} from "jquery";
>foo : any
>bar : any
import * as baz from "fs";
>baz : any
import boom = require("jquery");
>boom : any
foo(bar, baz, boom);
>foo(bar, baz, boom) : any
>foo : any
>bar : any
>baz : any
>boom : any
=== tests/cases/conformance/ambient/declarations.d.ts ===
declare module "jquery"
No type information for this code.// Semicolon is optional
No type information for this code.declare module "fs";
No type information for this code.
No type information for this code.
@@ -0,0 +1,9 @@
//// [ambientShorthand_declarationEmit.ts]
declare module "foo";
//// [ambientShorthand_declarationEmit.js]
//// [ambientShorthand_declarationEmit.d.ts]
declare module "foo";
@@ -0,0 +1,4 @@
=== tests/cases/conformance/ambient/ambientShorthand_declarationEmit.ts ===
declare module "foo";
No type information for this code.
No type information for this code.

Some files were not shown because too many files have changed in this diff Show More