Merge branch 'master' of https://github.com/Microsoft/TypeScript into feature/eslint

This commit is contained in:
Alexander T
2019-08-21 10:02:57 +03:00
23 changed files with 557 additions and 101 deletions
+5 -1
View File
@@ -10891,7 +10891,11 @@ namespace ts {
}
function getAliasSymbolForTypeNode(node: TypeNode) {
return isTypeAlias(node.parent) ? getSymbolOfNode(node.parent) : undefined;
let host = node.parent;
while (isParenthesizedTypeNode(host)) {
host = host.parent;
}
return isTypeAlias(host) ? getSymbolOfNode(host) : undefined;
}
function getTypeArgumentsForAliasSymbol(symbol: Symbol | undefined) {
+4
View File
@@ -5116,6 +5116,10 @@
"category": "Message",
"code": 95088
},
"Add 'await' to initializers": {
"category": "Message",
"code": 95089
},
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
"category": "Error",
+18 -4
View File
@@ -90,14 +90,28 @@ namespace ts {
return false;
}
const nextDirectorySeparator = dirPath.indexOf(directorySeparator, rootLength);
let nextDirectorySeparator = dirPath.indexOf(directorySeparator, rootLength);
if (nextDirectorySeparator === -1) {
// ignore "/user", "c:/users" or "c:/folderAtRoot"
return false;
}
if (dirPath.charCodeAt(0) !== CharacterCodes.slash &&
dirPath.substr(rootLength, nextDirectorySeparator).search(/users/i) === -1) {
let pathPartForUserCheck = dirPath.substring(rootLength, nextDirectorySeparator + 1);
const isNonDirectorySeparatorRoot = rootLength > 1 || dirPath.charCodeAt(0) !== CharacterCodes.slash;
if (isNonDirectorySeparatorRoot &&
dirPath.search(/[a-zA-Z]:/) !== 0 && // Non dos style paths
pathPartForUserCheck.search(/[a-zA-z]\$\//) === 0) { // Dos style nextPart
nextDirectorySeparator = dirPath.indexOf(directorySeparator, nextDirectorySeparator + 1);
if (nextDirectorySeparator === -1) {
// ignore "//vda1cs4850/c$/folderAtRoot"
return false;
}
pathPartForUserCheck = dirPath.substring(rootLength + pathPartForUserCheck.length, nextDirectorySeparator + 1);
}
if (isNonDirectorySeparatorRoot &&
pathPartForUserCheck.search(/users\//i) !== 0) {
// Paths like c:/folderAtRoot/subFolder are allowed
return true;
}
@@ -105,7 +119,7 @@ namespace ts {
for (let searchIndex = nextDirectorySeparator + 1, searchLevels = 2; searchLevels > 0; searchLevels--) {
searchIndex = dirPath.indexOf(directorySeparator, searchIndex) + 1;
if (searchIndex === 0) {
// Folder isnt at expected minimun levels
// Folder isnt at expected minimum levels
return false;
}
}
+1 -1
View File
@@ -2183,7 +2183,7 @@ namespace ts {
if (isIdentifierStart(ch, languageVersion)) {
let char = ch;
while (pos < end && isIdentifierPart(char = codePointAt(text, pos), languageVersion)) pos += charSize(char);
while (pos < end && isIdentifierPart(char = codePointAt(text, pos), languageVersion) || text.charCodeAt(pos) === CharacterCodes.minus) pos += charSize(char);
tokenValue = text.substring(tokenPos, pos);
if (char === CharacterCodes.backslash) {
tokenValue += scanIdentifierParts();
+34 -31
View File
@@ -35,38 +35,16 @@ interface Array<T> { length: number; [n: number]: T; }`
executingFilePath?: string;
currentDirectory?: string;
newLine?: string;
useWindowsStylePaths?: boolean;
windowsStyleRoot?: string;
environmentVariables?: Map<string>;
}
export function createWatchedSystem(fileOrFolderList: readonly FileOrFolderOrSymLink[], params?: TestServerHostCreationParameters): TestServerHost {
if (!params) {
params = {};
}
const host = new TestServerHost(/*withSafelist*/ false,
params.useCaseSensitiveFileNames !== undefined ? params.useCaseSensitiveFileNames : false,
params.executingFilePath || getExecutingFilePathFromLibFile(),
params.currentDirectory || "/",
fileOrFolderList,
params.newLine,
params.useWindowsStylePaths,
params.environmentVariables);
return host;
return new TestServerHost(/*withSafelist*/ false, fileOrFolderList, params);
}
export function createServerHost(fileOrFolderList: readonly FileOrFolderOrSymLink[], params?: TestServerHostCreationParameters): TestServerHost {
if (!params) {
params = {};
}
const host = new TestServerHost(/*withSafelist*/ true,
params.useCaseSensitiveFileNames !== undefined ? params.useCaseSensitiveFileNames : false,
params.executingFilePath || getExecutingFilePathFromLibFile(),
params.currentDirectory || "/",
fileOrFolderList,
params.newLine,
params.useWindowsStylePaths,
params.environmentVariables);
return host;
return new TestServerHost(/*withSafelist*/ true, fileOrFolderList, params);
}
export interface File {
@@ -326,6 +304,16 @@ interface Array<T> { length: number; [n: number]: T; }`
}
const timeIncrements = 1000;
export interface TestServerHostOptions {
useCaseSensitiveFileNames: boolean;
executingFilePath: string;
currentDirectory: string;
fileOrFolderorSymLinkList: readonly FileOrFolderOrSymLink[];
newLine?: string;
useWindowsStylePaths?: boolean;
environmentVariables?: Map<string>;
}
export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost, ModuleResolutionHost {
args: string[] = [];
@@ -342,16 +330,31 @@ interface Array<T> { length: number; [n: number]: T; }`
readonly watchedDirectories = createMultiMap<TestDirectoryWatcher>();
readonly watchedDirectoriesRecursive = createMultiMap<TestDirectoryWatcher>();
readonly watchedFiles = createMultiMap<TestFileWatcher>();
public readonly useCaseSensitiveFileNames: boolean;
public readonly newLine: string;
public readonly windowsStyleRoot?: string;
private readonly environmentVariables?: Map<string>;
private readonly executingFilePath: string;
private readonly currentDirectory: string;
private readonly customWatchFile: HostWatchFile | undefined;
private readonly customRecursiveWatchDirectory: HostWatchDirectory | undefined;
public require: ((initialPath: string, moduleName: string) => server.RequireResult) | undefined;
constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, executingFilePath: string, currentDirectory: string, fileOrFolderorSymLinkList: readonly FileOrFolderOrSymLink[], public readonly newLine = "\n", public readonly useWindowsStylePath?: boolean, private readonly environmentVariables?: Map<string>) {
this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
constructor(
public withSafeList: boolean,
fileOrFolderorSymLinkList: readonly FileOrFolderOrSymLink[],
{
useCaseSensitiveFileNames, executingFilePath, currentDirectory,
newLine, windowsStyleRoot, environmentVariables
}: TestServerHostCreationParameters = {}) {
this.useCaseSensitiveFileNames = !!useCaseSensitiveFileNames;
this.newLine = newLine || "\n";
this.windowsStyleRoot = windowsStyleRoot;
this.environmentVariables = environmentVariables;
currentDirectory = currentDirectory || "/";
this.getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName);
this.executingFilePath = this.getHostSpecificPath(executingFilePath);
this.executingFilePath = this.getHostSpecificPath(executingFilePath || getExecutingFilePathFromLibFile());
this.currentDirectory = this.getHostSpecificPath(currentDirectory);
this.reloadFS(fileOrFolderorSymLinkList);
const tscWatchFile = this.environmentVariables && this.environmentVariables.get("TSC_WATCHFILE") as Tsc_WatchFile;
@@ -418,8 +421,8 @@ interface Array<T> { length: number; [n: number]: T; }`
}
getHostSpecificPath(s: string) {
if (this.useWindowsStylePath && s.startsWith(directorySeparator)) {
return "c:/" + s.substring(1);
if (this.windowsStyleRoot && s.startsWith(directorySeparator)) {
return this.windowsStyleRoot + s.substring(1);
}
return s;
}
@@ -433,7 +436,7 @@ interface Array<T> { length: number; [n: number]: T; }`
const mapNewLeaves = createMap<true>();
const isNewFs = this.fs.size === 0;
fileOrFolderOrSymLinkList = fileOrFolderOrSymLinkList.concat(this.withSafeList ? safeList : []);
const filesOrFoldersToLoad: readonly FileOrFolderOrSymLink[] = !this.useWindowsStylePath ? fileOrFolderOrSymLinkList :
const filesOrFoldersToLoad: readonly FileOrFolderOrSymLink[] = !this.windowsStyleRoot ? fileOrFolderOrSymLinkList :
fileOrFolderOrSymLinkList.map<FileOrFolderOrSymLink>(f => {
const result = clone(f);
result.path = this.getHostSpecificPath(f.path);
+154 -45
View File
@@ -31,7 +31,7 @@ namespace ts.codefix {
errorCodes,
getCodeActions: context => {
const { sourceFile, errorCode, span, cancellationToken, program } = context;
const expression = getAwaitableExpression(sourceFile, errorCode, span, cancellationToken, program);
const expression = getFixableErrorSpanExpression(sourceFile, errorCode, span, cancellationToken, program);
if (!expression) {
return;
}
@@ -45,32 +45,40 @@ namespace ts.codefix {
getAllCodeActions: context => {
const { sourceFile, program, cancellationToken } = context;
const checker = context.program.getTypeChecker();
const fixedDeclarations = createMap<true>();
return codeFixAll(context, errorCodes, (t, diagnostic) => {
const expression = getAwaitableExpression(sourceFile, diagnostic.code, diagnostic, cancellationToken, program);
const expression = getFixableErrorSpanExpression(sourceFile, diagnostic.code, diagnostic, cancellationToken, program);
if (!expression) {
return;
}
const trackChanges: ContextualTrackChangesFunction = cb => (cb(t), []);
return getDeclarationSiteFix(context, expression, diagnostic.code, checker, trackChanges)
|| getUseSiteFix(context, expression, diagnostic.code, checker, trackChanges);
return getDeclarationSiteFix(context, expression, diagnostic.code, checker, trackChanges, fixedDeclarations)
|| getUseSiteFix(context, expression, diagnostic.code, checker, trackChanges, fixedDeclarations);
});
},
});
function getDeclarationSiteFix(context: CodeFixContext | CodeFixAllContext, expression: Expression, errorCode: number, checker: TypeChecker, trackChanges: ContextualTrackChangesFunction) {
const { sourceFile } = context;
const awaitableInitializer = findAwaitableInitializer(expression, sourceFile, checker);
if (awaitableInitializer) {
const initializerChanges = trackChanges(t => makeChange(t, errorCode, sourceFile, checker, awaitableInitializer));
function getDeclarationSiteFix(context: CodeFixContext | CodeFixAllContext, expression: Expression, errorCode: number, checker: TypeChecker, trackChanges: ContextualTrackChangesFunction, fixedDeclarations?: Map<true>) {
const { sourceFile, program, cancellationToken } = context;
const awaitableInitializers = findAwaitableInitializers(expression, sourceFile, cancellationToken, program, checker);
if (awaitableInitializers) {
const initializerChanges = trackChanges(t => {
forEach(awaitableInitializers.initializers, ({ expression }) => makeChange(t, errorCode, sourceFile, checker, expression, fixedDeclarations));
if (fixedDeclarations && awaitableInitializers.needsSecondPassForFixAll) {
makeChange(t, errorCode, sourceFile, checker, expression, fixedDeclarations);
}
});
return createCodeFixActionNoFixId(
"addMissingAwaitToInitializer",
initializerChanges,
[Diagnostics.Add_await_to_initializer_for_0, expression.getText(sourceFile)]);
awaitableInitializers.initializers.length === 1
? [Diagnostics.Add_await_to_initializer_for_0, awaitableInitializers.initializers[0].declarationSymbol.name]
: Diagnostics.Add_await_to_initializers);
}
}
function getUseSiteFix(context: CodeFixContext | CodeFixAllContext, expression: Expression, errorCode: number, checker: TypeChecker, trackChanges: ContextualTrackChangesFunction) {
const changes = trackChanges(t => makeChange(t, errorCode, context.sourceFile, checker, expression));
function getUseSiteFix(context: CodeFixContext | CodeFixAllContext, expression: Expression, errorCode: number, checker: TypeChecker, trackChanges: ContextualTrackChangesFunction, fixedDeclarations?: Map<true>) {
const changes = trackChanges(t => makeChange(t, errorCode, context.sourceFile, checker, expression, fixedDeclarations));
return createCodeFixAction(fixId, changes, Diagnostics.Add_await, fixId, Diagnostics.Fix_all_expressions_possibly_missing_await);
}
@@ -84,7 +92,7 @@ namespace ts.codefix {
some(relatedInformation, related => related.code === Diagnostics.Did_you_forget_to_use_await.code));
}
function getAwaitableExpression(sourceFile: SourceFile, errorCode: number, span: TextSpan, cancellationToken: CancellationToken, program: Program): Expression | undefined {
function getFixableErrorSpanExpression(sourceFile: SourceFile, errorCode: number, span: TextSpan, cancellationToken: CancellationToken, program: Program): Expression | undefined {
const token = getTokenAtPosition(sourceFile, span.start);
// Checker has already done work to determine that await might be possible, and has attached
// related info to the node, so start by finding the expression that exactly matches up
@@ -101,38 +109,117 @@ namespace ts.codefix {
&& isInsideAwaitableBody(expression) ? expression : undefined;
}
function findAwaitableInitializer(expression: Node, sourceFile: SourceFile, checker: TypeChecker): Expression | undefined {
if (!isIdentifier(expression)) {
interface AwaitableInitializer {
expression: Expression;
declarationSymbol: Symbol;
}
interface AwaitableInitializers {
initializers: readonly AwaitableInitializer[];
needsSecondPassForFixAll: boolean;
}
function findAwaitableInitializers(
expression: Node,
sourceFile: SourceFile,
cancellationToken: CancellationToken,
program: Program,
checker: TypeChecker,
): AwaitableInitializers | undefined {
const identifiers = getIdentifiersFromErrorSpanExpression(expression, checker);
if (!identifiers) {
return;
}
const symbol = checker.getSymbolAtLocation(expression);
if (!symbol) {
return;
let isCompleteFix = identifiers.isCompleteFix;
let initializers: AwaitableInitializer[] | undefined;
for (const identifier of identifiers.identifiers) {
const symbol = checker.getSymbolAtLocation(identifier);
if (!symbol) {
continue;
}
const declaration = tryCast(symbol.valueDeclaration, isVariableDeclaration);
const variableName = declaration && tryCast(declaration.name, isIdentifier);
const variableStatement = getAncestor(declaration, SyntaxKind.VariableStatement);
if (!declaration || !variableStatement ||
declaration.type ||
!declaration.initializer ||
variableStatement.getSourceFile() !== sourceFile ||
hasModifier(variableStatement, ModifierFlags.Export) ||
!variableName ||
!isInsideAwaitableBody(declaration.initializer)) {
isCompleteFix = false;
continue;
}
const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken);
const isUsedElsewhere = FindAllReferences.Core.eachSymbolReferenceInFile(variableName, checker, sourceFile, reference => {
return identifier !== reference && !symbolReferenceIsAlsoMissingAwait(reference, diagnostics, sourceFile, checker);
});
if (isUsedElsewhere) {
isCompleteFix = false;
continue;
}
(initializers || (initializers = [])).push({
expression: declaration.initializer,
declarationSymbol: symbol,
});
}
return initializers && {
initializers,
needsSecondPassForFixAll: !isCompleteFix,
};
}
const declaration = tryCast(symbol.valueDeclaration, isVariableDeclaration);
const variableName = tryCast(declaration && declaration.name, isIdentifier);
const variableStatement = getAncestor(declaration, SyntaxKind.VariableStatement);
if (!declaration || !variableStatement ||
declaration.type ||
!declaration.initializer ||
variableStatement.getSourceFile() !== sourceFile ||
hasModifier(variableStatement, ModifierFlags.Export) ||
!variableName ||
!isInsideAwaitableBody(declaration.initializer)) {
return;
interface Identifiers {
identifiers: readonly Identifier[];
isCompleteFix: boolean;
}
function getIdentifiersFromErrorSpanExpression(expression: Node, checker: TypeChecker): Identifiers | undefined {
if (isPropertyAccessExpression(expression.parent) && isIdentifier(expression.parent.expression)) {
return { identifiers: [expression.parent.expression], isCompleteFix: true };
}
const isUsedElsewhere = FindAllReferences.Core.eachSymbolReferenceInFile(variableName, checker, sourceFile, identifier => {
return identifier !== expression;
});
if (isUsedElsewhere) {
return;
if (isIdentifier(expression)) {
return { identifiers: [expression], isCompleteFix: true };
}
if (isBinaryExpression(expression)) {
let sides: Identifier[] | undefined;
let isCompleteFix = true;
for (const side of [expression.left, expression.right]) {
const type = checker.getTypeAtLocation(side);
if (checker.getPromisedTypeOfPromise(type)) {
if (!isIdentifier(side)) {
isCompleteFix = false;
continue;
}
(sides || (sides = [])).push(side);
}
}
return sides && { identifiers: sides, isCompleteFix };
}
}
return declaration.initializer;
function symbolReferenceIsAlsoMissingAwait(reference: Identifier, diagnostics: readonly Diagnostic[], sourceFile: SourceFile, checker: TypeChecker) {
const errorNode = isPropertyAccessExpression(reference.parent) ? reference.parent.name :
isBinaryExpression(reference.parent) ? reference.parent :
reference;
const diagnostic = find(diagnostics, diagnostic =>
diagnostic.start === errorNode.getStart(sourceFile) &&
diagnostic.start + diagnostic.length! === errorNode.getEnd());
return diagnostic && contains(errorCodes, diagnostic.code) ||
// A Promise is usually not correct in a binary expression (it’s not valid
// in an arithmetic expression and an equality comparison seems unusual),
// but if the other side of the binary expression has an error, the side
// is typed `any` which will squash the error that would identify this
// Promise as an invalid operand. So if the whole binary expression is
// typed `any` as a result, there is a strong likelihood that this Promise
// is accidentally missing `await`.
checker.getTypeAtLocation(errorNode).flags & TypeFlags.Any;
}
function isInsideAwaitableBody(node: Node) {
@@ -145,26 +232,48 @@ namespace ts.codefix {
ancestor.parent.kind === SyntaxKind.MethodDeclaration));
}
function makeChange(changeTracker: textChanges.ChangeTracker, errorCode: number, sourceFile: SourceFile, checker: TypeChecker, insertionSite: Expression) {
function makeChange(changeTracker: textChanges.ChangeTracker, errorCode: number, sourceFile: SourceFile, checker: TypeChecker, insertionSite: Expression, fixedDeclarations?: Map<true>) {
if (isBinaryExpression(insertionSite)) {
const { left, right } = insertionSite;
const leftType = checker.getTypeAtLocation(left);
const rightType = checker.getTypeAtLocation(right);
const newLeft = checker.getPromisedTypeOfPromise(leftType) ? createAwait(left) : left;
const newRight = checker.getPromisedTypeOfPromise(rightType) ? createAwait(right) : right;
changeTracker.replaceNode(sourceFile, left, newLeft);
changeTracker.replaceNode(sourceFile, right, newRight);
for (const side of [insertionSite.left, insertionSite.right]) {
if (fixedDeclarations && isIdentifier(side)) {
const symbol = checker.getSymbolAtLocation(side);
if (symbol && fixedDeclarations.has(getSymbolId(symbol).toString())) {
continue;
}
}
const type = checker.getTypeAtLocation(side);
const newNode = checker.getPromisedTypeOfPromise(type) ? createAwait(side) : side;
changeTracker.replaceNode(sourceFile, side, newNode);
}
}
else if (errorCode === propertyAccessCode && isPropertyAccessExpression(insertionSite.parent)) {
if (fixedDeclarations && isIdentifier(insertionSite.parent.expression)) {
const symbol = checker.getSymbolAtLocation(insertionSite.parent.expression);
if (symbol && fixedDeclarations.has(getSymbolId(symbol).toString())) {
return;
}
}
changeTracker.replaceNode(
sourceFile,
insertionSite.parent.expression,
createParen(createAwait(insertionSite.parent.expression)));
}
else if (contains(callableConstructableErrorCodes, errorCode) && isCallOrNewExpression(insertionSite.parent)) {
if (fixedDeclarations && isIdentifier(insertionSite)) {
const symbol = checker.getSymbolAtLocation(insertionSite);
if (symbol && fixedDeclarations.has(getSymbolId(symbol).toString())) {
return;
}
}
changeTracker.replaceNode(sourceFile, insertionSite, createParen(createAwait(insertionSite)));
}
else {
if (fixedDeclarations && isVariableDeclaration(insertionSite.parent) && isIdentifier(insertionSite.parent.name)) {
const symbol = checker.getSymbolAtLocation(insertionSite.parent.name);
if (symbol && !addToSeen(fixedDeclarations, getSymbolId(symbol))) {
return;
}
}
changeTracker.replaceNode(sourceFile, insertionSite, createAwait(insertionSite));
}
}
+1
View File
@@ -626,6 +626,7 @@ namespace Harness.Parallel.Host {
const perfData = readSavedPerfData(configOption);
context.describe = addSuite as Mocha.SuiteFunction;
context.it = addSuite as Mocha.TestFunction;
function addSuite(title: string) {
// Note, sub-suites are not indexed (we assume such granularity is not required)
+28 -8
View File
@@ -151,24 +151,42 @@ namespace Harness.Parallel.Worker {
unitTestSuiteMap.set(suite.title, suite);
}
}
if (!unitTestTestMap && unitTestSuite.tests.length) {
unitTestTestMap = ts.createMap<Mocha.Test>();
for (const test of unitTestSuite.tests) {
unitTestTestMap.set(test.title, test);
}
}
if (!unitTestSuiteMap) {
if (!unitTestSuiteMap && !unitTestTestMap) {
throw new Error(`Asked to run unit test ${task.file}, but no unit tests were discovered!`);
}
const suite = unitTestSuiteMap.get(task.file);
if (!suite) {
let suite = unitTestSuiteMap.get(task.file);
const test = unitTestTestMap.get(task.file);
if (!suite && !test) {
throw new Error(`Unit test with name "${task.file}" was asked to be run, but such a test does not exist!`);
}
const root = new Suite("", new Mocha.Context());
root.timeout(globalTimeout || 40_000);
root.addSuite(suite);
Object.setPrototypeOf(suite.ctx, root.ctx);
if (suite) {
root.addSuite(suite);
Object.setPrototypeOf(suite.ctx, root.ctx);
}
else if (test) {
const newSuite = new Suite("", new Mocha.Context());
newSuite.addTest(test);
root.addSuite(newSuite);
Object.setPrototypeOf(newSuite.ctx, root.ctx);
Object.setPrototypeOf(test.ctx, root.ctx);
test.parent = newSuite;
suite = newSuite;
}
runSuite(task, suite, payload => {
suite.parent = unitTestSuite;
Object.setPrototypeOf(suite.ctx, unitTestSuite.ctx);
runSuite(task, suite!, payload => {
suite!.parent = unitTestSuite;
Object.setPrototypeOf(suite!.ctx, unitTestSuite.ctx);
fn(payload);
});
}
@@ -284,6 +302,8 @@ namespace Harness.Parallel.Worker {
// The root suite for all unit tests.
let unitTestSuite: Suite;
let unitTestSuiteMap: ts.Map<Mocha.Suite>;
// (Unit) Tests directly within the root suite
let unitTestTestMap: ts.Map<Mocha.Test>;
if (runUnitTests) {
unitTestSuite = new Suite("", new Mocha.Context());
@@ -1064,7 +1064,7 @@ namespace ts.projectSystem {
content: "let x = 1;"
};
const host = createServerHost([file1, configFile], { useWindowsStylePaths: true });
const host = createServerHost([file1, configFile], { windowsStyleRoot: "c:/" });
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
@@ -99,7 +99,7 @@ namespace ts.projectSystem {
content: "let y = 10;"
};
const files = [configFile, file1, file2, libFile];
const host = createServerHost(files, { useWindowsStylePaths: true });
const host = createServerHost(files, { windowsStyleRoot: "c:/" });
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
const project = projectService.configuredProjects.get(configFile.path)!;
@@ -211,4 +211,39 @@ namespace ts.projectSystem {
}
});
describe("unittests:: tsserver:: watchEnvironment:: tsserverProjectSystem watching files with network style paths", () => {
function verifyFilePathStyle(path: string) {
const windowsStyleRoot = path.substr(0, getRootLength(path));
const host = createServerHost(
[libFile, { path, content: "const x = 10" }],
{ windowsStyleRoot }
);
const service = createProjectService(host);
service.openClientFile(path);
checkNumberOfProjects(service, { inferredProjects: 1 });
const libPath = `${windowsStyleRoot}${libFile.path.substring(1)}`;
checkProjectActualFiles(service.inferredProjects[0], [path, libPath]);
checkWatchedFiles(host, [libPath, `${getDirectoryPath(path)}/tsconfig.json`, `${getDirectoryPath(path)}/jsconfig.json`]);
}
it("for file of style c:/myprojects/project/x.js", () => {
verifyFilePathStyle("c:/myprojects/project/x.js");
});
it("for file of style //vda1cs4850/myprojects/project/x.js", () => {
verifyFilePathStyle("//vda1cs4850/myprojects/project/x.js");
});
it("for file of style //vda1cs4850/c$/myprojects/project/x.js", () => {
verifyFilePathStyle("//vda1cs4850/c$/myprojects/project/x.js");
});
it("for file of style c:/users/username/myprojects/project/x.js", () => {
verifyFilePathStyle("c:/users/username/myprojects/project/x.js");
});
it("for file of style //vda1cs4850/c$/users/username/myprojects/project/x.js", () => {
verifyFilePathStyle("//vda1cs4850/c$/users/username/myprojects/project/x.js");
});
});
}
@@ -18,7 +18,7 @@ type F1 = ([a, b, c]) => void;
>c : any
type T2 = ({ a });
>T2 : { a: any; }
>T2 : T2
>a : any
type F2 = ({ a }) => void;
@@ -46,7 +46,7 @@ type Result3 = Example3<'x' | 'y'>; // "x" | "y"
>Result3 : "x" | "y"
type Example4<T extends string, U extends string> = (Record<T, any> & Record<U, any>);
>Example4 : Record<T, any> & Record<U, any>
>Example4 : Example4<T, U>
type Result4 = keyof Example4<'x', 'y'>; // "x" | "y"
>Result4 : "x" | "y"
@@ -0,0 +1,49 @@
//// [parenthesisDoesNotBlockAliasSymbolCreation.ts]
export type InvalidKeys<K extends string|number|symbol> = { [P in K]? : never };
export type InvalidKeys2<K extends string|number|symbol> = (
{ [P in K]? : never }
);
export type A<T> = (
T & InvalidKeys<"a">
);
export type A2<T> = (
T & InvalidKeys2<"a">
);
export const a = null as A<{ x : number }>;
export const a2 = null as A2<{ x : number }>;
export const a3 = null as { x : number } & InvalidKeys<"a">;
export const a4 = null as { x : number } & InvalidKeys2<"a">;
//// [parenthesisDoesNotBlockAliasSymbolCreation.js]
"use strict";
exports.__esModule = true;
exports.a = null;
exports.a2 = null;
exports.a3 = null;
exports.a4 = null;
//// [parenthesisDoesNotBlockAliasSymbolCreation.d.ts]
export declare type InvalidKeys<K extends string | number | symbol> = {
[P in K]?: never;
};
export declare type InvalidKeys2<K extends string | number | symbol> = ({
[P in K]?: never;
});
export declare type A<T> = (T & InvalidKeys<"a">);
export declare type A2<T> = (T & InvalidKeys2<"a">);
export declare const a: A<{
x: number;
}>;
export declare const a2: A2<{
x: number;
}>;
export declare const a3: {
x: number;
} & InvalidKeys<"a">;
export declare const a4: {
x: number;
} & InvalidKeys2<"a">;
@@ -0,0 +1,56 @@
=== tests/cases/compiler/parenthesisDoesNotBlockAliasSymbolCreation.ts ===
export type InvalidKeys<K extends string|number|symbol> = { [P in K]? : never };
>InvalidKeys : Symbol(InvalidKeys, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 0, 0))
>K : Symbol(K, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 0, 24))
>P : Symbol(P, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 0, 61))
>K : Symbol(K, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 0, 24))
export type InvalidKeys2<K extends string|number|symbol> = (
>InvalidKeys2 : Symbol(InvalidKeys2, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 0, 80))
>K : Symbol(K, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 1, 25))
{ [P in K]? : never }
>P : Symbol(P, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 2, 7))
>K : Symbol(K, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 1, 25))
);
export type A<T> = (
>A : Symbol(A, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 3, 2))
>T : Symbol(T, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 5, 14))
T & InvalidKeys<"a">
>T : Symbol(T, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 5, 14))
>InvalidKeys : Symbol(InvalidKeys, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 0, 0))
);
export type A2<T> = (
>A2 : Symbol(A2, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 7, 2))
>T : Symbol(T, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 8, 15))
T & InvalidKeys2<"a">
>T : Symbol(T, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 8, 15))
>InvalidKeys2 : Symbol(InvalidKeys2, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 0, 80))
);
export const a = null as A<{ x : number }>;
>a : Symbol(a, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 12, 12))
>A : Symbol(A, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 3, 2))
>x : Symbol(x, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 12, 28))
export const a2 = null as A2<{ x : number }>;
>a2 : Symbol(a2, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 13, 12))
>A2 : Symbol(A2, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 7, 2))
>x : Symbol(x, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 13, 30))
export const a3 = null as { x : number } & InvalidKeys<"a">;
>a3 : Symbol(a3, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 14, 12))
>x : Symbol(x, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 14, 27))
>InvalidKeys : Symbol(InvalidKeys, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 0, 0))
export const a4 = null as { x : number } & InvalidKeys2<"a">;
>a4 : Symbol(a4, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 15, 12))
>x : Symbol(x, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 15, 27))
>InvalidKeys2 : Symbol(InvalidKeys2, Decl(parenthesisDoesNotBlockAliasSymbolCreation.ts, 0, 80))
@@ -0,0 +1,45 @@
=== tests/cases/compiler/parenthesisDoesNotBlockAliasSymbolCreation.ts ===
export type InvalidKeys<K extends string|number|symbol> = { [P in K]? : never };
>InvalidKeys : InvalidKeys<K>
export type InvalidKeys2<K extends string|number|symbol> = (
>InvalidKeys2 : InvalidKeys2<K>
{ [P in K]? : never }
);
export type A<T> = (
>A : A<T>
T & InvalidKeys<"a">
);
export type A2<T> = (
>A2 : A2<T>
T & InvalidKeys2<"a">
);
export const a = null as A<{ x : number }>;
>a : A<{ x: number; }>
>null as A<{ x : number }> : A<{ x: number; }>
>null : null
>x : number
export const a2 = null as A2<{ x : number }>;
>a2 : A2<{ x: number; }>
>null as A2<{ x : number }> : A2<{ x: number; }>
>null : null
>x : number
export const a3 = null as { x : number } & InvalidKeys<"a">;
>a3 : { x: number; } & InvalidKeys<"a">
>null as { x : number } & InvalidKeys<"a"> : { x: number; } & InvalidKeys<"a">
>null : null
>x : number
export const a4 = null as { x : number } & InvalidKeys2<"a">;
>a4 : { x: number; } & InvalidKeys2<"a">
>null as { x : number } & InvalidKeys2<"a"> : { x: number; } & InvalidKeys2<"a">
>null : null
>x : number
@@ -14,8 +14,8 @@ exports.__esModule = true;
"program": {
"fileInfos": {
"../../../.ts/lib.es5.d.ts": {
"version": "146944634190",
"signature": "146944634190"
"version": "406734842058",
"signature": "406734842058"
},
"../../../.ts/lib.es2015.d.ts": {
"version": "57263133672",
@@ -114,8 +114,8 @@ exports.__esModule = true;
"program": {
"fileInfos": {
"../../../.ts/lib.es5.d.ts": {
"version": "146944634190",
"signature": "146944634190"
"version": "406734842058",
"signature": "406734842058"
},
"../../../.ts/lib.es2015.d.ts": {
"version": "57263133672",
@@ -237,8 +237,8 @@ exports.getVar = getVar;
"program": {
"fileInfos": {
"../../../.ts/lib.es5.d.ts": {
"version": "146944634190",
"signature": "146944634190"
"version": "406734842058",
"signature": "406734842058"
},
"../../../.ts/lib.es2015.d.ts": {
"version": "57263133672",
@@ -0,0 +1,18 @@
// @declaration: true
export type InvalidKeys<K extends string|number|symbol> = { [P in K]? : never };
export type InvalidKeys2<K extends string|number|symbol> = (
{ [P in K]? : never }
);
export type A<T> = (
T & InvalidKeys<"a">
);
export type A2<T> = (
T & InvalidKeys2<"a">
);
export const a = null as A<{ x : number }>;
export const a2 = null as A2<{ x : number }>;
export const a3 = null as { x : number } & InvalidKeys<"a">;
export const a4 = null as { x : number } & InvalidKeys2<"a">;
@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
////async function fn(a: Promise<string>) {
//// const x = a;
//// x.toLowerCase();
////}
verify.codeFix({
description: "Add 'await' to initializer for 'x'",
index: 0,
newFileContent:
`async function fn(a: Promise<string>) {
const x = await a;
x.toLowerCase();
}`
});
@@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />
////async function fn(a: number, b: Promise<number>) {
//// const x = b;
//// const y = b;
//// fn(x, b);
//// fn(y, b);
//// x.toFixed();
//// y.then;
////
//// b + b;
//// x + b;
//// x + x.toFixed();
////}
verify.codeFixAll({
fixAllDescription: ts.Diagnostics.Fix_all_expressions_possibly_missing_await.message,
fixId: "addMissingAwait",
newFileContent:
`async function fn(a: number, b: Promise<number>) {
const x = await b;
const y = b;
fn(x, b);
fn(await y, b);
x.toFixed();
y.then;
await b + await b;
x + await b;
x + x.toFixed();
}`
});
@@ -0,0 +1,28 @@
/// <reference path="fourslash.ts" />
////async function fn(a: string, b: Promise<string>) {
//// const x = b;
//// const y = b;
//// x + y;
////}
verify.codeFix({
description: "Add 'await' to initializers",
index: 0,
newFileContent:
`async function fn(a: string, b: Promise<string>) {
const x = await b;
const y = await b;
x + y;
}`
});
verify.codeFixAll({
fixAllDescription: ts.Diagnostics.Fix_all_expressions_possibly_missing_await.message,
fixId: "addMissingAwait",
newFileContent:
`async function fn(a: string, b: Promise<string>) {
const x = await b;
const y = await b;
x + y;
}`
});
@@ -0,0 +1,24 @@
///<reference path="fourslash.ts" />
// @allowJs: true
// @Filename: dummy.js
//// /**
//// * @typedef Product
//// * @property {string} title
//// * @property {boolean} h/*1*/igh-top some-comments
//// */
////
//// /**
//// * @type {Pro/*2*/duct}
//// */
//// const product = {
//// /*3*/
//// }
verify.quickInfoAt('1', '(property) high-top: boolean', 'some-comments');
verify.quickInfoAt('2', 'type Product = {\n title: string;\n high-top: boolean;\n}');
verify.completions({
marker: ['3'],
includes: ['"high-top"']
});