diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f3c59a61717..31928f73ce2 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -18,6 +18,13 @@ "problemMatcher": [ "$tsc" ] + }, + { + "taskName": "tests", + "showOutput": "silent", + "problemMatcher": [ + "$tsc" + ] } ] } \ No newline at end of file diff --git a/Jakefile.js b/Jakefile.js index ba3e18f3e56..2dcba9f97e7 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -135,6 +135,7 @@ var harnessSources = harnessCoreSources.concat([ "projectErrors.ts", "matchFiles.ts", "initializeTSConfig.ts", + "extractMethods.ts", "printer.ts", "textChanges.ts", "telemetry.ts", @@ -535,7 +536,6 @@ var tscFile = path.join(builtLocalDirectory, compilerFilename); compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false); var servicesFile = path.join(builtLocalDirectory, "typescriptServices.js"); -var servicesFileInBrowserTest = path.join(builtLocalDirectory, "typescriptServicesInBrowserTest.js"); var standaloneDefinitionsFile = path.join(builtLocalDirectory, "typescriptServices.d.ts"); var nodePackageFile = path.join(builtLocalDirectory, "typescript.js"); var nodeDefinitionsFile = path.join(builtLocalDirectory, "typescript.d.ts"); @@ -574,22 +574,6 @@ compileFile(servicesFile, servicesSources, [builtLocalDirectory, copyright].conc 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, - inlineSourceMap: true - }); - file(typescriptServicesDts, [servicesFile]); var cancellationTokenFile = path.join(builtLocalDirectory, "cancellationToken.js"); @@ -727,7 +711,7 @@ compileFile( /*prereqs*/[builtLocalDirectory, tscFile].concat(libraryTargets).concat(servicesSources).concat(harnessSources), /*prefixes*/[], /*useBuiltCompiler:*/ true, - /*opts*/ { inlineSourceMap: true, types: ["node", "mocha", "chai"], lib: "es6" }); + /*opts*/ { types: ["node", "mocha", "chai"], lib: "es6" }); var internalTests = "internal/"; @@ -963,13 +947,14 @@ var nodeServerInFile = "tests/webTestServer.ts"; compileFile(nodeServerOutFile, [nodeServerInFile], [builtLocalDirectory, tscFile], [], /*useBuiltCompiler:*/ true, { noOutFile: true, lib: "es6" }); desc("Runs browserify on run.js to produce a file suitable for running tests in the browser"); -task("browserify", ["tests", run, builtLocalDirectory, nodeServerOutFile], function() { - var cmd = 'browserify built/local/run.js -t ./scripts/browserify-optional -d -o built/local/bundle.js'; +task("browserify", [], function() { + // Shell out to `gulp`, since we do the work to handle sourcemaps correctly w/o inline maps there + var cmd = 'gulp browserify --silent'; exec(cmd); }, { async: true }); desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is jake runtests-browser. Additional optional parameters tests=[regex], browser=[chrome|IE]"); -task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFileInBrowserTest], function () { +task("runtests-browser", ["browserify", nodeServerOutFile], function () { cleanTestDirs(); host = "node"; browser = process.env.browser || process.env.b || (os.platform() === "linux" ? "chrome" : "IE"); @@ -1124,14 +1109,15 @@ task("update-sublime", ["local", serverFile], function () { var tslintRuleDir = "scripts/tslint"; var tslintRules = [ - "nextLineRule", "booleanTriviaRule", - "typeOperatorSpacingRule", - "noInOperatorRule", + "debugAssertRule", + "nextLineRule", + "noBomRule", "noIncrementDecrementRule", - "objectLiteralSurroundingSpaceRule", + "noInOperatorRule", "noTypeAssertionWhitespaceRule", - "noBomRule" + "objectLiteralSurroundingSpaceRule", + "typeOperatorSpacingRule", ]; var tslintRulesFiles = tslintRules.map(function (p) { return path.join(tslintRuleDir, p + ".ts"); diff --git a/package.json b/package.json index a114ac68a2d..2c5f8f929a7 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,6 @@ "setup-hooks": "node scripts/link-hooks.js" }, "browser": { - "buffer": false, "fs": false, "os": false, "path": false diff --git a/scripts/tslint/booleanTriviaRule.ts b/scripts/tslint/booleanTriviaRule.ts index 189dafac77e..c498131be16 100644 --- a/scripts/tslint/booleanTriviaRule.ts +++ b/scripts/tslint/booleanTriviaRule.ts @@ -34,6 +34,7 @@ function walk(ctx: Lint.WalkContext): void { switch (methodName) { case "apply": case "assert": + case "assertEqual": case "call": case "equal": case "fail": @@ -69,7 +70,7 @@ function walk(ctx: Lint.WalkContext): void { const ranges = ts.getTrailingCommentRanges(sourceFile.text, arg.pos) || ts.getLeadingCommentRanges(sourceFile.text, arg.pos); if (ranges === undefined || ranges.length !== 1 || ranges[0].kind !== ts.SyntaxKind.MultiLineCommentTrivia) { - ctx.addFailureAtNode(arg, "Tag boolean argument with parameter name"); + ctx.addFailureAtNode(arg, "Tag argument with parameter name"); return; } diff --git a/scripts/tslint/debugAssertRule.ts b/scripts/tslint/debugAssertRule.ts new file mode 100644 index 00000000000..933b27697b0 --- /dev/null +++ b/scripts/tslint/debugAssertRule.ts @@ -0,0 +1,45 @@ +import * as Lint from "tslint/lib"; +import * as ts from "typescript"; + +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, ctx => walk(ctx)); + } +} + +function walk(ctx: Lint.WalkContext): void { + ts.forEachChild(ctx.sourceFile, function recur(node) { + if (ts.isCallExpression(node)) { + checkCall(node); + } + ts.forEachChild(node, recur); + }); + + function checkCall(node: ts.CallExpression) { + if (!isDebugAssert(node.expression) || node.arguments.length < 2) { + return; + } + + const message = node.arguments[1]; + if (!ts.isStringLiteral(message)) { + ctx.addFailureAtNode(message, "Second argument to 'Debug.assert' should be a string literal."); + } + + if (node.arguments.length < 3) { + return; + } + + const message2 = node.arguments[2]; + if (!ts.isStringLiteral(message2) && !ts.isArrowFunction(message2)) { + ctx.addFailureAtNode(message, "Third argument to 'Debug.assert' should be a string literal or arrow function."); + } + } + + function isDebugAssert(expr: ts.Node): boolean { + return ts.isPropertyAccessExpression(expr) && isName(expr.expression, "Debug") && isName(expr.name, "assert"); + } + + function isName(expr: ts.Node, text: string): boolean { + return ts.isIdentifier(expr) && expr.text === text; + } +} diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 2fbd0c3e141..a7e94da09d9 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -806,11 +806,7 @@ namespace ts { return antecedent; } setFlowNodeReferenced(antecedent); - return { - flags, - expression, - antecedent - }; + return { flags, expression, antecedent }; } function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode { @@ -818,31 +814,18 @@ namespace ts { return antecedent; } setFlowNodeReferenced(antecedent); - return { - flags: FlowFlags.SwitchClause, - switchStatement, - clauseStart, - clauseEnd, - antecedent - }; + return { flags: FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent }; } function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode { setFlowNodeReferenced(antecedent); - return { - flags: FlowFlags.Assignment, - antecedent, - node - }; + return { flags: FlowFlags.Assignment, antecedent, node }; } function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode { setFlowNodeReferenced(antecedent); - return { - flags: FlowFlags.ArrayMutation, - antecedent, - node - }; + const res: FlowArrayMutation = { flags: FlowFlags.ArrayMutation, antecedent, node }; + return res; } function finishFlowLabel(flow: FlowLabel): FlowNode { @@ -2784,7 +2767,6 @@ namespace ts { function computeParameter(node: ParameterDeclaration, subtreeFlags: TransformFlags) { let transformFlags = subtreeFlags; - const modifierFlags = getModifierFlags(node); const name = node.name; const initializer = node.initializer; const dotDotDotToken = node.dotDotDotToken; @@ -2799,7 +2781,7 @@ namespace ts { } // If a parameter has an accessibility modifier, then it is TypeScript syntax. - if (modifierFlags & ModifierFlags.ParameterPropertyModifier) { + if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) { transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsParameterPropertyAssignments; } @@ -2844,9 +2826,8 @@ namespace ts { function computeClassDeclaration(node: ClassDeclaration, subtreeFlags: TransformFlags) { let transformFlags: TransformFlags; - const modifierFlags = getModifierFlags(node); - if (modifierFlags & ModifierFlags.Ambient) { + if (hasModifier(node, ModifierFlags.Ambient)) { // An ambient declaration is TypeScript syntax. transformFlags = TransformFlags.AssertTypeScript; } @@ -2921,7 +2902,10 @@ namespace ts { function computeCatchClause(node: CatchClause, subtreeFlags: TransformFlags) { let transformFlags = subtreeFlags; - if (node.variableDeclaration && isBindingPattern(node.variableDeclaration.name)) { + if (!node.variableDeclaration) { + transformFlags |= TransformFlags.AssertESNext; + } + else if (isBindingPattern(node.variableDeclaration.name)) { transformFlags |= TransformFlags.AssertES2015; } @@ -3187,11 +3171,10 @@ namespace ts { function computeVariableStatement(node: VariableStatement, subtreeFlags: TransformFlags) { let transformFlags: TransformFlags; - const modifierFlags = getModifierFlags(node); const declarationListTransformFlags = node.declarationList.transformFlags; // An ambient declaration is TypeScript syntax. - if (modifierFlags & ModifierFlags.Ambient) { + if (hasModifier(node, ModifierFlags.Ambient)) { transformFlags = TransformFlags.AssertTypeScript; } else { diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 7d4b1ce37a2..19a18f7b9c1 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -97,14 +97,17 @@ namespace ts { } changedFilesSinceLastEmit = changedFilesSinceLastEmit || createMap(); - fileInfos = mutateExistingMapWithSameExistingValues( - fileInfos, arrayToMap(program.getSourceFiles(), sourceFile => sourceFile.path), - // Add new file info - (_path, sourceFile) => addNewFileInfo(program, sourceFile), - // Remove existing file info - removeExistingFileInfo, - // We will update in place instead of deleting existing value and adding new one - (existingInfo, sourceFile) => updateExistingFileInfo(program, existingInfo, sourceFile, hasInvalidatedResolution) + mutateMap( + fileInfos || (fileInfos = createMap()), + arrayToMap(program.getSourceFiles(), sourceFile => sourceFile.path), + { + // Add new file info + createNewValue: (_path, sourceFile) => addNewFileInfo(program, sourceFile), + // Remove existing file info + onDeleteExistingValue: removeExistingFileInfo, + // We will update in place instead of deleting existing value and adding new one + onExistingValue: (existingInfo, sourceFile) => updateExistingFileInfo(program, existingInfo, sourceFile, hasInvalidatedResolution) + } ); } @@ -370,27 +373,31 @@ namespace ts { const references = createMap>(); const referencedBy = createMultiMap(); return { - addScriptInfo: setReferences, + addScriptInfo: (program, sourceFile) => { + const refs = createMap(); + references.set(sourceFile.path, refs); + setReferences(program, sourceFile, refs); + }, removeScriptInfo, - updateScriptInfo: setReferences, + updateScriptInfo: (program, sourceFile) => setReferences(program, sourceFile, references.get(sourceFile.path)), getFilesAffectedByUpdatedShape }; - function setReferences(program: Program, sourceFile: SourceFile) { + function setReferences(program: Program, sourceFile: SourceFile, existingReferences: Map) { const path = sourceFile.path; - references.set(path, - mutateExistingMapWithNewSet( - // Existing references - references.get(path), - // Updated references - getReferencedFiles(program, sourceFile), + mutateMap( + // Existing references + existingReferences, + // Updated references + getReferencedFiles(program, sourceFile), + { // Creating new Reference: as sourceFile references file with path 'key' // in other words source file (path) is referenced by 'key' - key => { referencedBy.add(key, path); return true; }, + createNewValue: (key): true => { referencedBy.add(key, path); return true; }, // Remove existing reference by entry: source file doesnt reference file 'key' any more // in other words source file (path) is not referenced by 'key' - (key, _existingValue) => { referencedBy.remove(key, path); } - ) + onDeleteExistingValue: (key, _existingValue) => { referencedBy.remove(key, path); } + } ); } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a578b7208ae..d71955b791c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -223,11 +223,10 @@ namespace ts { getSuggestionForNonexistentProperty: (node, type) => unescapeLeadingUnderscores(getSuggestionForNonexistentProperty(node, type)), getSuggestionForNonexistentSymbol: (location, name, meaning) => unescapeLeadingUnderscores(getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning)), getBaseConstraintOfType, - getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()), - resolveNameAtLocation(location: Node, name: string, meaning: SymbolFlags): Symbol | undefined { - location = getParseTreeNode(location); - return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, escapeLeadingUnderscores(name)); + resolveName(name, location, meaning) { + return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); }, + getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()), }; const tupleTypes: GenericType[] = []; @@ -481,7 +480,7 @@ namespace ts { Type, ResolvedBaseConstructorType, DeclaredType, - ResolvedReturnType + ResolvedReturnType, } const enum CheckMode { @@ -835,13 +834,13 @@ namespace ts { (current.parent).initializer === current; if (initializerOfProperty) { - if (getModifierFlags(current.parent) & ModifierFlags.Static) { + if (hasModifier(current.parent, ModifierFlags.Static)) { if (declaration.kind === SyntaxKind.MethodDeclaration) { return true; } } else { - const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !(getModifierFlags(declaration) & ModifierFlags.Static); + const isDeclarationInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !hasModifier(declaration, ModifierFlags.Static); if (!isDeclarationInstanceProperty || getContainingClass(usage) !== getContainingClass(declaration)) { return true; } @@ -978,7 +977,7 @@ namespace ts { // local variables of the constructor. This effectively means that entities from outer scopes // by the same name as a constructor parameter or local variable are inaccessible // in initializer expressions for instance member variables. - if (isClassLike(location.parent) && !(getModifierFlags(location) & ModifierFlags.Static)) { + if (isClassLike(location.parent) && !hasModifier(location, ModifierFlags.Static)) { const ctor = findConstructorDeclaration(location.parent); if (ctor && ctor.locals) { if (lookup(ctor.locals, name, meaning & SymbolFlags.Value)) { @@ -997,7 +996,7 @@ namespace ts { result = undefined; break; } - if (lastLocation && getModifierFlags(lastLocation) & ModifierFlags.Static) { + if (lastLocation && hasModifier(lastLocation, ModifierFlags.Static)) { // TypeScript 1.0 spec (April 2014): 3.4.1 // The scope of a type parameter extends over the entire declaration with which the type // parameter list is associated, with the exception of static member declarations in classes. @@ -1086,7 +1085,10 @@ namespace ts { location = location.parent; } - if (result && nameNotFoundMessage && noUnusedIdentifiers) { + // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`. + // If `result === lastLocation.symbol`, that means that we are somewhere inside `lastLocation` looking up a name, and resolving to `lastLocation` itself. + // That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used. + if (result && nameNotFoundMessage && noUnusedIdentifiers && result !== lastLocation.symbol) { result.isReferenced = true; } @@ -1197,7 +1199,7 @@ namespace ts { // No static member is present. // Check if we're in an instance method and look for a relevant instance member. - if (location === container && !(getModifierFlags(location) & ModifierFlags.Static)) { + if (location === container && !hasModifier(location, ModifierFlags.Static)) { const instanceType = (getDeclaredTypeOfSymbol(classSymbol)).thisType; if (getPropertyOfType(instanceType, name)) { error(errorLocation, Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, diagnosticName(nameArg)); @@ -1754,7 +1756,7 @@ namespace ts { // An external module with an 'export =' declaration resolves to the target of the 'export =' declaration, // and an external module with no 'export =' declaration resolves to the module itself. function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol { - return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports.get("export=" as __String), dontResolveAlias)) || moduleSymbol; + return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports.get(InternalSymbolName.ExportEquals), dontResolveAlias)) || moduleSymbol; } // An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export =' @@ -1769,7 +1771,7 @@ namespace ts { } function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean { - return moduleSymbol.exports.get("export=" as __String) !== undefined; + return moduleSymbol.exports.get(InternalSymbolName.ExportEquals) !== undefined; } function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] { @@ -2169,7 +2171,7 @@ namespace ts { if (accessibleSymbolChain) { const hasAccessibleDeclarations = hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible); if (!hasAccessibleDeclarations) { - return { + return { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning), errorModuleName: symbol !== initialSymbol ? symbolToString(symbol, enclosingDeclaration, SymbolFlags.Namespace) : undefined, @@ -2242,7 +2244,7 @@ namespace ts { const anyImportSyntax = getAnyImportSyntax(declaration); if (anyImportSyntax && - !(getModifierFlags(anyImportSyntax) & ModifierFlags.Export) && // import clause without export + !hasModifier(anyImportSyntax, ModifierFlags.Export) && // import clause without export isDeclarationVisible(anyImportSyntax.parent)) { // In function "buildTypeDisplay" where we decide whether to write type-alias or serialize types, // we want to just check if type- alias is accessible or not but we don't care about emitting those alias at that time @@ -2291,7 +2293,7 @@ namespace ts { const symbol = resolveName(enclosingDeclaration, firstIdentifier.escapedText, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined); // Verify if the symbol is accessible - return (symbol && hasVisibleDeclarations(symbol, /*shouldComputeAliasToMakeVisible*/ true)) || { + return (symbol && hasVisibleDeclarations(symbol, /*shouldComputeAliasToMakeVisible*/ true)) || { accessibility: SymbolAccessibility.NotAccessible, errorSymbolName: getTextOfNode(firstIdentifier), errorNode: firstIdentifier @@ -2569,8 +2571,8 @@ namespace ts { } function shouldWriteTypeOfFunctionSymbol() { - const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method - forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static)); + const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method) && // typeof static method + some(symbol.declarations, declaration => hasModifier(declaration, ModifierFlags.Static)); const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) && (symbol.parent || // is exported function symbol forEach(symbol.declarations, declaration => @@ -3417,9 +3419,7 @@ namespace ts { if (!symbolStack) { symbolStack = []; } - const isConstructorObject = type.flags & TypeFlags.Object && - getObjectFlags(type) & ObjectFlags.Anonymous && - type.symbol && type.symbol.flags & SymbolFlags.Class; + const isConstructorObject = type.objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class; if (isConstructorObject) { writeLiteralType(type, flags); } @@ -3436,16 +3436,16 @@ namespace ts { } function shouldWriteTypeOfFunctionSymbol() { - const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method - forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static)); + const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method) && // typeof static method + some(symbol.declarations, declaration => hasModifier(declaration, ModifierFlags.Static)); const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) && (symbol.parent || // is exported function symbol - forEach(symbol.declarations, declaration => + some(symbol.declarations, declaration => declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock)); if (isStaticMethodSymbol || isNonLocalFunctionSymbol) { // typeof is allowed only for static/non local functions return !!(flags & TypeFormatFlags.UseTypeOfFunction) || // use typeof if format flags specify it - (contains(symbolStack, symbol)); // it is type of the symbol uses itself recursively + contains(symbolStack, symbol); // it is type of the symbol uses itself recursively } } } @@ -3891,7 +3891,7 @@ namespace ts { case SyntaxKind.SetAccessor: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: - if (getModifierFlags(node) & (ModifierFlags.Private | ModifierFlags.Protected)) { + if (hasModifier(node, ModifierFlags.Private | ModifierFlags.Protected)) { // Private/protected properties/methods are not visible return false; } @@ -5095,8 +5095,8 @@ namespace ts { return unknownType; } - const declaration = findDeclaration( - symbol, d => d.kind === SyntaxKind.JSDocTypedefTag || d.kind === SyntaxKind.TypeAliasDeclaration); + const declaration = find(symbol.declarations, d => + d.kind === SyntaxKind.JSDocTypedefTag || d.kind === SyntaxKind.TypeAliasDeclaration); let type = getTypeFromTypeNode(declaration.kind === SyntaxKind.JSDocTypedefTag ? declaration.typeExpression : declaration.type); if (popTypeResolution()) { @@ -6299,7 +6299,7 @@ namespace ts { return { kind: TypePredicateKind.This, type: getTypeFromTypeNode(node.type) - } as ThisTypePredicate; + }; } } @@ -6656,7 +6656,7 @@ namespace ts { const declaration = getIndexDeclarationOfSymbol(symbol, kind); if (declaration) { return createIndexInfo(declaration.type ? getTypeFromTypeNode(declaration.type) : anyType, - (getModifierFlags(declaration) & ModifierFlags.Readonly) !== 0, declaration); + hasModifier(declaration, ModifierFlags.Readonly), declaration); } return undefined; } @@ -7555,11 +7555,11 @@ namespace ts { return getTypeOfSymbol(prop); } } - if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) { + if (!(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) { if (isTypeAny(objectType)) { return anyType; } - const indexInfo = isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) || + const indexInfo = isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) || getIndexInfoOfType(objectType, IndexKind.String) || undefined; if (indexInfo) { @@ -7908,7 +7908,7 @@ namespace ts { const container = getThisContainer(node, /*includeArrowFunctions*/ false); const parent = container && container.parent; if (parent && (isClassLike(parent) || parent.kind === SyntaxKind.InterfaceDeclaration)) { - if (!(getModifierFlags(container) & ModifierFlags.Static) && + if (!hasModifier(container, ModifierFlags.Static) && (container.kind !== SyntaxKind.Constructor || isNodeDescendantOf(node, (container).body))) { return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent)).thisType; } @@ -8074,7 +8074,7 @@ namespace ts { function cloneTypeMapper(mapper: TypeMapper): TypeMapper { return mapper && isInferenceContext(mapper) ? - createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.inferences) : + createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.compareTypes, mapper.inferences) : mapper; } @@ -8108,13 +8108,13 @@ namespace ts { parameterName: predicate.parameterName, parameterIndex: predicate.parameterIndex, type: instantiateType(predicate.type, mapper) - } as IdentifierTypePredicate; + }; } else { return { kind: TypePredicateKind.This, type: instantiateType(predicate.type, mapper) - } as ThisTypePredicate; + }; } } @@ -8404,16 +8404,17 @@ namespace ts { if (forEach(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) { return true; } - // For arrow functions we now know we're not context sensitive. - if (node.kind === SyntaxKind.ArrowFunction) { - return false; + if (node.kind !== SyntaxKind.ArrowFunction) { + // If the first parameter is not an explicit 'this' parameter, then the function has + // an implicit 'this' parameter which is subject to contextual typing. + const parameter = firstOrUndefined(node.parameters); + if (!(parameter && parameterIsThisKeyword(parameter))) { + return true; + } } - // If the first parameter is not an explicit 'this' parameter, then the function has - // an implicit 'this' parameter which is subject to contextual typing. Otherwise we - // know that all parameters (including 'this') have type annotations and nothing is - // subject to contextual typing. - const parameter = firstOrUndefined(node.parameters); - return !(parameter && parameterIsThisKeyword(parameter)); + + // TODO(anhans): A block should be context-sensitive if it has a context-sensitive return value. + return node.body.kind === SyntaxKind.Block ? false : isContextSensitive(node.body); } function isContextSensitiveFunctionOrObjectLiteralMethod(func: Node): func is FunctionExpression | ArrowFunction | MethodDeclaration { @@ -8515,7 +8516,7 @@ namespace ts { ignoreReturnTypes: boolean, reportErrors: boolean, errorReporter: ErrorReporter, - compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary { + compareTypes: TypeComparer): Ternary { // TODO (drosen): De-duplicate code between related functions. if (source === target) { return Ternary.True; @@ -8525,7 +8526,7 @@ namespace ts { } if (source.typeParameters) { - source = instantiateSignatureInContextOf(source, target); + source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes); } let result = Ternary.True; @@ -8591,7 +8592,7 @@ namespace ts { // The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions if (target.typePredicate) { if (source.typePredicate) { - result &= compareTypePredicateRelatedTo(source.typePredicate, target.typePredicate, reportErrors, errorReporter, compareTypes); + result &= compareTypePredicateRelatedTo(source.typePredicate, target.typePredicate, source.declaration, target.declaration, reportErrors, errorReporter, compareTypes); } else if (isIdentifierTypePredicate(target.typePredicate)) { if (reportErrors) { @@ -8613,8 +8614,11 @@ namespace ts { return result; } - function compareTypePredicateRelatedTo(source: TypePredicate, + function compareTypePredicateRelatedTo( + source: TypePredicate, target: TypePredicate, + sourceDeclaration: SignatureDeclaration, + targetDeclaration: SignatureDeclaration, reportErrors: boolean, errorReporter: ErrorReporter, compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary { @@ -8627,11 +8631,13 @@ namespace ts { } if (source.kind === TypePredicateKind.Identifier) { - const sourceIdentifierPredicate = source as IdentifierTypePredicate; - const targetIdentifierPredicate = target as IdentifierTypePredicate; - if (sourceIdentifierPredicate.parameterIndex !== targetIdentifierPredicate.parameterIndex) { + const sourcePredicate = source as IdentifierTypePredicate; + const targetPredicate = target as IdentifierTypePredicate; + const sourceIndex = sourcePredicate.parameterIndex - (getThisParameter(sourceDeclaration) ? 1 : 0); + const targetIndex = targetPredicate.parameterIndex - (getThisParameter(targetDeclaration) ? 1 : 0); + if (sourceIndex !== targetIndex) { if (reportErrors) { - errorReporter(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, sourceIdentifierPredicate.parameterName, targetIdentifierPredicate.parameterName); + errorReporter(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, sourcePredicate.parameterName, targetPredicate.parameterName); errorReporter(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typePredicateToString(source), typePredicateToString(target)); } return Ternary.False; @@ -8938,11 +8944,21 @@ namespace ts { !(target.flags & TypeFlags.Union) && !isIntersectionConstituent && source !== globalObjectType && - getPropertiesOfType(source).length > 0 && + (getPropertiesOfType(source).length > 0 || + getSignaturesOfType(source, SignatureKind.Call).length > 0 || + getSignaturesOfType(source, SignatureKind.Construct).length > 0) && isWeakType(target) && !hasCommonProperties(source, target)) { if (reportErrors) { - reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, typeToString(source), typeToString(target)); + const calls = getSignaturesOfType(source, SignatureKind.Call); + const constructs = getSignaturesOfType(source, SignatureKind.Construct); + if (calls.length > 0 && isRelatedTo(getReturnTypeOfSignature(calls[0]), target, /*reportErrors*/ false) || + constructs.length > 0 && isRelatedTo(getReturnTypeOfSignature(constructs[0]), target, /*reportErrors*/ false)) { + reportError(Diagnostics.Value_of_type_0_has_no_properties_in_common_with_type_1_Did_you_mean_to_call_it, typeToString(source), typeToString(target)); + } + else { + reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, typeToString(source), typeToString(target)); + } } return Ternary.False; } @@ -9671,6 +9687,11 @@ namespace ts { if (sourceInfo) { return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors); } + if (isGenericMappedType(source)) { + // A generic mapped type { [P in K]: T } is related to an index signature { [x: string]: U } + // if T is related to U. + return kind === IndexKind.String && isRelatedTo(getTemplateTypeFromMappedType(source), targetInfo.type, reportErrors); + } if (isObjectLiteralType(source)) { let related = Ternary.True; if (kind === IndexKind.String) { @@ -9707,8 +9728,8 @@ namespace ts { return true; } - const sourceAccessibility = getModifierFlags(sourceSignature.declaration) & ModifierFlags.NonPublicAccessibilityModifier; - const targetAccessibility = getModifierFlags(targetSignature.declaration) & ModifierFlags.NonPublicAccessibilityModifier; + const sourceAccessibility = getSelectedModifierFlags(sourceSignature.declaration, ModifierFlags.NonPublicAccessibilityModifier); + const targetAccessibility = getSelectedModifierFlags(targetSignature.declaration, ModifierFlags.NonPublicAccessibilityModifier); // A public, protected and private signature is assignable to a private signature. if (targetAccessibility === ModifierFlags.Private) { @@ -9782,7 +9803,7 @@ namespace ts { const symbol = type.symbol; if (symbol && symbol.flags & SymbolFlags.Class) { const declaration = getClassLikeDeclarationOfSymbol(symbol); - if (declaration && getModifierFlags(declaration) & ModifierFlags.Abstract) { + if (declaration && hasModifier(declaration, ModifierFlags.Abstract)) { return true; } } @@ -10273,13 +10294,14 @@ namespace ts { } } - function createInferenceContext(signature: Signature, flags: InferenceFlags, baseInferences?: InferenceInfo[]): InferenceContext { + function createInferenceContext(signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext { const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(signature.typeParameters, createInferenceInfo); const context = mapper as InferenceContext; context.mappedTypes = signature.typeParameters; context.signature = signature; context.inferences = inferences; context.flags = flags; + context.compareTypes = compareTypes || compareTypesAssignable; return context; function mapper(t: Type): Type { @@ -10727,7 +10749,7 @@ namespace ts { const constraint = getConstraintOfTypeParameter(context.signature.typeParameters[index]); if (constraint) { const instantiatedConstraint = instantiateType(constraint, context); - if (!isTypeAssignableTo(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { + if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { inference.inferredType = inferredType = instantiatedConstraint; } } @@ -10794,17 +10816,6 @@ namespace ts { return undefined; } - function getLeftmostIdentifierOrThis(node: Node): Node { - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.ThisKeyword: - return node; - case SyntaxKind.PropertyAccessExpression: - return getLeftmostIdentifierOrThis((node).expression); - } - return undefined; - } - function getBindingElementNameText(element: BindingElement): string | undefined { if (element.parent.kind === SyntaxKind.ObjectBindingPattern) { const name = element.propertyName || element.name; @@ -11378,7 +11389,7 @@ namespace ts { (parent.parent).operatorToken.kind === SyntaxKind.EqualsToken && (parent.parent).left === parent && !isAssignmentTarget(parent.parent) && - isTypeAnyOrAllConstituentTypesHaveKind(getTypeOfExpression((parent).argumentExpression), TypeFlags.NumberLike | TypeFlags.Undefined); + isTypeAssignableToKind(getTypeOfExpression((parent).argumentExpression), TypeFlags.NumberLike); return isLengthPushOrUnshift || isElementAssignment; } @@ -11550,7 +11561,7 @@ namespace ts { } else { const indexType = getTypeOfExpression(((node).left).argumentExpression); - if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike | TypeFlags.Undefined)) { + if (isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) { evolvedType = addEvolvingArrayElementType(evolvedType, (node).right); } } @@ -12459,7 +12470,7 @@ namespace ts { break; case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: - if (getModifierFlags(container) & ModifierFlags.Static) { + if (hasModifier(container, ModifierFlags.Static)) { error(node, Diagnostics.this_cannot_be_referenced_in_a_static_property_initializer); // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks } @@ -12577,7 +12588,7 @@ namespace ts { checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class); } - if ((getModifierFlags(container) & ModifierFlags.Static) || isCallExpression) { + if (hasModifier(container, ModifierFlags.Static) || isCallExpression) { nodeCheckFlag = NodeCheckFlags.SuperStatic; } else { @@ -12642,7 +12653,7 @@ namespace ts { // This helper creates an object with a "value" property that wraps the `super` property or indexed access for both get and set. // This is required for destructuring assignments, as a call expression cannot be used as the target of a destructuring assignment // while a property access can. - if (container.kind === SyntaxKind.MethodDeclaration && getModifierFlags(container) & ModifierFlags.Async) { + if (container.kind === SyntaxKind.MethodDeclaration && hasModifier(container, ModifierFlags.Async)) { if (isSuperProperty(node.parent) && isAssignmentTarget(node.parent)) { getNodeLinks(container).flags |= NodeCheckFlags.AsyncMethodWithSuperBinding; } @@ -12708,7 +12719,7 @@ namespace ts { // topmost container must be something that is directly nested in the class declaration\object literal expression if (isClassLike(container.parent) || container.parent.kind === SyntaxKind.ObjectLiteralExpression) { - if (getModifierFlags(container) & ModifierFlags.Static) { + if (hasModifier(container, ModifierFlags.Static)) { return container.kind === SyntaxKind.MethodDeclaration || container.kind === SyntaxKind.MethodSignature || container.kind === SyntaxKind.GetAccessor || @@ -13392,11 +13403,7 @@ namespace ts { function isNumericComputedName(name: ComputedPropertyName): boolean { // It seems odd to consider an expression of type Any to result in a numeric name, // but this behavior is consistent with checkIndexedAccess - return isTypeAnyOrAllConstituentTypesHaveKind(checkComputedPropertyName(name), TypeFlags.NumberLike); - } - - function isTypeAnyOrAllConstituentTypesHaveKind(type: Type, kind: TypeFlags): boolean { - return isTypeAny(type) || isTypeOfKind(type, kind); + return isTypeAssignableToKind(checkComputedPropertyName(name), TypeFlags.NumberLike); } function isInfinityOrNaNString(name: string | __String): boolean { @@ -13432,10 +13439,11 @@ namespace ts { const links = getNodeLinks(node.expression); if (!links.resolvedType) { links.resolvedType = checkExpression(node.expression); - // This will allow types number, string, symbol or any. It will also allow enums, the unknown // type, and any union of these types (like string | number). - if (!isTypeAnyOrAllConstituentTypesHaveKind(links.resolvedType, TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.ESSymbol)) { + if (links.resolvedType.flags & TypeFlags.Nullable || + !isTypeAssignableToKind(links.resolvedType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol) && + !isTypeAssignableTo(links.resolvedType, getUnionType([stringType, numberType, esSymbolType]))) { error(node, Diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any); } else { @@ -14099,11 +14107,8 @@ namespace ts { */ function resolveCustomJsxElementAttributesType(openingLikeElement: JsxOpeningLikeElement, shouldIncludeAllStatelessAttributesType: boolean, - elementType?: Type, + elementType: Type = checkExpression(openingLikeElement.tagName), elementClassType?: Type): Type { - if (!elementType) { - elementType = checkExpression(openingLikeElement.tagName); - } if (elementType.flags & TypeFlags.Union) { const types = (elementType as UnionType).types; @@ -14237,11 +14242,12 @@ namespace ts { */ function getCustomJsxElementAttributesType(node: JsxOpeningLikeElement, shouldIncludeAllStatelessAttributesType: boolean): Type { const links = getNodeLinks(node); - if (!links.resolvedJsxElementAttributesType) { + const linkLocation = shouldIncludeAllStatelessAttributesType ? "resolvedJsxElementAllAttributesType" : "resolvedJsxElementAttributesType"; + if (!links[linkLocation]) { const elemClassType = getJsxGlobalElementClassType(); - return links.resolvedJsxElementAttributesType = resolveCustomJsxElementAttributesType(node, shouldIncludeAllStatelessAttributesType, /*elementType*/ undefined, elemClassType); + return links[linkLocation] = resolveCustomJsxElementAttributesType(node, shouldIncludeAllStatelessAttributesType, /*elementType*/ undefined, elemClassType); } - return links.resolvedJsxElementAttributesType; + return links[linkLocation]; } /** @@ -14758,7 +14764,7 @@ namespace ts { if (prop && noUnusedIdentifiers && (prop.flags & SymbolFlags.ClassMember) && - prop.valueDeclaration && (getModifierFlags(prop.valueDeclaration) & ModifierFlags.Private)) { + prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Private)) { if (getCheckFlags(prop) & CheckFlags.Instantiated) { getSymbolLinks(prop).target.isReferenced = true; } @@ -15128,8 +15134,8 @@ namespace ts { } // Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec) - function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper): Signature { - const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes); + function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper, compareTypes?: TypeComparer): Signature { + const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes, compareTypes); forEachMatchingParameterType(contextualSignature, signature, (source, target) => { // Type parameters from outer context referenced by source type are fixed by instantiation of the source type inferTypes(context.inferences, instantiateType(source, contextualMapper || identityMapper), target); @@ -15542,7 +15548,7 @@ namespace ts { case SyntaxKind.ComputedPropertyName: const nameType = checkComputedPropertyName(element.name); - if (isTypeOfKind(nameType, TypeFlags.ESSymbol)) { + if (isTypeAssignableToKind(nameType, TypeFlags.ESSymbol)) { return nameType; } else { @@ -15704,9 +15710,10 @@ namespace ts { // // For a decorator, no arguments are susceptible to contextual typing due to the fact // decorators are applied to a declaration by the emitter, and not to an expression. + const isSingleNonGenericCandidate = candidates.length === 1 && !candidates[0].typeParameters; let excludeArgument: boolean[]; let excludeCount = 0; - if (!isDecorator) { + if (!isDecorator && !isSingleNonGenericCandidate) { // We do not need to call `getEffectiveArgumentCount` here as it only // applies when calculating the number of arguments for a decorator. for (let i = isTaggedTemplate ? 1 : 0; i < args.length; i++) { @@ -15859,6 +15866,19 @@ namespace ts { function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { candidateForArgumentError = undefined; candidateForTypeArgumentError = undefined; + + if (isSingleNonGenericCandidate) { + const candidate = candidates[0]; + if (!hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) { + return undefined; + } + if (!checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) { + candidateForArgumentError = candidate; + return undefined; + } + return candidate; + } + for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) { const originalCandidate = candidates[candidateIndex]; if (!hasCorrectArity(node, args, originalCandidate, signatureHelpTrailingComma)) { @@ -15906,7 +15926,6 @@ namespace ts { return undefined; } - } function getLongestCandidateIndex(candidates: Signature[], argsCount: number): number { @@ -16038,7 +16057,7 @@ namespace ts { // In the case of a merged class-module or class-interface declaration, // only the class declaration node will have the Abstract flag set. const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol); - if (valueDecl && getModifierFlags(valueDecl) & ModifierFlags.Abstract) { + if (valueDecl && hasModifier(valueDecl, ModifierFlags.Abstract)) { error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(getNameOfDeclaration(valueDecl))); return resolveErrorCall(node); } @@ -16091,10 +16110,10 @@ namespace ts { } const declaration = signature.declaration; - const modifiers = getModifierFlags(declaration); + const modifiers = getSelectedModifierFlags(declaration, ModifierFlags.NonPublicAccessibilityModifier); // Public constructor is accessible. - if (!(modifiers & ModifierFlags.NonPublicAccessibilityModifier)) { + if (!modifiers) { return true; } @@ -16401,12 +16420,35 @@ namespace ts { if (moduleSymbol) { const esModuleSymbol = resolveESModuleSymbol(moduleSymbol, specifier, /*dontRecursivelyResolve*/ true); if (esModuleSymbol) { - return createPromiseReturnType(node, getTypeOfSymbol(esModuleSymbol)); + return createPromiseReturnType(node, getTypeWithSyntheticDefaultImportType(getTypeOfSymbol(esModuleSymbol), esModuleSymbol)); } } return createPromiseReturnType(node, anyType); } + function getTypeWithSyntheticDefaultImportType(type: Type, symbol: Symbol): Type { + if (allowSyntheticDefaultImports && type && type !== unknownType) { + const synthType = type as SyntheticDefaultModuleType; + if (!synthType.syntheticType) { + if (!getPropertyOfType(type, InternalSymbolName.Default)) { + const memberTable = createSymbolTable(); + const newSymbol = createSymbol(SymbolFlags.Alias, InternalSymbolName.Default); + newSymbol.target = resolveSymbol(symbol); + memberTable.set(InternalSymbolName.Default, newSymbol); + const anonymousSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type); + const defaultContainingObject = createAnonymousType(anonymousSymbol, memberTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined); + anonymousSymbol.type = defaultContainingObject; + synthType.syntheticType = getIntersectionType([type, defaultContainingObject]); + } + else { + synthType.syntheticType = type; + } + } + return synthType.syntheticType; + } + return type; + } + function isCommonJsRequire(node: Node) { if (!isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) { return false; @@ -16544,14 +16586,14 @@ namespace ts { // When contextual typing assigns a type to a parameter that contains a binding pattern, we also need to push // the destructured type into the contained binding elements. - function assignBindingElementTypes(node: VariableLikeDeclaration) { - if (isBindingPattern(node.name)) { - for (const element of node.name.elements) { - if (!isOmittedExpression(element)) { - if (element.name.kind === SyntaxKind.Identifier) { - getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element); - } - assignBindingElementTypes(element); + function assignBindingElementTypes(pattern: BindingPattern) { + for (const element of pattern.elements) { + if (!isOmittedExpression(element)) { + if (element.name.kind === SyntaxKind.Identifier) { + getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element); + } + else { + assignBindingElementTypes(element.name); } } } @@ -16561,13 +16603,14 @@ namespace ts { const links = getSymbolLinks(parameter); if (!links.type) { links.type = contextualType; - const name = getNameOfDeclaration(parameter.valueDeclaration); - // if inference didn't come up with anything but {}, fall back to the binding pattern if present. - if (links.type === emptyObjectType && - (name.kind === SyntaxKind.ObjectBindingPattern || name.kind === SyntaxKind.ArrayBindingPattern)) { - links.type = getTypeFromBindingPattern(name); + const decl = parameter.valueDeclaration as ParameterDeclaration; + if (decl.name.kind !== SyntaxKind.Identifier) { + // if inference didn't come up with anything but {}, fall back to the binding pattern if present. + if (links.type === emptyObjectType) { + links.type = getTypeFromBindingPattern(decl.name); + } + assignBindingElementTypes(decl.name); } - assignBindingElementTypes(parameter.valueDeclaration); } } @@ -16827,18 +16870,18 @@ namespace ts { function checkFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | MethodDeclaration, checkMode?: CheckMode): Type { Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); - // Grammar checking - const hasGrammarError = checkGrammarFunctionLikeDeclaration(node); - if (!hasGrammarError && node.kind === SyntaxKind.FunctionExpression) { - checkGrammarForGenerator(node); - } - // The identityMapper object is used to indicate that function expressions are wildcards if (checkMode === CheckMode.SkipContextSensitive && isContextSensitive(node)) { checkNodeDeferred(node); return anyFunctionType; } + // Grammar checking + const hasGrammarError = checkGrammarFunctionLikeDeclaration(node); + if (!hasGrammarError && node.kind === SyntaxKind.FunctionExpression) { + checkGrammarForGenerator(node); + } + const links = getNodeLinks(node); const type = getTypeOfSymbol(node.symbol); @@ -16932,7 +16975,7 @@ namespace ts { } function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage): boolean { - if (!isTypeAnyOrAllConstituentTypesHaveKind(type, TypeFlags.NumberLike)) { + if (!isTypeAssignableToKind(type, TypeFlags.NumberLike)) { error(operand, diagnostic); return false; } @@ -17110,31 +17153,22 @@ namespace ts { return false; } - // Return true if type is of the given kind. A union type is of a given kind if all constituent types - // are of the given kind. An intersection type is of a given kind if at least one constituent type is - // of the given kind. - function isTypeOfKind(type: Type, kind: TypeFlags): boolean { - if (type.flags & kind) { + function isTypeAssignableToKind(source: Type, kind: TypeFlags, strict?: boolean): boolean { + if (source.flags & kind) { return true; } - if (type.flags & TypeFlags.Union) { - const types = (type).types; - for (const t of types) { - if (!isTypeOfKind(t, kind)) { - return false; - } - } - return true; + if (strict && source.flags & (TypeFlags.Any | TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Null)) { + return false; } - if (type.flags & TypeFlags.Intersection) { - const types = (type).types; - for (const t of types) { - if (isTypeOfKind(t, kind)) { - return true; - } - } - } - return false; + return (kind & TypeFlags.NumberLike && isTypeAssignableTo(source, numberType)) || + (kind & TypeFlags.StringLike && isTypeAssignableTo(source, stringType)) || + (kind & TypeFlags.BooleanLike && isTypeAssignableTo(source, booleanType)) || + (kind & TypeFlags.Void && isTypeAssignableTo(source, voidType)) || + (kind & TypeFlags.Never && isTypeAssignableTo(source, neverType)) || + (kind & TypeFlags.Null && isTypeAssignableTo(source, nullType)) || + (kind & TypeFlags.Undefined && isTypeAssignableTo(source, undefinedType)) || + (kind & TypeFlags.ESSymbol && isTypeAssignableTo(source, esSymbolType)) || + (kind & TypeFlags.NonPrimitive && isTypeAssignableTo(source, nonPrimitiveType)); } function isConstEnumObjectType(type: Type): boolean { @@ -17154,7 +17188,7 @@ namespace ts { // and the right operand to be of type Any, a subtype of the 'Function' interface type, or have a call or construct signature. // The result is always of the Boolean primitive type. // NOTE: do not raise error if leftType is unknown as related error was already reported - if (isTypeOfKind(leftType, TypeFlags.Primitive)) { + if (!isTypeAny(leftType) && isTypeAssignableToKind(leftType, TypeFlags.Primitive)) { error(left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } // NOTE: do not raise error if right is unknown as related error was already reported @@ -17177,10 +17211,10 @@ namespace ts { // The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type, // and the right operand to be of type Any, an object type, or a type parameter type. // The result is always of the Boolean primitive type. - if (!(isTypeComparableTo(leftType, stringType) || isTypeOfKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbol))) { + if (!(isTypeComparableTo(leftType, stringType) || isTypeAssignableToKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbol))) { error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); } - if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeVariable | TypeFlags.NonPrimitive)) { + if (!isTypeAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.TypeVariable)) { error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } return booleanType; @@ -17489,32 +17523,30 @@ namespace ts { return silentNeverType; } - if (!isTypeOfKind(leftType, TypeFlags.Any | TypeFlags.StringLike) && !isTypeOfKind(rightType, TypeFlags.Any | TypeFlags.StringLike)) { + if (!isTypeAssignableToKind(leftType, TypeFlags.StringLike) && !isTypeAssignableToKind(rightType, TypeFlags.StringLike)) { leftType = checkNonNullType(leftType, left); rightType = checkNonNullType(rightType, right); } let resultType: Type; - if (isTypeOfKind(leftType, TypeFlags.NumberLike) && isTypeOfKind(rightType, TypeFlags.NumberLike)) { + if (isTypeAssignableToKind(leftType, TypeFlags.NumberLike, /*strict*/ true) && isTypeAssignableToKind(rightType, TypeFlags.NumberLike, /*strict*/ true)) { // Operands of an enum type are treated as having the primitive type Number. // If both operands are of the Number primitive type, the result is of the Number primitive type. resultType = numberType; } - else { - if (isTypeOfKind(leftType, TypeFlags.StringLike) || isTypeOfKind(rightType, TypeFlags.StringLike)) { + else if (isTypeAssignableToKind(leftType, TypeFlags.StringLike, /*strict*/ true) || isTypeAssignableToKind(rightType, TypeFlags.StringLike, /*strict*/ true)) { // If one or both operands are of the String primitive type, the result is of the String primitive type. resultType = stringType; - } - else if (isTypeAny(leftType) || isTypeAny(rightType)) { - // Otherwise, the result is of type Any. - // NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we. - resultType = leftType === unknownType || rightType === unknownType ? unknownType : anyType; - } + } + else if (isTypeAny(leftType) || isTypeAny(rightType)) { + // Otherwise, the result is of type Any. + // NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we. + resultType = leftType === unknownType || rightType === unknownType ? unknownType : anyType; + } - // Symbols are not allowed at all in arithmetic expressions - if (resultType && !checkForDisallowedESSymbolOperand(operator)) { - return resultType; - } + // Symbols are not allowed at all in arithmetic expressions + if (resultType && !checkForDisallowedESSymbolOperand(operator)) { + return resultType; } if (!resultType) { @@ -17725,15 +17757,14 @@ namespace ts { return getBestChoiceType(type1, type2); } - function checkLiteralExpression(node: Expression): Type { - if (node.kind === SyntaxKind.NumericLiteral) { - checkGrammarNumericLiteral(node); - } + function checkLiteralExpression(node: LiteralExpression | Token): Type { switch (node.kind) { + case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.StringLiteral: - return getFreshTypeOfLiteralType(getLiteralType((node).text)); + return getFreshTypeOfLiteralType(getLiteralType(node.text)); case SyntaxKind.NumericLiteral: - return getFreshTypeOfLiteralType(getLiteralType(+(node).text)); + checkGrammarNumericLiteral(node); + return getFreshTypeOfLiteralType(getLiteralType(+node.text)); case SyntaxKind.TrueKeyword: return trueType; case SyntaxKind.FalseKeyword: @@ -17951,15 +17982,14 @@ namespace ts { return checkSuperExpression(node); case SyntaxKind.NullKeyword: return nullWideningType; + case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: - return checkLiteralExpression(node); + return checkLiteralExpression(node as LiteralExpression); case SyntaxKind.TemplateExpression: return checkTemplateExpression(node); - case SyntaxKind.NoSubstitutionTemplateLiteral: - return stringType; case SyntaxKind.RegularExpressionLiteral: return globalRegExpType; case SyntaxKind.ArrayLiteralExpression: @@ -18064,7 +18094,7 @@ namespace ts { checkVariableLikeDeclaration(node); let func = getContainingFunction(node); - if (getModifierFlags(node) & ModifierFlags.ParameterPropertyModifier) { + if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) { func = getContainingFunction(node); if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) { error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation); @@ -18299,7 +18329,7 @@ namespace ts { } } else { - const isStatic = getModifierFlags(member) & ModifierFlags.Static; + const isStatic = hasModifier(member, ModifierFlags.Static); const names = isStatic ? staticNames : instanceNames; const memberName = member.name && getPropertyNameForPropertyNameNode(member.name); @@ -18360,7 +18390,7 @@ namespace ts { function checkClassForStaticPropertyNameConflicts(node: ClassLikeDeclaration) { for (const member of node.members) { const memberNameNode = member.name; - const isStatic = getModifierFlags(member) & ModifierFlags.Static; + const isStatic = hasModifier(member, ModifierFlags.Static); if (isStatic && memberNameNode) { const memberName = getPropertyNameForPropertyNameNode(memberNameNode); switch (memberName) { @@ -18465,7 +18495,7 @@ namespace ts { // Abstract methods cannot have an implementation. // Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node. - if (getModifierFlags(node) & ModifierFlags.Abstract && node.body) { + if (hasModifier(node, ModifierFlags.Abstract) && node.body) { error(node, Diagnostics.Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, declarationNameToString(node.name)); } } @@ -18514,18 +18544,9 @@ namespace ts { return forEachChild(n, containsSuperCall); } - function markThisReferencesAsErrors(n: Node): void { - if (n.kind === SyntaxKind.ThisKeyword) { - error(n, Diagnostics.this_cannot_be_referenced_in_current_location); - } - else if (n.kind !== SyntaxKind.FunctionExpression && n.kind !== SyntaxKind.FunctionDeclaration) { - forEachChild(n, markThisReferencesAsErrors); - } - } - function isInstancePropertyWithInitializer(n: Node): boolean { return n.kind === SyntaxKind.PropertyDeclaration && - !(getModifierFlags(n) & ModifierFlags.Static) && + !hasModifier(n, ModifierFlags.Static) && !!(n).initializer; } @@ -18548,8 +18569,8 @@ namespace ts { // - The constructor declares parameter properties // or the containing class declares instance member variables with initializers. const superCallShouldBeFirst = - forEach((node.parent).members, isInstancePropertyWithInitializer) || - forEach(node.parameters, p => getModifierFlags(p) & ModifierFlags.ParameterPropertyModifier); + some((node.parent).members, isInstancePropertyWithInitializer) || + some(node.parameters, p => hasModifier(p, ModifierFlags.ParameterPropertyModifier)); // Skip past any prologue directives to find the first statement // to ensure that it was a super call. @@ -18603,10 +18624,12 @@ namespace ts { const otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; const otherAccessor = getDeclarationOfKind(node.symbol, otherKind); if (otherAccessor) { - if ((getModifierFlags(node) & ModifierFlags.AccessibilityModifier) !== (getModifierFlags(otherAccessor) & ModifierFlags.AccessibilityModifier)) { + const nodeFlags = getModifierFlags(node); + const otherFlags = getModifierFlags(otherAccessor); + if ((nodeFlags & ModifierFlags.AccessibilityModifier) !== (otherFlags & ModifierFlags.AccessibilityModifier)) { error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility); } - if (hasModifier(node, ModifierFlags.Abstract) !== hasModifier(otherAccessor, ModifierFlags.Abstract)) { + if ((nodeFlags & ModifierFlags.Abstract) !== (otherFlags & ModifierFlags.Abstract)) { error(node.name, Diagnostics.Accessors_must_both_be_abstract_or_non_abstract); } @@ -18737,7 +18760,7 @@ namespace ts { } // Check if we're indexing with a numeric type and the object type is a generic // type with a constraint that has a numeric index signature. - if (maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && isTypeOfKind(indexType, TypeFlags.NumberLike)) { + if (maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) { const constraint = getBaseConstraintOfType(objectType); if (constraint && getIndexInfoOfType(constraint, IndexKind.Number)) { return type; @@ -18762,7 +18785,7 @@ namespace ts { } function isPrivateWithinAmbient(node: Node): boolean { - return (getModifierFlags(node) & ModifierFlags.Private) && isInAmbientContext(node); + return hasModifier(node, ModifierFlags.Private) && isInAmbientContext(node); } function getEffectiveDeclarationFlags(n: Node, flagsToCheck: ModifierFlags): ModifierFlags { @@ -18875,13 +18898,13 @@ namespace ts { !isComputedPropertyName(node.name) && !isComputedPropertyName(subsequentName) && getEscapedTextOfIdentifierOrLiteral(node.name) === getEscapedTextOfIdentifierOrLiteral(subsequentName))) { const reportError = (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) && - (getModifierFlags(node) & ModifierFlags.Static) !== (getModifierFlags(subsequentNode) & ModifierFlags.Static); + hasModifier(node, ModifierFlags.Static) !== hasModifier(subsequentNode, ModifierFlags.Static); // we can get here in two cases // 1. mixed static and instance class members // 2. something with the same name was defined before the set of overloads that prevents them from merging // here we'll report error only for the first case since for second we should already report error in binder if (reportError) { - const diagnostic = getModifierFlags(node) & ModifierFlags.Static ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static; + const diagnostic = hasModifier(node, ModifierFlags.Static) ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static; error(errorNode, diagnostic); } return; @@ -18899,7 +18922,7 @@ namespace ts { else { // Report different errors regarding non-consecutive blocks of declarations depending on whether // the node in question is abstract. - if (getModifierFlags(node) & ModifierFlags.Abstract) { + if (hasModifier(node, ModifierFlags.Abstract)) { error(errorNode, Diagnostics.All_declarations_of_an_abstract_method_must_be_consecutive); } else { @@ -18975,7 +18998,7 @@ namespace ts { // Abstract methods can't have an implementation -- in particular, they don't need one. if (lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body && - !(getModifierFlags(lastSeenNonAmbientDeclaration) & ModifierFlags.Abstract) && !lastSeenNonAmbientDeclaration.questionToken) { + !hasModifier(lastSeenNonAmbientDeclaration, ModifierFlags.Abstract) && !lastSeenNonAmbientDeclaration.questionToken) { reportImplementationExpectedError(lastSeenNonAmbientDeclaration); } @@ -19778,13 +19801,13 @@ namespace ts { if (node.members) { for (const member of node.members) { if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.PropertyDeclaration) { - if (!member.symbol.isReferenced && getModifierFlags(member) & ModifierFlags.Private) { + if (!member.symbol.isReferenced && hasModifier(member, ModifierFlags.Private)) { error(member.name, Diagnostics._0_is_declared_but_never_used, unescapeLeadingUnderscores(member.symbol.escapedName)); } } else if (member.kind === SyntaxKind.Constructor) { for (const parameter of (member).parameters) { - if (!parameter.symbol.isReferenced && getModifierFlags(parameter) & ModifierFlags.Private) { + if (!parameter.symbol.isReferenced && hasModifier(parameter, ModifierFlags.Private)) { error(parameter.name, Diagnostics.Property_0_is_declared_but_never_used, unescapeLeadingUnderscores(parameter.symbol.escapedName)); } } @@ -20254,7 +20277,7 @@ namespace ts { ModifierFlags.Readonly | ModifierFlags.Static; - return (getModifierFlags(left) & interestingFlags) === (getModifierFlags(right) & interestingFlags); + return getSelectedModifierFlags(left, interestingFlags) === getSelectedModifierFlags(right, interestingFlags); } function checkVariableDeclaration(node: VariableDeclaration) { @@ -20444,7 +20467,7 @@ namespace ts { // unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved // in this case error about missing name is already reported - do not report extra one - if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeVariable | TypeFlags.NonPrimitive)) { + if (!isTypeAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.TypeVariable)) { error(node.expression, Diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter); } @@ -21007,7 +21030,7 @@ namespace ts { // Only process instance properties with computed names here. // Static properties cannot be in conflict with indexers, // and properties with literal names were already checked. - if (!(getModifierFlags(member) & ModifierFlags.Static) && hasDynamicName(member)) { + if (!hasModifier(member, ModifierFlags.Static) && hasDynamicName(member)) { const propType = getTypeOfSymbol(member.symbol); checkIndexConstraintForProperty(member.symbol, propType, type, declaredStringIndexer, stringIndexType, IndexKind.String); checkIndexConstraintForProperty(member.symbol, propType, type, declaredNumberIndexer, numberIndexType, IndexKind.Number); @@ -21202,7 +21225,7 @@ namespace ts { } function checkClassDeclaration(node: ClassDeclaration) { - if (!node.name && !(getModifierFlags(node) & ModifierFlags.Default)) { + if (!node.name && !hasModifier(node, ModifierFlags.Default)) { grammarErrorOnFirstToken(node, Diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name); } checkClassLikeDeclaration(node); @@ -21308,7 +21331,7 @@ namespace ts { const signatures = getSignaturesOfType(type, SignatureKind.Construct); if (signatures.length) { const declaration = signatures[0].declaration; - if (declaration && getModifierFlags(declaration) & ModifierFlags.Private) { + if (declaration && hasModifier(declaration, ModifierFlags.Private)) { const typeClassDeclaration = getClassLikeDeclarationOfSymbol(type.symbol); if (!isNodeWithinClass(node, typeClassDeclaration)) { error(node, Diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, getFullyQualifiedName(type.symbol)); @@ -21374,7 +21397,7 @@ namespace ts { // It is an error to inherit an abstract member without implementing it or being declared abstract. // If there is no declaration for the derived class (as in the case of class expressions), // then the class cannot be declared abstract. - if (baseDeclarationFlags & ModifierFlags.Abstract && (!derivedClassDecl || !(getModifierFlags(derivedClassDecl) & ModifierFlags.Abstract))) { + if (baseDeclarationFlags & ModifierFlags.Abstract && (!derivedClassDecl || !hasModifier(derivedClassDecl, ModifierFlags.Abstract))) { if (derivedClassDecl.kind === SyntaxKind.ClassExpression) { error(derivedClassDecl, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1, symbolToString(baseProperty), typeToString(baseType)); @@ -21997,7 +22020,7 @@ namespace ts { // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors. return; } - if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) { + if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && hasModifiers(node)) { grammarErrorOnFirstToken(node, Diagnostics.An_import_declaration_cannot_have_modifiers); } if (checkExternalImportOrExportDeclaration(node)) { @@ -22027,7 +22050,7 @@ namespace ts { checkGrammarDecorators(node) || checkGrammarModifiers(node); if (isInternalModuleImportEqualsDeclaration(node) || checkExternalImportOrExportDeclaration(node)) { checkImportBinding(node); - if (getModifierFlags(node) & ModifierFlags.Export) { + if (hasModifier(node, ModifierFlags.Export)) { markExportAsReferenced(node); } if (isInternalModuleImportEqualsDeclaration(node)) { @@ -22060,7 +22083,7 @@ namespace ts { return; } - if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) { + if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && hasModifiers(node)) { grammarErrorOnFirstToken(node, Diagnostics.An_export_declaration_cannot_have_modifiers); } @@ -22133,7 +22156,7 @@ namespace ts { return; } // Grammar checking - if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && getModifierFlags(node) !== 0) { + if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && hasModifiers(node)) { grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers); } if (node.expression.kind === SyntaxKind.Identifier) { @@ -22531,7 +22554,7 @@ namespace ts { } const symbols = createSymbolTable(); - let memberFlags: ModifierFlags = ModifierFlags.None; + let isStatic = false; populateSymbols(); @@ -22564,7 +22587,7 @@ namespace ts { // add the type parameters into the symbol table // (type parameters of classDeclaration/classExpression and interface are in member property of the symbol. // Note: that the memberFlags come from previous iteration. - if (!(memberFlags & ModifierFlags.Static)) { + if (!isStatic) { copySymbols(getSymbolOfNode(location).members, meaning & SymbolFlags.Type); } break; @@ -22580,7 +22603,7 @@ namespace ts { copySymbol(argumentsSymbol, meaning); } - memberFlags = getModifierFlags(location); + isStatic = hasModifier(location, ModifierFlags.Static); location = location.parent; } @@ -23041,7 +23064,7 @@ namespace ts { */ function getParentTypeOfClassElement(node: ClassElement) { const classSymbol = getSymbolOfNode(node.parent); - return getModifierFlags(node) & ModifierFlags.Static + return hasModifier(node, ModifierFlags.Static) ? getTypeOfSymbol(classSymbol) : getDeclaredTypeOfSymbol(classSymbol); } @@ -23353,14 +23376,14 @@ namespace ts { return strictNullChecks && !isOptionalParameter(parameter) && parameter.initializer && - !(getModifierFlags(parameter) & ModifierFlags.ParameterPropertyModifier); + !hasModifier(parameter, ModifierFlags.ParameterPropertyModifier); } function isOptionalUninitializedParameterProperty(parameter: ParameterDeclaration) { return strictNullChecks && isOptionalParameter(parameter) && !parameter.initializer && - !!(getModifierFlags(parameter) & ModifierFlags.ParameterPropertyModifier); + hasModifier(parameter, ModifierFlags.ParameterPropertyModifier); } function getNodeCheckFlags(node: Node): NodeCheckFlags { @@ -23431,22 +23454,22 @@ namespace ts { else if (type.flags & TypeFlags.Any) { return TypeReferenceSerializationKind.ObjectType; } - else if (isTypeOfKind(type, TypeFlags.Void | TypeFlags.Nullable | TypeFlags.Never)) { + else if (isTypeAssignableToKind(type, TypeFlags.Void | TypeFlags.Nullable | TypeFlags.Never)) { return TypeReferenceSerializationKind.VoidNullableOrNeverType; } - else if (isTypeOfKind(type, TypeFlags.BooleanLike)) { + else if (isTypeAssignableToKind(type, TypeFlags.BooleanLike)) { return TypeReferenceSerializationKind.BooleanType; } - else if (isTypeOfKind(type, TypeFlags.NumberLike)) { + else if (isTypeAssignableToKind(type, TypeFlags.NumberLike)) { return TypeReferenceSerializationKind.NumberLikeType; } - else if (isTypeOfKind(type, TypeFlags.StringLike)) { + else if (isTypeAssignableToKind(type, TypeFlags.StringLike)) { return TypeReferenceSerializationKind.StringLikeType; } else if (isTupleType(type)) { return TypeReferenceSerializationKind.ArrayLikeType; } - else if (isTypeOfKind(type, TypeFlags.ESSymbol)) { + else if (isTypeAssignableToKind(type, TypeFlags.ESSymbol)) { return TypeReferenceSerializationKind.ESSymbolType; } else if (isFunctionType(type)) { @@ -23975,7 +23998,7 @@ namespace ts { node.kind !== SyntaxKind.SetAccessor) { return grammarErrorOnNode(modifier, Diagnostics.abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration); } - if (!(node.parent.kind === SyntaxKind.ClassDeclaration && getModifierFlags(node.parent) & ModifierFlags.Abstract)) { + if (!(node.parent.kind === SyntaxKind.ClassDeclaration && hasModifier(node.parent, ModifierFlags.Abstract))) { return grammarErrorOnNode(modifier, Diagnostics.Abstract_methods_can_only_appear_within_an_abstract_class); } if (flags & ModifierFlags.Static) { @@ -24195,7 +24218,7 @@ namespace ts { if (parameter.dotDotDotToken) { return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.An_index_signature_cannot_have_a_rest_parameter); } - if (getModifierFlags(parameter) !== 0) { + if (hasModifiers(parameter)) { return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier); } if (parameter.questionToken) { @@ -24534,10 +24557,10 @@ namespace ts { else if (isInAmbientContext(accessor)) { return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_be_declared_in_an_ambient_context); } - else if (accessor.body === undefined && !(getModifierFlags(accessor) & ModifierFlags.Abstract)) { + else if (accessor.body === undefined && !hasModifier(accessor, ModifierFlags.Abstract)) { return grammarErrorAtPos(getSourceFileOfNode(accessor), accessor.end - 1, ";".length, Diagnostics._0_expected, "{"); } - else if (accessor.body && getModifierFlags(accessor) & ModifierFlags.Abstract) { + else if (accessor.body && hasModifier(accessor, ModifierFlags.Abstract)) { return grammarErrorOnNode(accessor, Diagnostics.An_abstract_accessor_cannot_have_an_implementation); } else if (accessor.typeParameters) { @@ -24920,7 +24943,7 @@ namespace ts { node.kind === SyntaxKind.ExportDeclaration || node.kind === SyntaxKind.ExportAssignment || node.kind === SyntaxKind.NamespaceExportDeclaration || - getModifierFlags(node) & (ModifierFlags.Ambient | ModifierFlags.Export | ModifierFlags.Default)) { + hasModifier(node, ModifierFlags.Ambient | ModifierFlags.Export | ModifierFlags.Default)) { return false; } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index e3348892844..3748dd9b9a4 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -340,7 +340,6 @@ namespace ts { isTSConfigOnly: true, category: Diagnostics.Module_Resolution_Options, description: Diagnostics.A_series_of_entries_which_re_map_imports_to_lookup_locations_relative_to_the_baseUrl - }, { // this option can only be specified in tsconfig.json @@ -384,6 +383,12 @@ namespace ts { category: Diagnostics.Module_Resolution_Options, description: Diagnostics.Allow_default_imports_from_modules_with_no_default_export_This_does_not_affect_code_emit_just_typechecking }, + { + name: "preserveSymlinks", + type: "boolean", + category: Diagnostics.Module_Resolution_Options, + description: Diagnostics.Do_not_resolve_the_real_path_of_symlinks, + }, // Source Maps { @@ -1448,14 +1453,10 @@ namespace ts { } } else { - // If no includes were specified, exclude common package folders and the outDir - const specs = includeSpecs ? [] : ["node_modules", "bower_components", "jspm_packages"]; - const outDir = raw["compilerOptions"] && raw["compilerOptions"]["outDir"]; if (outDir) { - specs.push(outDir); + excludeSpecs = [outDir]; } - excludeSpecs = specs; } if (filesSpecs === undefined && includeSpecs === undefined) { @@ -1478,7 +1479,12 @@ namespace ts { } } - export function getErrorForNoInputFiles({ includeSpecs, excludeSpecs }: ConfigFileSpecs, configFileName?: string) { + export function isErrorNoInputFiles(error: Diagnostic) { + return error.code === Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code; + } + + /*@internal*/ + export function getErrorForNoInputFiles({ includeSpecs, excludeSpecs }: ConfigFileSpecs, configFileName: string | undefined) { return createCompilerDiagnostic( Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, configFileName || "tsconfig.json", @@ -1924,9 +1930,9 @@ namespace ts { /** * Expands an array of file specifications. * - * @param fileNames The literal file names to include. - * @param include The wildcard file specifications to include. - * @param exclude The wildcard file specifications to exclude. + * @param filesSpecs The literal file names to include. + * @param includeSpecs The wildcard file specifications to include. + * @param excludeSpecs The wildcard file specifications to exclude. * @param basePath The base path for any relative file specifications. * @param options Compiler options. * @param host The host used to resolve files and directories. @@ -2044,23 +2050,13 @@ namespace ts { } function validateSpecs(specs: ReadonlyArray, errors: Push, allowTrailingRecursion: boolean, jsonSourceFile: JsonSourceFile, specKey: string): ReadonlyArray { - const validSpecs: string[] = []; - for (const spec of specs) { - if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) { - errors.push(createDiagnostic(Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec)); + return specs.filter(spec => { + const diag = specToDiagnostic(spec, allowTrailingRecursion); + if (diag !== undefined) { + errors.push(createDiagnostic(diag, spec)); } - else if (invalidMultipleRecursionPatterns.test(spec)) { - errors.push(createDiagnostic(Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, spec)); - } - else if (invalidDotDotAfterRecursiveWildcardPattern.test(spec)) { - errors.push(createDiagnostic(Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec)); - } - else { - validSpecs.push(spec); - } - } - - return validSpecs; + return diag === undefined; + }); function createDiagnostic(message: DiagnosticMessage, spec: string): Diagnostic { if (jsonSourceFile && jsonSourceFile.jsonObject) { @@ -2078,6 +2074,18 @@ namespace ts { } } + function specToDiagnostic(spec: string, allowTrailingRecursion: boolean): ts.DiagnosticMessage | undefined { + if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) { + return Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0; + } + else if (invalidMultipleRecursionPatterns.test(spec)) { + return Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0; + } + else if (invalidDotDotAfterRecursiveWildcardPattern.test(spec)) { + return Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0; + } + } + /** * Gets directories in a set of include patterns that should be watched for changes. */ diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index 06285309f64..adf15c7e28d 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -8,7 +8,7 @@ namespace ts { setWriter(writer: EmitTextWriter): void; emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void; - emitTrailingCommentsOfPosition(pos: number): void; + emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean): void; emitLeadingCommentsOfPosition(pos: number): void; } @@ -306,7 +306,7 @@ namespace ts { } } - function emitTrailingCommentsOfPosition(pos: number) { + function emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean) { if (disabled) { return; } @@ -315,7 +315,7 @@ namespace ts { performance.mark("beforeEmitTrailingCommentsOfPosition"); } - forEachTrailingCommentToEmit(pos, emitTrailingCommentOfPosition); + forEachTrailingCommentToEmit(pos, prefixSpace ? emitTrailingComment : emitTrailingCommentOfPosition); if (extendedDiagnostics) { performance.measure("commentTime", "beforeEmitTrailingCommentsOfPosition"); @@ -415,17 +415,7 @@ namespace ts { * @return true if the comment is a triple-slash comment else false */ function isTripleSlashComment(commentPos: number, commentEnd: number) { - // Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text - // so that we don't end up computing comment string and doing match for all // comments - if (currentText.charCodeAt(commentPos + 1) === CharacterCodes.slash && - commentPos + 2 < commentEnd && - currentText.charCodeAt(commentPos + 2) === CharacterCodes.slash) { - const textSubStr = currentText.substring(commentPos, commentEnd); - return textSubStr.match(fullTripleSlashReferencePathRegEx) || - textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ? - true : false; - } - return false; + return isRecognizedTripleSlashComment(currentText, commentPos, commentEnd); } } } \ No newline at end of file diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 813d805adea..c163de24466 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -11,20 +11,6 @@ namespace ts { /* @internal */ namespace ts { - /** - * Ternary values are defined such that - * x & y is False if either x or y is False. - * x & y is Maybe if either x or y is Maybe, but neither x or y is False. - * x & y is True if both x and y are True. - * x | y is False if both x and y are False. - * x | y is Maybe if either x or y is Maybe, but neither x or y is True. - * x | y is True if either x or y is True. - */ - export const enum Ternary { - False = 0, - Maybe = 1, - True = -1 - } // More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times. export const collator: { compare(a: string, b: string): number } = typeof Intl === "object" && typeof Intl.Collator === "function" ? new Intl.Collator(/*locales*/ undefined, { usage: "sort", sensitivity: "accent" }) : undefined; @@ -375,11 +361,11 @@ namespace ts { return false; } - export function filterMutate(array: T[], f: (x: T) => boolean): void { + export function filterMutate(array: T[], f: (x: T, i: number, array: T[]) => boolean): void { let outIndex = 0; - for (const item of array) { - if (f(item)) { - array[outIndex] = item; + for (let i = 0; i < array.length; i++) { + if (f(array[i], i, array)) { + array[outIndex] = array[i]; outIndex++; } } @@ -524,13 +510,28 @@ namespace ts { return result || array; } - export function mapDefined(array: ReadonlyArray, mapFn: (x: T, i: number) => U | undefined): U[] { + export function mapDefined(array: ReadonlyArray | undefined, mapFn: (x: T, i: number) => U | undefined): U[] { const result: U[] = []; - for (let i = 0; i < array.length; i++) { - const item = array[i]; - const mapped = mapFn(item, i); - if (mapped !== undefined) { - result.push(mapped); + if (array) { + for (let i = 0; i < array.length; i++) { + const item = array[i]; + const mapped = mapFn(item, i); + if (mapped !== undefined) { + result.push(mapped); + } + } + } + return result; + } + + export function mapDefinedIter(iter: Iterator, mapFn: (x: T) => U | undefined): U[] { + const result: U[] = []; + while (true) { + const { value, done } = iter.next(); + if (done) break; + const res = mapFn(value); + if (res !== undefined) { + result.push(res); } } return result; @@ -1320,12 +1321,12 @@ namespace ts { export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): Diagnostic { const end = start + length; - Debug.assert(start >= 0, "start must be non-negative, is " + start); - Debug.assert(length >= 0, "length must be non-negative, is " + length); + Debug.assertGreaterThanOrEqual(start, 0); + Debug.assertGreaterThanOrEqual(length, 0); if (file) { - Debug.assert(start <= file.text.length, `start must be within the bounds of the file. ${start} > ${file.text.length}`); - Debug.assert(end <= file.text.length, `end must be the bounds of the file. ${end} > ${file.text.length}`); + Debug.assertLessThanOrEqual(start, file.text.length); + Debug.assertLessThanOrEqual(end, file.text.length); } let text = getLocaleSpecificMessage(message); @@ -1905,14 +1906,54 @@ namespace ts { const reservedCharacterPattern = /[^\w\s\/]/g; const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question]; - /** - * Matches any single directory segment unless it is the last segment and a .min.js file - * Breakdown: - * [^./] # matches everything up to the first . character (excluding directory seperators) - * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension - */ - const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; - const singleAsteriskRegexFragmentOther = "[^/]*"; + /* @internal */ + export const commonPackageFolders: ReadonlyArray = ["node_modules", "bower_components", "jspm_packages"]; + + const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`; + + interface WildcardMatcher { + singleAsteriskRegexFragment: string; + doubleAsteriskRegexFragment: string; + replaceWildcardCharacter: (match: string) => string; + } + + const filesMatcher: WildcardMatcher = { + /** + * Matches any single directory segment unless it is the last segment and a .min.js file + * Breakdown: + * [^./] # matches everything up to the first . character (excluding directory seperators) + * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension + */ + singleAsteriskRegexFragment: "([^./]|(\\.(?!min\\.js$))?)*", + /** + * Regex for the ** wildcard. Matches any number of subdirectories. When used for including + * files or directories, does not match subdirectories that start with a . character + */ + doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`, + replaceWildcardCharacter: match => replaceWildcardCharacter(match, filesMatcher.singleAsteriskRegexFragment) + }; + + const directoriesMatcher: WildcardMatcher = { + singleAsteriskRegexFragment: "[^/]*", + /** + * Regex for the ** wildcard. Matches any number of subdirectories. When used for including + * files or directories, does not match subdirectories that start with a . character + */ + doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`, + replaceWildcardCharacter: match => replaceWildcardCharacter(match, directoriesMatcher.singleAsteriskRegexFragment) + }; + + const excludeMatcher: WildcardMatcher = { + singleAsteriskRegexFragment: "[^/]*", + doubleAsteriskRegexFragment: "(/.+?)?", + replaceWildcardCharacter: match => replaceWildcardCharacter(match, excludeMatcher.singleAsteriskRegexFragment) + }; + + const wildcardMatchers = { + files: filesMatcher, + directories: directoriesMatcher, + exclude: excludeMatcher + }; export function getRegularExpressionForWildcard(specs: ReadonlyArray, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined { const patterns = getRegularExpressionsForWildcards(specs, basePath, usage); @@ -1931,17 +1972,8 @@ namespace ts { return undefined; } - const replaceWildcardCharacter = usage === "files" ? replaceWildCardCharacterFiles : replaceWildCardCharacterOther; - const singleAsteriskRegexFragment = usage === "files" ? singleAsteriskRegexFragmentFiles : singleAsteriskRegexFragmentOther; - - /** - * Regex for the ** wildcard. Matches any number of subdirectories. When used for including - * files or directories, does not match subdirectories that start with a . character - */ - const doubleAsteriskRegexFragment = usage === "exclude" ? "(/.+?)?" : "(/[^/.][^/]*)*?"; - return flatMap(specs, spec => - spec && getSubPatternFromSpec(spec, basePath, usage, singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter)); + spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage])); } /** @@ -1952,7 +1984,7 @@ namespace ts { return !/[.*?]/.test(lastPathComponent); } - function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", singleAsteriskRegexFragment: string, doubleAsteriskRegexFragment: string, replaceWildcardCharacter: (match: string) => string): string | undefined { + function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined { let subpattern = ""; let hasRecursiveDirectoryWildcard = false; let hasWrittenComponent = false; @@ -1991,20 +2023,36 @@ namespace ts { } if (usage !== "exclude") { + let componentPattern = ""; // The * and ? wildcards should not match directories or files that start with . if they // appear first in a component. Dotted directories and files can be included explicitly // like so: **/.*/.* if (component.charCodeAt(0) === CharacterCodes.asterisk) { - subpattern += "([^./]" + singleAsteriskRegexFragment + ")?"; + componentPattern += "([^./]" + singleAsteriskRegexFragment + ")?"; component = component.substr(1); } else if (component.charCodeAt(0) === CharacterCodes.question) { - subpattern += "[^./]"; + componentPattern += "[^./]"; component = component.substr(1); } - } - subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); + componentPattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); + + // Patterns should not include subfolders like node_modules unless they are + // explicitly included as part of the path. + // + // As an optimization, if the component pattern is the same as the component, + // then there definitely were no wildcard characters and we do not need to + // add the exclusion pattern. + if (componentPattern !== component) { + subpattern += implicitExcludePathRegexPattern; + } + + subpattern += componentPattern; + } + else { + subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); + } } hasWrittenComponent = true; @@ -2018,14 +2066,6 @@ namespace ts { return subpattern; } - function replaceWildCardCharacterFiles(match: string) { - return replaceWildcardCharacter(match, singleAsteriskRegexFragmentFiles); - } - - function replaceWildCardCharacterOther(match: string) { - return replaceWildcardCharacter(match, singleAsteriskRegexFragmentOther); - } - function replaceWildcardCharacter(match: string, singleAsteriskRegexFragment: string) { return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match; } @@ -2199,20 +2239,14 @@ namespace ts { /** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */ export const supportedTypescriptExtensionsForExtractExtension: ReadonlyArray = [Extension.Dts, Extension.Ts, Extension.Tsx]; export const supportedJavascriptExtensions: ReadonlyArray = [Extension.Js, Extension.Jsx]; - const allSupportedExtensions = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions]; + const allSupportedExtensions: ReadonlyArray = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions]; export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: ReadonlyArray): ReadonlyArray { const needAllExtensions = options && options.allowJs; if (!extraFileExtensions || extraFileExtensions.length === 0 || !needAllExtensions) { return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions; } - const extensions: string[] = allSupportedExtensions.slice(0); - for (const extInfo of extraFileExtensions) { - if (extensions.indexOf(extInfo.extension) === -1) { - extensions.push(extInfo.extension); - } - } - return extensions; + return deduplicate([...allSupportedExtensions, ...extraFileExtensions.map(e => e.extension)]); } export function hasJavaScriptFileExtension(fileName: string) { @@ -2380,15 +2414,40 @@ namespace ts { return currentAssertionLevel >= level; } - export function assert(expression: boolean, message?: string, verboseDebugInfo?: () => string, stackCrawlMark?: Function): void { + export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: Function): void { if (!expression) { if (verboseDebugInfo) { - message += "\r\nVerbose Debug Information: " + verboseDebugInfo(); + message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo()); } fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert); } } + export function assertEqual(a: T, b: T, msg?: string, msg2?: string): void { + if (a !== b) { + const message = msg ? msg2 ? `${msg} ${msg2}` : msg : ""; + fail(`Expected ${a} === ${b}. ${message}`); + } + } + + export function assertLessThan(a: number, b: number, msg?: string): void { + if (a >= b) { + fail(`Expected ${a} < ${b}. ${msg || ""}`); + } + } + + export function assertLessThanOrEqual(a: number, b: number): void { + if (a > b) { + fail(`Expected ${a} <= ${b}`); + } + } + + export function assertGreaterThanOrEqual(a: number, b: number): void { + if (a < b) { + fail(`Expected ${a} >= ${b}`); + } + } + export function fail(message?: string, stackCrawlMark?: Function): void { debugger; const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure."); @@ -2554,6 +2613,11 @@ namespace ts { } Debug.fail(`File ${path} has unknown extension.`); } + + export function isAnySupportedFileExtension(path: string): boolean { + return tryGetExtensionFromPath(path) !== undefined; + } + export function tryGetExtensionFromPath(path: string): Extension | undefined { return find(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e)); } @@ -2704,12 +2768,14 @@ namespace ts { const path = toPath(fileOrFolder); const existingResult = cachedReadDirectoryResult.get(path); if (existingResult) { + // This was a folder already present, remove it if this doesnt exist any more if (!host.directoryExists(fileOrFolder)) { cachedReadDirectoryResult.delete(path); } } else { - // Was this earlier file + // This was earlier a file (hence not in cached directory contents) + // or we never cached the directory containing it const parentResult = cachedReadDirectoryResult.get(getDirectoryPath(path)); if (parentResult) { const baseName = getBaseFileName(fileOrFolder); diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 43f7446865f..2c4be2ae7c3 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -1367,6 +1367,10 @@ namespace ts { } function writeVariableStatement(node: VariableStatement) { + // If binding pattern doesn't have name, then there is nothing to be emitted for declaration file i.e. const [,] = [1,2]. + if (every(node.declarationList && node.declarationList.declarations, decl => decl.name && isEmptyBindingPattern(decl.name))) { + return; + } emitJsDocComments(node); emitModuleElementDeclarationFlags(node); if (isLet(node.declarationList)) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 61c8da92ceb..bcdfcc80954 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1908,6 +1908,10 @@ "category": "Error", "code": 2559 }, + "Value of type '{0}' has no properties in common with type '{1}'. Did you mean to call it?": { + "category": "Error", + "code": 2560 + }, "JSX element attributes type '{0}' may not be a union type.": { "category": "Error", "code": 2600 @@ -2658,6 +2662,10 @@ "category": "Message", "code": 6012 }, + "Do not resolve the real path of symlinks.": { + "category": "Message", + "code": 6013 + }, "Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'.": { "category": "Message", "code": 6015 @@ -3669,5 +3677,15 @@ "Convert function '{0}' to class": { "category": "Message", "code": 95002 + }, + + "Extract function": { + "category": "Message", + "code": 95003 + }, + + "Extract function into '{0}'": { + "category": "Message", + "code": 95004 } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 4f07331a98e..5444c618353 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1195,7 +1195,9 @@ namespace ts { if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { const dotRangeStart = node.expression.end; const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1; - const dotToken = { kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd }; + const dotToken = createToken(SyntaxKind.DotToken); + dotToken.pos = dotRangeStart; + dotToken.end = dotRangeEnd; indentBeforeDot = needsIndentation(node, node.expression, dotToken); indentAfterDot = needsIndentation(node, dotToken, node.name); } @@ -1348,7 +1350,7 @@ namespace ts { increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined); emitLeadingCommentsOfPosition(node.operatorToken.pos); writeTokenNode(node.operatorToken); - emitTrailingCommentsOfPosition(node.operatorToken.end); + emitTrailingCommentsOfPosition(node.operatorToken.end, /*prefixSpace*/ true); // Binary operators should have a space before the comment starts increaseIndentIf(indentAfterOperator, " "); emitExpression(node.right); decreaseIndentIf(indentBeforeOperator, indentAfterOperator); @@ -1572,8 +1574,20 @@ namespace ts { write(";"); } + function emitTokenWithComment(token: SyntaxKind, pos: number, contextNode?: Node) { + const node = contextNode && getParseTreeNode(contextNode); + if (node && node.kind === contextNode.kind) { + pos = skipTrivia(currentSourceFile.text, pos); + } + pos = writeToken(token, pos, /*contextNode*/ contextNode); + if (node && node.kind === contextNode.kind) { + emitTrailingCommentsOfPosition(pos, /*prefixSpace*/ true); + } + return pos; + } + function emitReturnStatement(node: ReturnStatement) { - writeToken(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node); + emitTokenWithComment(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node); emitExpressionWithPrefix(" ", node.expression); write(";"); } @@ -2133,10 +2147,12 @@ namespace ts { function emitCatchClause(node: CatchClause) { const openParenPos = writeToken(SyntaxKind.CatchKeyword, node.pos); write(" "); - writeToken(SyntaxKind.OpenParenToken, openParenPos); - emit(node.variableDeclaration); - writeToken(SyntaxKind.CloseParenToken, node.variableDeclaration ? node.variableDeclaration.end : openParenPos); - write(" "); + if (node.variableDeclaration) { + writeToken(SyntaxKind.OpenParenToken, openParenPos); + emit(node.variableDeclaration); + writeToken(SyntaxKind.CloseParenToken, node.variableDeclaration.end); + write(" "); + } emit(node.block); } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index ac889ba9c04..82d194f3cfb 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2128,14 +2128,14 @@ namespace ts { : node; } - export function createCatchClause(variableDeclaration: string | VariableDeclaration, block: Block) { + export function createCatchClause(variableDeclaration: string | VariableDeclaration | undefined, block: Block) { const node = createSynthesizedNode(SyntaxKind.CatchClause); node.variableDeclaration = isString(variableDeclaration) ? createVariableDeclaration(variableDeclaration) : variableDeclaration; node.block = block; return node; } - export function updateCatchClause(node: CatchClause, variableDeclaration: VariableDeclaration, block: Block) { + export function updateCatchClause(node: CatchClause, variableDeclaration: VariableDeclaration | undefined, block: Block) { return node.variableDeclaration !== variableDeclaration || node.block !== block ? updateNode(createCatchClause(variableDeclaration, block), node) @@ -2586,7 +2586,7 @@ namespace ts { } export function addSyntheticLeadingComment(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) { - return setSyntheticLeadingComments(node, append(getSyntheticLeadingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text })); + return setSyntheticLeadingComments(node, append(getSyntheticLeadingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text })); } export function getSyntheticTrailingComments(node: Node): SynthesizedComment[] | undefined { @@ -2600,7 +2600,7 @@ namespace ts { } export function addSyntheticTrailingComment(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) { - return setSyntheticTrailingComments(node, append(getSyntheticTrailingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text })); + return setSyntheticTrailingComments(node, append(getSyntheticTrailingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text })); } /** diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 3258bba6656..e90a1d5b813 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -13,13 +13,32 @@ namespace ts { return compilerOptions.traceResolution && host.trace !== undefined; } - /** - * Result of trying to resolve a module. - * At least one of `ts` and `js` should be defined, or the whole thing should be `undefined`. - */ + /** Array that is only intended to be pushed to, never read. */ + /* @internal */ + export interface Push { + push(value: T): void; + } + + function withPackageId(packageId: PackageId | undefined, r: PathAndExtension | undefined): Resolved { + return r && { path: r.path, extension: r.ext, packageId }; + } + + function noPackageId(r: PathAndExtension | undefined): Resolved { + return withPackageId(/*packageId*/ undefined, r); + } + + /** Result of trying to resolve a module. */ interface Resolved { path: string; extension: Extension; + packageId: PackageId | undefined; + } + + /** Result of trying to resolve a module at a file. Needs to have 'packageId' added later. */ + interface PathAndExtension { + path: string; + // (Use a different name than `extension` to make sure Resolved isn't assignable to PathAndExtension.) + ext: Extension; } /** @@ -43,7 +62,7 @@ namespace ts { function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations { return { - resolvedModule: resolved && { resolvedFileName: resolved.path, extension: resolved.extension, isExternalLibraryImport }, + resolvedModule: resolved && { resolvedFileName: resolved.path, extension: resolved.extension, isExternalLibraryImport, packageId: resolved.packageId }, failedLookupLocations }; } @@ -54,9 +73,16 @@ namespace ts { traceEnabled: boolean; } + interface PackageJson { + name?: string; + version?: string; + typings?: string; + types?: string; + main?: string; + } + /** Reads from "main" or "types"/"typings" depending on `extensions`. */ - function tryReadPackageJsonFields(readTypes: boolean, packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string | undefined { - const jsonContent = readJson(packageJsonPath, state.host); + function tryReadPackageJsonFields(readTypes: boolean, jsonContent: PackageJson, baseDirectory: string, state: ModuleResolutionState): string | undefined { return readTypes ? tryReadFromField("typings") || tryReadFromField("types") : tryReadFromField("main"); function tryReadFromField(fieldName: "typings" | "types" | "main"): string | undefined { @@ -83,7 +109,7 @@ namespace ts { } } - function readJson(path: string, host: ModuleResolutionHost): { typings?: string, types?: string, main?: string } { + function readJson(path: string, host: ModuleResolutionHost): PackageJson { try { const jsonText = host.readFile(path); return jsonText ? JSON.parse(jsonText) : {}; @@ -174,7 +200,10 @@ namespace ts { let resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined; if (resolved) { - resolved = realpath(resolved, host, traceEnabled); + if (!options.preserveSymlinks) { + resolved = realpath(resolved, host, traceEnabled); + } + if (traceEnabled) { trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved, primary); } @@ -646,7 +675,7 @@ namespace ts { if (extension !== undefined) { const path = tryFile(candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state); if (path !== undefined) { - return { path, extension }; + return { path, extension, packageId: undefined }; } } @@ -678,7 +707,7 @@ namespace ts { const { resolvedModule, failedLookupLocations } = nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ts.ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*jsOnly*/ true); if (!resolvedModule) { - throw new Error(`Could not resolve JS module ${moduleName} starting at ${initialDir}. Looked in: ${failedLookupLocations.join(", ")}`); + throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations.join(", ")}`); } return resolvedModule.resolvedFileName; } @@ -708,8 +737,14 @@ namespace ts { trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1, moduleName, Extensions[extensions]); } const resolved = loadModuleFromNodeModules(extensions, moduleName, containingDirectory, failedLookupLocations, state, cache); + if (!resolved) return undefined; + + let resolvedValue = resolved.value; + if (!compilerOptions.preserveSymlinks) { + resolvedValue = resolvedValue && { ...resolved.value, path: realpath(resolved.value.path, host, traceEnabled), extension: resolved.value.extension }; + } // For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files. - return resolved && { value: resolved.value && { resolved: { path: realpath(resolved.value.path, host, traceEnabled), extension: resolved.value.extension }, isExternalLibraryImport: true } }; + return { value: resolvedValue && { resolved: resolvedValue, isExternalLibraryImport: true } }; } else { const candidate = normalizePath(combinePaths(containingDirectory, moduleName)); @@ -747,7 +782,7 @@ namespace ts { } const resolvedFromFile = loadModuleFromFile(extensions, candidate, failedLookupLocations, onlyRecordFailures, state); if (resolvedFromFile) { - return resolvedFromFile; + return noPackageId(resolvedFromFile); } } if (!onlyRecordFailures) { @@ -768,13 +803,15 @@ namespace ts { return !host.directoryExists || host.directoryExists(directoryName); } - export type HasInvalidatedResolution = (sourceFile: Path) => boolean; + function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, failedLookupLocations: Push, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved { + return noPackageId(loadModuleFromFile(extensions, candidate, failedLookupLocations, onlyRecordFailures, state)); + } /** * @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary * 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(extensions: Extensions, candidate: string, failedLookupLocations: Push, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined { + function loadModuleFromFile(extensions: Extensions, candidate: string, failedLookupLocations: Push, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined { // First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts" const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, failedLookupLocations, onlyRecordFailures, state); if (resolvedByAddingExtension) { @@ -794,7 +831,7 @@ namespace ts { } /** Try to return an existing file that adds one of the `extensions` to `candidate`. */ - function tryAddingExtensions(candidate: string, extensions: Extensions, failedLookupLocations: Push, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined { + function tryAddingExtensions(candidate: string, extensions: Extensions, failedLookupLocations: Push, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined { if (!onlyRecordFailures) { // check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing const directory = getDirectoryPath(candidate); @@ -812,9 +849,9 @@ namespace ts { return tryExtension(Extension.Js) || tryExtension(Extension.Jsx); } - function tryExtension(extension: Extension): Resolved | undefined { - const path = tryFile(candidate + extension, failedLookupLocations, onlyRecordFailures, state); - return path && { path, extension }; + function tryExtension(ext: Extension): PathAndExtension | undefined { + const path = tryFile(candidate + ext, failedLookupLocations, onlyRecordFailures, state); + return path && { path, ext }; } } @@ -840,12 +877,23 @@ namespace ts { function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true): Resolved | undefined { const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host); + let packageId: PackageId | undefined; + if (considerPackageJson) { const packageJsonPath = pathToPackageJson(candidate); if (directoryExists && state.host.fileExists(packageJsonPath)) { - const fromPackageJson = loadModuleFromPackageJson(packageJsonPath, extensions, candidate, failedLookupLocations, state); + if (state.traceEnabled) { + trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath); + } + const jsonContent = readJson(packageJsonPath, state.host); + + if (typeof jsonContent.name === "string" && typeof jsonContent.version === "string") { + packageId = { name: jsonContent.name, version: jsonContent.version }; + } + + const fromPackageJson = loadModuleFromPackageJson(jsonContent, extensions, candidate, failedLookupLocations, state); if (fromPackageJson) { - return fromPackageJson; + return withPackageId(packageId, fromPackageJson); } } else { @@ -857,15 +905,11 @@ namespace ts { } } - return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state); + return withPackageId(packageId, loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state)); } - function loadModuleFromPackageJson(packageJsonPath: string, extensions: Extensions, candidate: string, failedLookupLocations: Push, state: ModuleResolutionState): Resolved | undefined { - if (state.traceEnabled) { - trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath); - } - - const file = tryReadPackageJsonFields(extensions !== Extensions.JavaScript, packageJsonPath, candidate, state); + function loadModuleFromPackageJson(jsonContent: PackageJson, extensions: Extensions, candidate: string, failedLookupLocations: Push, state: ModuleResolutionState): PathAndExtension | undefined { + const file = tryReadPackageJsonFields(extensions !== Extensions.JavaScript, jsonContent, candidate, state); if (!file) { return undefined; } @@ -885,13 +929,18 @@ namespace ts { // Even if extensions is DtsOnly, we can still look up a .ts file as a result of package.json "types" const nextExtensions = extensions === Extensions.DtsOnly ? Extensions.TypeScript : extensions; // Don't do package.json lookup recursively, because Node.js' package lookup doesn't. - return nodeLoadModuleByRelativeName(nextExtensions, file, failedLookupLocations, onlyRecordFailures, state, /*considerPackageJson*/ false); + const result = nodeLoadModuleByRelativeName(nextExtensions, file, failedLookupLocations, onlyRecordFailures, state, /*considerPackageJson*/ false); + if (result) { + // It won't have a `packageId` set, because we disabled `considerPackageJson`. + Debug.assert(result.packageId === undefined); + return { path: result.path, ext: result.extension }; + } } /** Resolve from an arbitrarily specified file. Return `undefined` if it has an unsupported extension. */ - function resolvedIfExtensionMatches(extensions: Extensions, path: string): Resolved | undefined { - const extension = tryGetExtensionFromPath(path); - return extension !== undefined && extensionIsOk(extensions, extension) ? { path, extension } : undefined; + function resolvedIfExtensionMatches(extensions: Extensions, path: string): PathAndExtension | undefined { + const ext = tryGetExtensionFromPath(path); + return ext !== undefined && extensionIsOk(extensions, ext) ? { path, ext } : undefined; } /** True if `extension` is one of the supported `extensions`. */ @@ -913,7 +962,7 @@ namespace ts { function loadModuleFromNodeModulesFolder(extensions: Extensions, moduleName: string, nodeModulesFolder: string, nodeModulesFolderExists: boolean, failedLookupLocations: Push, state: ModuleResolutionState): Resolved | undefined { const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName)); - return loadModuleFromFile(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) || + return loadModuleFromFileNoPackageId(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) || loadNodeModuleFromDirectory(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state); } @@ -998,7 +1047,7 @@ namespace ts { if (traceEnabled) { trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache, moduleName); } - return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension } }; + return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId } }; } } @@ -1012,7 +1061,7 @@ namespace ts { return createResolvedModuleWithFailedLookupLocations(resolved && resolved.value, /*isExternalLibraryImport*/ false, failedLookupLocations); function tryResolve(extensions: Extensions): SearchResult { - const resolvedUsingSettings = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loadModuleFromFile, failedLookupLocations, state); + const resolvedUsingSettings = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loadModuleFromFileNoPackageId, failedLookupLocations, state); if (resolvedUsingSettings) { return { value: resolvedUsingSettings }; } @@ -1026,7 +1075,7 @@ namespace ts { return resolutionFromCache; } const searchName = normalizePath(combinePaths(directory, moduleName)); - return toSearchResult(loadModuleFromFile(extensions, searchName, failedLookupLocations, /*onlyRecordFailures*/ false, state)); + return toSearchResult(loadModuleFromFileNoPackageId(extensions, searchName, failedLookupLocations, /*onlyRecordFailures*/ false, state)); }); if (resolved) { return resolved; @@ -1038,7 +1087,7 @@ namespace ts { } else { const candidate = normalizePath(combinePaths(containingDirectory, moduleName)); - return toSearchResult(loadModuleFromFile(extensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state)); + return toSearchResult(loadModuleFromFileNoPackageId(extensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state)); } } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 49b682c7302..a2fe752ad18 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3202,7 +3202,7 @@ namespace ts { return undefined; } - const isAsync = !!(getModifierFlags(arrowFunction) & ModifierFlags.Async); + const isAsync = hasModifier(arrowFunction, ModifierFlags.Async); // If we have an arrow, then try to parse the body. Even if not, try to parse if we // have an opening brace, just in case we're in an error state. @@ -3382,7 +3382,7 @@ namespace ts { function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction { const node = createNode(SyntaxKind.ArrowFunction); node.modifiers = parseModifiersForArrowFunction(); - const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; + const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; // Arrow functions are never generators. // @@ -4515,7 +4515,7 @@ namespace ts { node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None; - const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; + const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; node.name = isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) : isGenerator ? doInYieldContext(parseOptionalIdentifier) : @@ -4799,11 +4799,16 @@ namespace ts { function parseCatchClause(): CatchClause { const result = createNode(SyntaxKind.CatchClause); parseExpected(SyntaxKind.CatchKeyword); - if (parseExpected(SyntaxKind.OpenParenToken)) { + + if (parseOptional(SyntaxKind.OpenParenToken)) { result.variableDeclaration = parseVariableDeclaration(); + parseExpected(SyntaxKind.CloseParenToken); + } + else { + // Keep shape of node to avoid degrading performance. + result.variableDeclaration = undefined; } - parseExpected(SyntaxKind.CloseParenToken); result.block = parseBlock(/*ignoreMissingOpenBrace*/ false); return finishNode(result); } @@ -6164,7 +6169,7 @@ namespace ts { export function parseIsolatedJSDocComment(content: string, start: number, length: number): { jsDoc: JSDoc, diagnostics: Diagnostic[] } | undefined { initializeState(content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); - sourceFile = { languageVariant: LanguageVariant.Standard, text: content }; + sourceFile = { languageVariant: LanguageVariant.Standard, text: content }; // tslint:disable-line no-object-literal-type-assertion const jsDoc = parseJSDocCommentWorker(start, length); const diagnostics = parseDiagnostics; clearState(); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 58931379d7c..142d6351a46 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -456,51 +456,66 @@ namespace ts { /** * Updates the existing missing file watches with the new set of missing files after new program is created */ - export function updateMissingFilePathsWatch(program: Program, existingMap: Map, + export function updateMissingFilePathsWatch( + program: Program, + missingFileWatches: Map, createMissingFileWatch: (missingFilePath: Path) => FileWatcher, - closeExistingMissingFilePathFileWatcher: (missingFilePath: Path, fileWatcher: FileWatcher) => void) { - + closeExistingMissingFilePathFileWatcher: (missingFilePath: Path, fileWatcher: FileWatcher) => void + ) { const missingFilePaths = program.getMissingFilePaths(); const newMissingFilePathMap = arrayToSet(missingFilePaths); // Update the missing file paths watcher - return mutateExistingMapWithNewSet( - existingMap, newMissingFilePathMap, - // Watch the missing files - createMissingFileWatch, - // Files that are no longer missing (e.g. because they are no longer required) - // should no longer be watched. - closeExistingMissingFilePathFileWatcher + mutateMap( + missingFileWatches, + newMissingFilePathMap, + { + // Watch the missing files + createNewValue: createMissingFileWatch, + // Files that are no longer missing (e.g. because they are no longer required) + // should no longer be watched. + onDeleteExistingValue: closeExistingMissingFilePathFileWatcher + } ); } - export type WildCardDirectoryWatchers = { watcher: FileWatcher, recursive: boolean }; + export interface WildcardDirectoryWatchers { + watcher: FileWatcher; + flags: WatchDirectoryFlags; + } /** * Updates the existing wild card directory watcyhes with the new set of wild card directories from the config file after new program is created */ - export function updateWatchingWildcardDirectories(existingWatchedForWildcards: Map, wildcardDirectories: Map, - watchDirectory: (directory: string, recursive: boolean) => FileWatcher, - closeDirectoryWatcher: (directory: string, watcher: FileWatcher, recursive: boolean, recursiveChanged: boolean) => void) { - return mutateExistingMap( - existingWatchedForWildcards, wildcardDirectories, - // Create new watch and recursive info - (directory, flag) => { - const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0; - return { - watcher: watchDirectory(directory, recursive), - recursive - }; - }, - // Close existing watch thats not needed any more - (directory, { watcher, recursive }) => closeDirectoryWatcher(directory, watcher, recursive, /*recursiveChanged*/ false), - // Watcher is same if the recursive flags match - ({ recursive: existingRecursive }, flag) => { - // If the recursive dont match, it needs update - const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0; - return existingRecursive !== recursive; - }, - // Close existing watch that doesnt match in recursive flag - (directory, { watcher, recursive }) => closeDirectoryWatcher(directory, watcher, recursive, /*recursiveChanged*/ true) + export function updateWatchingWildcardDirectories( + existingWatchedForWildcards: Map, + wildcardDirectories: Map, + watchDirectory: (directory: string, flags: WatchDirectoryFlags) => FileWatcher, + closeDirectoryWatcher: (directory: string, wildcardDirectoryWatcher: WildcardDirectoryWatchers, flagsChanged: boolean) => void + ) { + mutateMap( + existingWatchedForWildcards, + wildcardDirectories, + { + // Create new watch and recursive info + createNewValue: (directory, flags) => { + return { + watcher: watchDirectory(directory, flags), + flags + }; + }, + // Close existing watch thats not needed any more + onDeleteExistingValue: (directory, wildcardDirectoryWatcher) => + closeDirectoryWatcher(directory, wildcardDirectoryWatcher, /*flagsChanged*/ false), + // Close existing watch that doesnt match in the flags + shouldDeleteExistingValue: (directory, wildcardDirectoryWatcher, flags) => { + // Watcher is same if the recursive flags match + if (wildcardDirectoryWatcher.flags === flags) { + return false; + } + closeDirectoryWatcher(directory, wildcardDirectoryWatcher, /*flagsChanged*/ true); + return true; + } + } ); } @@ -591,6 +606,15 @@ namespace ts { resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(checkAllDefined(typeReferenceDirectiveNames), containingFile, loader); } + // Map from a stringified PackageId to the source file with that id. + // Only one source file may have a given packageId. Others become redirects (see createRedirectSourceFile). + // `packageIdToSourceFile` is only used while building the program, while `sourceFileToPackageName` and `isSourceFileTargetOfRedirect` are kept around. + const packageIdToSourceFile = createMap(); + // Maps from a SourceFile's `.path` to the name of the package it was imported with. + let sourceFileToPackageName = createMap(); + // See `sourceFileIsRedirectedTo`. + let redirectTargetsSet = createMap(); + const filesByName = createMap(); let missingFilePaths: Path[]; // stores 'filename -> file association' ignoring case @@ -680,6 +704,8 @@ namespace ts { isSourceFileFromExternalLibrary, dropDiagnosticsProducingTypeChecker, getSourceFileFromReference, + sourceFileToPackageName, + redirectTargetsSet, }; verifyCompilerOptions(); @@ -912,8 +938,12 @@ namespace ts { return oldProgram.structureIsReused = StructureIsReused.Not; } - for (const oldSourceFile of oldProgram.getSourceFiles()) { - const newSourceFile = host.getSourceFileByPath + const oldSourceFiles = oldProgram.getSourceFiles(); + const enum SeenPackageName { Exists, Modified } + const seenPackageNames = createMap(); + + for (const oldSourceFile of oldSourceFiles) { + let newSourceFile = host.getSourceFileByPath ? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target, /*onError*/ undefined, shouldCreateNewSourceFile) : host.getSourceFile(oldSourceFile.fileName, options.target, /*onError*/ undefined, shouldCreateNewSourceFile); @@ -921,10 +951,46 @@ namespace ts { return oldProgram.structureIsReused = StructureIsReused.Not; } + Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`"); + + let fileChanged: boolean; + if (oldSourceFile.redirectInfo) { + // We got `newSourceFile` by path, so it is actually for the unredirected file. + // This lets us know if the unredirected file has changed. If it has we should break the redirect. + if (newSourceFile !== oldSourceFile.redirectInfo.unredirected) { + // Underlying file has changed. Might not redirect anymore. Must rebuild program. + return oldProgram.structureIsReused = StructureIsReused.Not; + } + fileChanged = false; + newSourceFile = oldSourceFile; // Use the redirect. + } + else if (oldProgram.redirectTargetsSet.has(oldSourceFile.path)) { + // If a redirected-to source file changes, the redirect may be broken. + if (newSourceFile !== oldSourceFile) { + return oldProgram.structureIsReused = StructureIsReused.Not; + } + fileChanged = false; + } + else { + fileChanged = newSourceFile !== oldSourceFile; + } + newSourceFile.path = oldSourceFile.path; filePaths.push(newSourceFile.path); - if (oldSourceFile !== newSourceFile) { + const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path); + if (packageName !== undefined) { + // If there are 2 different source files for the same package name and at least one of them changes, + // they might become redirects. So we must rebuild the program. + const prevKind = seenPackageNames.get(packageName); + const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists; + if ((prevKind !== undefined && newKind === SeenPackageName.Modified) || prevKind === SeenPackageName.Modified) { + return oldProgram.structureIsReused = StructureIsReused.Not; + } + seenPackageNames.set(packageName, newKind); + } + + if (fileChanged) { // The `newSourceFile` object was created for the new program. if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) { @@ -1030,6 +1096,9 @@ namespace ts { } resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives(); + sourceFileToPackageName = oldProgram.sourceFileToPackageName; + redirectTargetsSet = oldProgram.redirectTargetsSet; + return oldProgram.structureIsReused = StructureIsReused.Completely; } @@ -1670,7 +1739,7 @@ namespace ts { /** This has side effects through `findSourceFile`. */ function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): void { getSourceFileFromReferenceWorker(fileName, - fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd), + fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd, /*packageId*/ undefined), (diagnostic, ...args) => { fileProcessingDiagnostics.add(refFile !== undefined && refEnd !== undefined && refPos !== undefined ? createFileDiagnostic(refFile, refPos, refEnd - refPos, diagnostic, ...args) @@ -1689,8 +1758,26 @@ namespace ts { } } + function createRedirectSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path): SourceFile { + const redirect: SourceFile = Object.create(redirectTarget); + redirect.fileName = fileName; + redirect.path = path; + redirect.redirectInfo = { redirectTarget, unredirected }; + Object.defineProperties(redirect, { + id: { + get(this: SourceFile) { return this.redirectInfo.redirectTarget.id; }, + set(this: SourceFile, value: SourceFile["id"]) { this.redirectInfo.redirectTarget.id = value; }, + }, + symbol: { + get(this: SourceFile) { return this.redirectInfo.redirectTarget.symbol; }, + set(this: SourceFile, value: SourceFile["symbol"]) { this.redirectInfo.redirectTarget.symbol = value; }, + }, + }); + return redirect; + } + // Get source file from normalized fileName - function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile { + function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile: SourceFile, refPos: number, refEnd: number, packageId: PackageId | undefined): SourceFile | undefined { if (filesByName.has(path)) { const file = filesByName.get(path); // try to check if we've already seen this file but with a different casing in path @@ -1733,6 +1820,26 @@ namespace ts { } }, shouldCreateNewSourceFile); + if (packageId) { + const packageIdKey = `${packageId.name}@${packageId.version}`; + const fileFromPackageId = packageIdToSourceFile.get(packageIdKey); + if (fileFromPackageId) { + // Some other SourceFile already exists with this package name and version. + // Instead of creating a duplicate, just redirect to the existing one. + const dupFile = createRedirectSourceFile(fileFromPackageId, file, fileName, path); + redirectTargetsSet.set(fileFromPackageId.path, true); + filesByName.set(path, dupFile); + sourceFileToPackageName.set(path, packageId.name); + files.push(dupFile); + return dupFile; + } + else if (file) { + // This is the first source file to have this packageId. + packageIdToSourceFile.set(packageIdKey, file); + sourceFileToPackageName.set(path, packageId.name); + } + } + filesByName.set(path, file); if (file) { sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); @@ -1895,7 +2002,7 @@ namespace ts { else if (shouldAddFile) { const path = toPath(resolvedFileName); const pos = skipTrivia(file.text, file.imports[i].pos); - findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, file, pos, file.imports[i].end); + findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, file, pos, file.imports[i].end, resolution.packageId); } if (isFromNodeModulesSearch) { diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 8533ec95f3d..193997a8a86 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -153,7 +153,7 @@ namespace ts { return; } watcher = _fs.watch( - dirPath, + dirPath || ".", { persistent: true }, (eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath) ); diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 45ad53ffbed..2249e577312 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -331,11 +331,14 @@ namespace ts { location ); } - else if (numElements !== 1 && (flattenContext.level < FlattenLevel.ObjectRest || numElements === 0)) { + else if (numElements !== 1 && (flattenContext.level < FlattenLevel.ObjectRest || numElements === 0) + || every(elements, isOmittedExpression)) { // For anything other than a single-element destructuring we need to generate a temporary // to ensure value is evaluated exactly once. Additionally, if we have zero elements // we need to emit *something* to ensure that in case a 'var' keyword was already emitted, // so in that case, we'll intentionally create that temporary. + // Or all the elements of the binding pattern are omitted expression such as "var [,] = [1,2]", + // then we will create temporary variable. const reuseIdentifierExpressions = !isDeclarationBindingElement(parent) || numElements !== 0; value = ensureIdentifier(flattenContext, value, reuseIdentifierExpressions, location); } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index f97de018d4a..2e148280369 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -394,7 +394,7 @@ namespace ts { function shouldVisitNode(node: Node): boolean { return (node.transformFlags & TransformFlags.ContainsES2015) !== 0 || convertedLoopState !== undefined - || (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && isStatement(node)) + || (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && (isStatement(node) || (node.kind === SyntaxKind.Block))) || (isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatementBody(node)) || isTypeScriptClassWrapper(node); } @@ -2105,13 +2105,14 @@ namespace ts { setCommentRange(declarationList, node); if (node.transformFlags & TransformFlags.ContainsBindingPattern - && (isBindingPattern(node.declarations[0].name) - || isBindingPattern(lastOrUndefined(node.declarations).name))) { + && (isBindingPattern(node.declarations[0].name) || isBindingPattern(lastOrUndefined(node.declarations).name))) { // If the first or last declaration is a binding pattern, we need to modify // the source map range for the declaration list. const firstDeclaration = firstOrUndefined(declarations); - const lastDeclaration = lastOrUndefined(declarations); - setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end)); + if (firstDeclaration) { + const lastDeclaration = lastOrUndefined(declarations); + setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end)); + } } return declarationList; @@ -2491,7 +2492,7 @@ namespace ts { const catchVariable = getGeneratedNameForNode(errorRecord); const returnMethod = createTempVariable(/*recordTempVariable*/ undefined); const values = createValuesHelper(context, expression, node.expression); - const next = createCall(createPropertyAccess(iterator, "next" ), /*typeArguments*/ undefined, []); + const next = createCall(createPropertyAccess(iterator, "next"), /*typeArguments*/ undefined, []); hoistVariableDeclaration(errorRecord); hoistVariableDeclaration(returnMethod); @@ -3173,6 +3174,7 @@ namespace ts { function visitCatchClause(node: CatchClause): CatchClause { const ancestorFacts = enterSubtree(HierarchyFacts.BlockScopeExcludes, HierarchyFacts.BlockScopeIncludes); let updated: CatchClause; + Debug.assert(!!node.variableDeclaration, "Catch clause variable should always be present when downleveling ES2015."); if (isBindingPattern(node.variableDeclaration.name)) { const temp = createTempVariable(/*recordTempVariable*/ undefined); const newVariableDeclaration = createVariableDeclaration(temp); diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 3d884001932..8d71016b051 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -101,6 +101,8 @@ namespace ts { return visitExpressionStatement(node as ExpressionStatement); case SyntaxKind.ParenthesizedExpression: return visitParenthesizedExpression(node as ParenthesizedExpression, noDestructuringValue); + case SyntaxKind.CatchClause: + return visitCatchClause(node as CatchClause); default: return visitEachChild(node, visitor, context); } @@ -212,6 +214,17 @@ namespace ts { return visitEachChild(node, noDestructuringValue ? visitorNoDestructuringValue : visitor, context); } + function visitCatchClause(node: CatchClause): CatchClause { + if (!node.variableDeclaration) { + return updateCatchClause( + node, + createVariableDeclaration(createTempVariable(/*recordTempVariable*/ undefined)), + visitNode(node.block, visitor, isBlock) + ); + } + return visitEachChild(node, visitor, context); + } + /** * Visits a BinaryExpression that contains a destructuring assignment. * diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 41edf24fe9e..f45147b526c 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -164,12 +164,11 @@ namespace ts { } // A generated code block - interface CodeBlock { - kind: CodeBlockKind; - } + type CodeBlock = | ExceptionBlock | LabeledBlock | SwitchBlock | LoopBlock | WithBlock; // a generated exception block, used for 'try' statements - interface ExceptionBlock extends CodeBlock { + interface ExceptionBlock { + kind: CodeBlockKind.Exception; state: ExceptionBlockState; startLabel: Label; catchVariable?: Identifier; @@ -179,27 +178,31 @@ namespace ts { } // A generated code that tracks the target for 'break' statements in a LabeledStatement. - interface LabeledBlock extends CodeBlock { + interface LabeledBlock { + kind: CodeBlockKind.Labeled; labelText: string; isScript: boolean; breakLabel: Label; } // a generated block that tracks the target for 'break' statements in a 'switch' statement - interface SwitchBlock extends CodeBlock { + interface SwitchBlock { + kind: CodeBlockKind.Switch; isScript: boolean; breakLabel: Label; } // a generated block that tracks the targets for 'break' and 'continue' statements, used for iteration statements - interface LoopBlock extends CodeBlock { + interface LoopBlock { + kind: CodeBlockKind.Loop; continueLabel: Label; isScript: boolean; breakLabel: Label; } // a generated block associated with a 'with' statement - interface WithBlock extends CodeBlock { + interface WithBlock { + kind: CodeBlockKind.With; expression: Identifier; startLabel: Label; endLabel: Label; @@ -2070,7 +2073,7 @@ namespace ts { const startLabel = defineLabel(); const endLabel = defineLabel(); markLabel(startLabel); - beginBlock({ + beginBlock({ kind: CodeBlockKind.With, expression, startLabel, @@ -2087,10 +2090,6 @@ namespace ts { markLabel(block.endLabel); } - function isWithBlock(block: CodeBlock): block is WithBlock { - return block.kind === CodeBlockKind.With; - } - /** * Begins a code block for a generated `try` statement. */ @@ -2098,7 +2097,7 @@ namespace ts { const startLabel = defineLabel(); const endLabel = defineLabel(); markLabel(startLabel); - beginBlock({ + beginBlock({ kind: CodeBlockKind.Exception, state: ExceptionBlockState.Try, startLabel, @@ -2188,10 +2187,6 @@ namespace ts { exception.state = ExceptionBlockState.Done; } - function isExceptionBlock(block: CodeBlock): block is ExceptionBlock { - return block.kind === CodeBlockKind.Exception; - } - /** * Begins a code block that supports `break` or `continue` statements that are defined in * the source tree and not from generated code. @@ -2199,7 +2194,7 @@ namespace ts { * @param labelText Names from containing labeled statements. */ function beginScriptLoopBlock(): void { - beginBlock({ + beginBlock({ kind: CodeBlockKind.Loop, isScript: true, breakLabel: -1, @@ -2217,7 +2212,7 @@ namespace ts { */ function beginLoopBlock(continueLabel: Label): Label { const breakLabel = defineLabel(); - beginBlock({ + beginBlock({ kind: CodeBlockKind.Loop, isScript: false, breakLabel, @@ -2245,7 +2240,7 @@ namespace ts { * */ function beginScriptSwitchBlock(): void { - beginBlock({ + beginBlock({ kind: CodeBlockKind.Switch, isScript: true, breakLabel: -1 @@ -2259,7 +2254,7 @@ namespace ts { */ function beginSwitchBlock(): Label { const breakLabel = defineLabel(); - beginBlock({ + beginBlock({ kind: CodeBlockKind.Switch, isScript: false, breakLabel, @@ -2280,7 +2275,7 @@ namespace ts { } function beginScriptLabeledBlock(labelText: string) { - beginBlock({ + beginBlock({ kind: CodeBlockKind.Labeled, isScript: true, labelText, @@ -2290,7 +2285,7 @@ namespace ts { function beginLabeledBlock(labelText: string) { const breakLabel = defineLabel(); - beginBlock({ + beginBlock({ kind: CodeBlockKind.Labeled, isScript: false, labelText, @@ -2448,7 +2443,7 @@ namespace ts { * @param location An optional source map location for the statement. */ function createInlineBreak(label: Label, location?: TextRange): ReturnStatement { - Debug.assert(label > 0, `Invalid label: ${label}`); + Debug.assertLessThan(0, label, "Invalid label"); return setTextRange( createReturn( createArrayLiteral([ @@ -2878,34 +2873,37 @@ namespace ts { for (; blockIndex < blockActions.length && blockOffsets[blockIndex] <= operationIndex; blockIndex++) { const block = blocks[blockIndex]; const blockAction = blockActions[blockIndex]; - if (isExceptionBlock(block)) { - if (blockAction === BlockAction.Open) { - if (!exceptionBlockStack) { - exceptionBlockStack = []; - } + switch (block.kind) { + case CodeBlockKind.Exception: + if (blockAction === BlockAction.Open) { + if (!exceptionBlockStack) { + exceptionBlockStack = []; + } - if (!statements) { - statements = []; - } + if (!statements) { + statements = []; + } - exceptionBlockStack.push(currentExceptionBlock); - currentExceptionBlock = block; - } - else if (blockAction === BlockAction.Close) { - currentExceptionBlock = exceptionBlockStack.pop(); - } - } - else if (isWithBlock(block)) { - if (blockAction === BlockAction.Open) { - if (!withBlockStack) { - withBlockStack = []; + exceptionBlockStack.push(currentExceptionBlock); + currentExceptionBlock = block; } + else if (blockAction === BlockAction.Close) { + currentExceptionBlock = exceptionBlockStack.pop(); + } + break; + case CodeBlockKind.With: + if (blockAction === BlockAction.Open) { + if (!withBlockStack) { + withBlockStack = []; + } - withBlockStack.push(block); - } - else if (blockAction === BlockAction.Close) { - withBlockStack.pop(); - } + withBlockStack.push(block); + } + else if (blockAction === BlockAction.Close) { + withBlockStack.pop(); + } + break; + // default: do nothing } } } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index d5bf20a525d..0d00f4d48a3 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -114,7 +114,7 @@ namespace ts { compilerOptions.reactNamespace, tagName, objectProperties, - filter(map(children, transformJsxChildToExpression), isDefined), + mapDefined(children, transformJsxChildToExpression), node, location ); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c1ab964af30..e4ea722df3d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -999,6 +999,7 @@ namespace ts { export interface StringLiteral extends LiteralExpression { kind: SyntaxKind.StringLiteral; /* @internal */ textSourceNode?: Identifier | StringLiteral | NumericLiteral; // Allows a StringLiteral to get its text from another node (used by transforms). + /* @internal */ singleQuote?: boolean; } // Note: 'brands' in our syntax nodes serve to give us a small amount of nominal typing. @@ -1808,7 +1809,7 @@ namespace ts { export interface CatchClause extends Node { kind: SyntaxKind.CatchClause; parent?: TryStatement; - variableDeclaration: VariableDeclaration; + variableDeclaration?: VariableDeclaration; block: Block; } @@ -2176,16 +2177,18 @@ namespace ts { locked?: boolean; } - export interface AfterFinallyFlow extends FlowNode, FlowLock { + export interface AfterFinallyFlow extends FlowNodeBase, FlowLock { antecedent: FlowNode; } - export interface PreFinallyFlow extends FlowNode { + export interface PreFinallyFlow extends FlowNodeBase { antecedent: FlowNode; lock: FlowLock; } - export interface FlowNode { + export type FlowNode = + | AfterFinallyFlow | PreFinallyFlow | FlowStart | FlowLabel | FlowAssignment | FlowCondition | FlowSwitchClause | FlowArrayMutation; + export interface FlowNodeBase { flags: FlowFlags; id?: number; // Node id used by flow type cache in checker } @@ -2193,30 +2196,30 @@ namespace ts { // 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 { + export interface FlowStart extends FlowNodeBase { container?: FunctionExpression | ArrowFunction | MethodDeclaration; } // FlowLabel represents a junction with multiple possible preceding control flows. - export interface FlowLabel extends FlowNode { + export interface FlowLabel extends FlowNodeBase { antecedents: FlowNode[]; } // FlowAssignment represents a node that assigns a value to a narrowable reference, // i.e. an identifier or a dotted name that starts with an identifier or 'this'. - export interface FlowAssignment extends FlowNode { + export interface FlowAssignment extends FlowNodeBase { node: Expression | VariableDeclaration | BindingElement; antecedent: FlowNode; } // FlowCondition represents a condition that is known to be true or false at the // node's location in the control flow. - export interface FlowCondition extends FlowNode { + export interface FlowCondition extends FlowNodeBase { expression: Expression; antecedent: FlowNode; } - export interface FlowSwitchClause extends FlowNode { + export interface FlowSwitchClause extends FlowNodeBase { switchStatement: SwitchStatement; clauseStart: number; // Start index of case/default clause range clauseEnd: number; // End index of case/default clause range @@ -2225,7 +2228,7 @@ namespace ts { // FlowArrayMutation represents a node potentially mutates an array, i.e. an // operation of the form 'x.push(value)', 'x.unshift(value)' or 'x[n] = value'. - export interface FlowArrayMutation extends FlowNode { + export interface FlowArrayMutation extends FlowNodeBase { node: CallExpression | BinaryExpression; antecedent: FlowNode; } @@ -2255,6 +2258,17 @@ namespace ts { } + /* @internal */ + export interface RedirectInfo { + /** Source file this redirects to. */ + readonly redirectTarget: SourceFile; + /** + * Source file for the duplicate package. This will not be used by the Program, + * but we need to keep this around so we can watch for changes in underlying. + */ + readonly unredirected: SourceFile; + } + // Source files are declarations when they are external modules. export interface SourceFile extends Declaration { kind: SyntaxKind.SourceFile; @@ -2265,6 +2279,13 @@ namespace ts { /* @internal */ path: Path; text: string; + /** + * If two source files are for the same version of the same package, one will redirect to the other. + * (See `createRedirectSourceFile` in program.ts.) + * The redirect will have this set. The other will not have anything set, but see Program#sourceFileIsRedirectedTo. + */ + /* @internal */ redirectInfo?: RedirectInfo | undefined; + amdDependencies: AmdDependency[]; moduleName: string; referencedFiles: FileReference[]; @@ -2436,6 +2457,11 @@ namespace ts { /* @internal */ structureIsReused?: StructureIsReused; /* @internal */ getSourceFileFromReference(referencingFile: SourceFile, ref: FileReference): SourceFile | undefined; + + /** Given a source file, get the name of the package it was imported from. */ + /* @internal */ sourceFileToPackageName: Map; + /** Set of all source files that some other source file redirects to. */ + /* @internal */ redirectTargetsSet: Map; } /* @internal */ @@ -2607,9 +2633,8 @@ namespace ts { * Does not include properties of primitive types. */ /* @internal */ getAllPossiblePropertiesOfType(type: Type): Symbol[]; - + /* @internal */ resolveName(name: string, location: Node, meaning: SymbolFlags): Symbol | undefined; /* @internal */ getJsxNamespace(): string; - /* @internal */ resolveNameAtLocation(location: Node, name: string, meaning: SymbolFlags): Symbol | undefined; } export enum NodeBuilderFlags { @@ -3068,6 +3093,7 @@ namespace ts { hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element + resolvedJsxElementAllAttributesType?: Type; // resolved all element attributes type of a JSX openinglike element hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt. superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing switchTypes?: Type[]; // Cached array of switch case expression types @@ -3318,6 +3344,11 @@ namespace ts { awaitedTypeOfType?: Type; } + /* @internal */ + export interface SyntheticDefaultModuleType extends Type { + syntheticType?: Type; + } + export interface TypeVariable extends Type { /* @internal */ resolvedBaseConstraint: Type; @@ -3426,11 +3457,29 @@ namespace ts { AnyDefault = 1 << 2, // Infer anyType for no inferences (otherwise emptyObjectType) } + /** + * Ternary values are defined such that + * x & y is False if either x or y is False. + * x & y is Maybe if either x or y is Maybe, but neither x or y is False. + * x & y is True if both x and y are True. + * x | y is False if both x and y are False. + * x | y is Maybe if either x or y is Maybe, but neither x or y is True. + * x | y is True if either x or y is True. + */ + export const enum Ternary { + False = 0, + Maybe = 1, + True = -1 + } + + export type TypeComparer = (s: Type, t: Type, reportErrors?: boolean) => Ternary; + /* @internal */ export interface InferenceContext extends TypeMapper { signature: Signature; // Generic signature for which inferences are made inferences: InferenceInfo[]; // Inferences made for each type parameter flags: InferenceFlags; // Inference flags + compareTypes: TypeComparer; // Type comparer function } /* @internal */ @@ -3560,6 +3609,7 @@ namespace ts { paths?: MapLike; /*@internal*/ plugins?: PluginImport[]; preserveConstEnums?: boolean; + preserveSymlinks?: boolean; project?: string; /* @internal */ pretty?: DiagnosticStyle; reactNamespace?: string; @@ -3689,7 +3739,13 @@ namespace ts { export interface ConfigFileSpecs { filesSpecs: ReadonlyArray; + /** + * Present to report errors (user specified specs), validatedIncludeSpecs are used for file name matching + */ includeSpecs: ReadonlyArray; + /** + * Present to report errors (user specified specs), validatedExcludeSpecs are used for file name matching + */ excludeSpecs: ReadonlyArray; validatedIncludeSpecs: ReadonlyArray; validatedExcludeSpecs: ReadonlyArray; @@ -3886,6 +3942,10 @@ namespace ts { readFile(fileName: string): string | undefined; trace?(s: string): void; directoryExists?(directoryName: string): boolean; + /** + * Resolve a symbolic link. + * @see https://nodejs.org/api/fs.html#fs_fs_realpathsync_path_options + */ realpath?(path: string): string; getCurrentDirectory?(): string; getDirectories?(path: string): string[]; @@ -3913,6 +3973,7 @@ namespace ts { /** * ResolvedModule with an explicitly provided `extension` property. * Prefer this over `ResolvedModule`. + * If changing this, remember to change `moduleResolutionIsEqualTo`. */ export interface ResolvedModuleFull extends ResolvedModule { /** @@ -3920,6 +3981,22 @@ namespace ts { * This is optional for backwards-compatibility, but will be added if not provided. */ extension: Extension; + packageId?: PackageId; + } + + /** + * Unique identifier with a package name and version. + * If changing this, remember to change `packageIdIsEqual`. + */ + export interface PackageId { + /** + * Name of the package. + * Should not include `@types`. + * If accessing a non-index file, this should include its name e.g. "foo/bar". + */ + name: string; + /** Version of the package, e.g. "1.2.3" */ + version: string; } export const enum Extension { @@ -3931,9 +4008,9 @@ namespace ts { } export interface ResolvedModuleWithFailedLookupLocations { - resolvedModule: ResolvedModuleFull | undefined; + readonly resolvedModule: ResolvedModuleFull | undefined; /* @internal */ - failedLookupLocations: string[]; + readonly failedLookupLocations: string[]; /*@internal*/ isInvalidated?: boolean; } @@ -3946,12 +4023,16 @@ namespace ts { } export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations { - resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective; - failedLookupLocations: string[]; + readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective; + readonly failedLookupLocations: string[]; /*@internal*/ isInvalidated?: boolean; } + export interface HasInvalidatedResolution { + (sourceFile: Path): boolean; + } + export interface CompilerHost extends ModuleResolutionHost { getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile; getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile; @@ -4019,7 +4100,6 @@ namespace ts { ContainsBindingPattern = 1 << 23, ContainsYield = 1 << 24, ContainsHoistedDeclarationOrCompletion = 1 << 25, - ContainsDynamicImport = 1 << 26, // Please leave this as 1 << 29. diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index bea28ed2702..3f0fae850d6 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -27,20 +27,6 @@ namespace ts { return undefined; } - export function findDeclaration(symbol: Symbol, predicate: (node: Declaration) => node is T): T | undefined; - export function findDeclaration(symbol: Symbol, predicate: (node: Declaration) => boolean): Declaration | undefined; - export function findDeclaration(symbol: Symbol, predicate: (node: Declaration) => boolean): Declaration | undefined { - const declarations = symbol.declarations; - if (declarations) { - for (const declaration of declarations) { - if (predicate(declaration)) { - return declaration; - } - } - } - return undefined; - } - export interface StringSymbolWriter extends SymbolWriter { string(): string; } @@ -112,19 +98,21 @@ namespace ts { sourceFile.resolvedTypeReferenceDirectiveNames.set(typeReferenceDirectiveName, resolvedTypeReferenceDirective); } - /* @internal */ export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleFull, newResolution: ResolvedModuleFull): boolean { return oldResolution.isExternalLibraryImport === newResolution.isExternalLibraryImport && oldResolution.extension === newResolution.extension && - oldResolution.resolvedFileName === newResolution.resolvedFileName; + oldResolution.resolvedFileName === newResolution.resolvedFileName && + packageIdIsEqual(oldResolution.packageId, newResolution.packageId); + } + + function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean { + return a === b || a && b && a.name === b.name && a.version === b.version; } - /* @internal */ export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean { return oldResolution.resolvedFileName === newResolution.resolvedFileName && oldResolution.primary === newResolution.primary; } - /* @internal */ export function hasChangesInResolutions( names: ReadonlyArray, newResolutions: ReadonlyArray, @@ -203,14 +191,6 @@ namespace ts { return `${file.fileName}(${loc.line + 1},${loc.character + 1})`; } - export function getStartPosOfNode(node: Node): number { - return node.pos; - } - - export function isDefined(value: any): boolean { - return value !== undefined; - } - export function getEndLinePosition(line: number, sourceFile: SourceFileLike): number { Debug.assert(line >= 0); const lineStarts = getLineStarts(sourceFile); @@ -262,6 +242,32 @@ namespace ts { return !nodeIsMissing(node); } + /** + * Determine if the given comment is a triple-slash + * + * @return true if the comment is a triple-slash comment else false + */ + export function isRecognizedTripleSlashComment(text: string, commentPos: number, commentEnd: number) { + // Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text + // so that we don't end up computing comment string and doing match for all // comments + if (text.charCodeAt(commentPos + 1) === CharacterCodes.slash && + commentPos + 2 < commentEnd && + text.charCodeAt(commentPos + 2) === CharacterCodes.slash) { + const textSubStr = text.substring(commentPos, commentEnd); + return textSubStr.match(fullTripleSlashReferencePathRegEx) || + textSubStr.match(fullTripleSlashAMDReferencePathRegEx) || + textSubStr.match(fullTripleSlashReferenceTypeReferenceDirectiveRegEx) || + textSubStr.match(defaultLibReferenceRegEx) ? + true : false; + } + return false; + } + + export function isPinnedComment(text: string, comment: CommentRange) { + return text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk && + text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation; + } + export function getTokenPosOfNode(node: Node, sourceFile?: SourceFileLike, includeJsDoc?: 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. @@ -338,15 +344,20 @@ namespace ts { // or a (possibly escaped) quoted form of the original text if it's string-like. switch (node.kind) { case SyntaxKind.StringLiteral: - return '"' + escapeText(node.text) + '"'; + if ((node).singleQuote) { + return "'" + escapeText(node.text, CharacterCodes.singleQuote) + "'"; + } + else { + return '"' + escapeText(node.text, CharacterCodes.doubleQuote) + '"'; + } case SyntaxKind.NoSubstitutionTemplateLiteral: - return "`" + escapeText(node.text) + "`"; + return "`" + escapeText(node.text, CharacterCodes.backtick) + "`"; case SyntaxKind.TemplateHead: - return "`" + escapeText(node.text) + "${"; + return "`" + escapeText(node.text, CharacterCodes.backtick) + "${"; case SyntaxKind.TemplateMiddle: - return "}" + escapeText(node.text) + "${"; + return "}" + escapeText(node.text, CharacterCodes.backtick) + "${"; case SyntaxKind.TemplateTail: - return "}" + escapeText(node.text) + "`"; + return "}" + escapeText(node.text, CharacterCodes.backtick) + "`"; case SyntaxKind.NumericLiteral: return node.text; } @@ -437,6 +448,7 @@ namespace ts { return isExternalModule(node) || compilerOptions.isolatedModules; } + /* @internal */ export function isBlockScope(node: Node, parentNode: Node) { switch (node.kind) { case SyntaxKind.SourceFile: @@ -638,10 +650,6 @@ namespace ts { return getLeadingCommentRanges(sourceFileOfNode.text, node.pos); } - export function getLeadingCommentRangesOfNodeFromText(node: Node, text: string) { - return getLeadingCommentRanges(text, node.pos); - } - export function getJSDocCommentRanges(node: Node, text: string) { const commentRanges = (node.kind === SyntaxKind.Parameter || node.kind === SyntaxKind.TypeParameter || @@ -649,7 +657,7 @@ namespace ts { node.kind === SyntaxKind.ArrowFunction || node.kind === SyntaxKind.ParenthesizedExpression) ? concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) : - getLeadingCommentRangesOfNodeFromText(node, text); + getLeadingCommentRanges(text, node.pos); // True if the comment starts with '/**' but not if it is '/**/' return filter(commentRanges, comment => text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk && @@ -657,9 +665,10 @@ namespace ts { text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash); } - export let fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*/; - export let fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*/; - export let fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*/; + export const fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*/; + const fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*/; + export const fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*/; + const defaultLibReferenceRegEx = /^(\/\/\/\s*/; export function isPartOfTypeNode(node: Node): boolean { if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) { @@ -1503,8 +1512,8 @@ namespace ts { parent.parent.kind === SyntaxKind.VariableStatement; const variableStatementNode = isInitializerOfVariableDeclarationInStatement ? parent.parent.parent : - isVariableOfVariableDeclarationStatement ? parent.parent : - undefined; + isVariableOfVariableDeclarationStatement ? parent.parent : + undefined; if (variableStatementNode) { getJSDocCommentsAndTagsWorker(variableStatementNode); } @@ -1618,7 +1627,7 @@ namespace ts { if (isInJavaScriptFile(node)) { if (node.type && node.type.kind === SyntaxKind.JSDocVariadicType || forEach(getJSDocParameterTags(node), - t => t.typeExpression && t.typeExpression.type.kind === SyntaxKind.JSDocVariadicType)) { + t => t.typeExpression && t.typeExpression.type.kind === SyntaxKind.JSDocVariadicType)) { return true; } } @@ -1850,7 +1859,7 @@ namespace ts { export function getFileReferenceFromReferencePath(comment: string, commentRange: CommentRange): ReferencePathMatchResult { const simpleReferenceRegEx = /^\/\/\/\s*/gim; + const isNoDefaultLibRegEx = new RegExp(defaultLibReferenceRegEx.source, "gim"); if (simpleReferenceRegEx.test(comment)) { if (isNoDefaultLibRegEx.test(comment)) { return { isNoDefaultLib: true }; @@ -2072,10 +2081,6 @@ namespace ts { return getParseTreeNode(sourceFile, isSourceFile) || sourceFile; } - export function getOriginalSourceFiles(sourceFiles: ReadonlyArray) { - return sameMap(sourceFiles, getOriginalSourceFile); - } - export const enum Associativity { Left, Right @@ -2360,7 +2365,9 @@ namespace ts { // the language service. These characters should be escaped when printing, and if any characters are added, // the map below must be updated. Note that this regexp *does not* include the 'delete' character. // There is no reason for this other than that JSON.stringify does not handle it either. - const escapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; + const doubleQuoteEscapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; + const singleQuoteEscapedCharsRegExp = /[\\\'\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; + const backtickQuoteEscapedCharsRegExp = /[\\\`\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; const escapedCharsMap = createMapFromTemplate({ "\0": "\\0", "\t": "\\t", @@ -2371,18 +2378,23 @@ namespace ts { "\n": "\\n", "\\": "\\\\", "\"": "\\\"", + "\'": "\\\'", + "\`": "\\\`", "\u2028": "\\u2028", // lineSeparator "\u2029": "\\u2029", // paragraphSeparator "\u0085": "\\u0085" // nextLine }); - /** * Based heavily on the abstract 'Quote'/'QuoteJSONString' operation from ECMA-262 (24.3.2.2), * but augmented for a few select characters (e.g. lineSeparator, paragraphSeparator, nextLine) * Note that this doesn't actually wrap the input in double quotes. */ - export function escapeString(s: string): string { + export function escapeString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string { + const escapedCharsRegExp = + quoteChar === CharacterCodes.backtick ? backtickQuoteEscapedCharsRegExp : + quoteChar === CharacterCodes.singleQuote ? singleQuoteEscapedCharsRegExp : + doubleQuoteEscapedCharsRegExp; return s.replace(escapedCharsRegExp, getReplacement); } @@ -2404,8 +2416,8 @@ namespace ts { } const nonAsciiCharacters = /[^\u0000-\u007F]/g; - export function escapeNonAsciiString(s: string): string { - s = escapeString(s); + export function escapeNonAsciiString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string { + s = escapeString(s, quoteChar); // Replace non-ASCII characters with '\uNNNN' escapes if any exist. // Otherwise just return the original string. return nonAsciiCharacters.test(s) ? @@ -2827,7 +2839,7 @@ namespace ts { // // var x = 10; if (node.pos === 0) { - leadingComments = filter(getLeadingCommentRanges(text, node.pos), isPinnedComment); + leadingComments = filter(getLeadingCommentRanges(text, node.pos), isPinnedCommentLocal); } } else { @@ -2873,9 +2885,8 @@ namespace ts { return currentDetachedCommentInfo; - function isPinnedComment(comment: CommentRange) { - return text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk && - text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation; + function isPinnedCommentLocal(comment: CommentRange) { + return isPinnedComment(text, comment); } } @@ -2981,8 +2992,12 @@ namespace ts { return getModifierFlags(node) !== ModifierFlags.None; } - export function hasModifier(node: Node, flags: ModifierFlags) { - return (getModifierFlags(node) & flags) !== 0; + export function hasModifier(node: Node, flags: ModifierFlags): boolean { + return !!getSelectedModifierFlags(node, flags); + } + + export function getSelectedModifierFlags(node: Node, flags: ModifierFlags): ModifierFlags { + return getModifierFlags(node) & flags; } export function getModifierFlags(node: Node): ModifierFlags { @@ -3068,24 +3083,6 @@ namespace ts { return false; } - // Returns false if this heritage clause element's expression contains something unsupported - // (i.e. not a name or dotted name). - export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean { - return isSupportedExpressionWithTypeArgumentsRest(node.expression); - } - - function isSupportedExpressionWithTypeArgumentsRest(node: Expression): boolean { - if (node.kind === SyntaxKind.Identifier) { - return true; - } - else if (isPropertyAccessExpression(node)) { - return isSupportedExpressionWithTypeArgumentsRest(node.expression); - } - else { - return false; - } - } - export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): boolean { return tryGetClassExtendingExpressionWithTypeArguments(node) !== undefined; } @@ -3219,81 +3216,6 @@ namespace ts { return system ? system.newLine : sys ? sys.newLine : carriageReturnLineFeed; } - /** - * Tests whether a node and its subtree is simple enough to have its position - * information ignored when emitting source maps in a destructuring assignment. - * - * @param node The expression to test. - */ - export function isSimpleExpression(node: Expression): boolean { - return isSimpleExpressionWorker(node, 0); - } - - function isSimpleExpressionWorker(node: Expression, depth: number): boolean { - if (depth <= 5) { - const kind = node.kind; - if (kind === SyntaxKind.StringLiteral - || kind === SyntaxKind.NumericLiteral - || kind === SyntaxKind.RegularExpressionLiteral - || kind === SyntaxKind.NoSubstitutionTemplateLiteral - || kind === SyntaxKind.Identifier - || kind === SyntaxKind.ThisKeyword - || kind === SyntaxKind.SuperKeyword - || kind === SyntaxKind.TrueKeyword - || kind === SyntaxKind.FalseKeyword - || kind === SyntaxKind.NullKeyword) { - return true; - } - else if (kind === SyntaxKind.PropertyAccessExpression) { - return isSimpleExpressionWorker((node).expression, depth + 1); - } - else if (kind === SyntaxKind.ElementAccessExpression) { - return isSimpleExpressionWorker((node).expression, depth + 1) - && isSimpleExpressionWorker((node).argumentExpression, depth + 1); - } - else if (kind === SyntaxKind.PrefixUnaryExpression - || kind === SyntaxKind.PostfixUnaryExpression) { - return isSimpleExpressionWorker((node).operand, depth + 1); - } - else if (kind === SyntaxKind.BinaryExpression) { - return (node).operatorToken.kind !== SyntaxKind.AsteriskAsteriskToken - && isSimpleExpressionWorker((node).left, depth + 1) - && isSimpleExpressionWorker((node).right, depth + 1); - } - else if (kind === SyntaxKind.ConditionalExpression) { - return isSimpleExpressionWorker((node).condition, depth + 1) - && isSimpleExpressionWorker((node).whenTrue, depth + 1) - && isSimpleExpressionWorker((node).whenFalse, depth + 1); - } - else if (kind === SyntaxKind.VoidExpression - || kind === SyntaxKind.TypeOfExpression - || kind === SyntaxKind.DeleteExpression) { - return isSimpleExpressionWorker((node).expression, depth + 1); - } - else if (kind === SyntaxKind.ArrayLiteralExpression) { - return (node).elements.length === 0; - } - else if (kind === SyntaxKind.ObjectLiteralExpression) { - return (node).properties.length === 0; - } - else if (kind === SyntaxKind.CallExpression) { - if (!isSimpleExpressionWorker((node).expression, depth + 1)) { - return false; - } - - for (const argument of (node).arguments) { - if (!isSimpleExpressionWorker(argument, depth + 1)) { - return false; - } - } - - return true; - } - } - - return false; - } - /** * Formats an enum value as a string for debugging and debug assertions. */ @@ -3366,24 +3288,6 @@ namespace ts { return formatEnum(flags, (ts).ObjectFlags, /*isFlags*/ true); } - export function getRangePos(range: TextRange | undefined) { - return range ? range.pos : -1; - } - - export function getRangeEnd(range: TextRange | undefined) { - return range ? range.end : -1; - } - - /** - * Increases (or decreases) a position by the provided amount. - * - * @param pos The position. - * @param value The delta. - */ - export function movePos(pos: number, value: number) { - return positionIsSynthesized(pos) ? -1 : pos + value; - } - /** * Creates a new TextRange from the provided pos and end. * @@ -3441,26 +3345,6 @@ namespace ts { return range.pos === range.end; } - /** - * Creates a new TextRange from a provided range with its end position collapsed to its - * start position. - * - * @param range A TextRange. - */ - export function collapseRangeToStart(range: TextRange): TextRange { - return isCollapsedRange(range) ? range : moveRangeEnd(range, range.pos); - } - - /** - * Creates a new TextRange from a provided range with its start position collapsed to its - * end position. - * - * @param range A TextRange. - */ - export function collapseRangeToEnd(range: TextRange): TextRange { - return isCollapsedRange(range) ? range : moveRangePos(range, range.end); - } - /** * Creates a new TextRange for a token at the provides start position. * @@ -3524,31 +3408,6 @@ namespace ts { return node.initializer !== undefined; } - /** - * Gets a value indicating whether a node is merged with a class declaration in the same scope. - */ - export function isMergedWithClass(node: Node) { - if (node.symbol) { - for (const declaration of node.symbol.declarations) { - if (declaration.kind === SyntaxKind.ClassDeclaration && declaration !== node) { - return true; - } - } - } - - return false; - } - - /** - * Gets a value indicating whether a node is the first declaration of its kind. - * - * @param node A Declaration node. - * @param kind The SyntaxKind to find among related declarations. - */ - export function isFirstDeclarationOfKind(node: Node, kind: SyntaxKind) { - return node.symbol && getDeclarationOfKind(node.symbol, kind) === node; - } - export function isWatchSet(options: CompilerOptions) { // Firefox has Object.prototype.watch return options.watch && options.hasOwnProperty("watch"); @@ -3629,91 +3488,69 @@ namespace ts { return true; } - export function cleanExistingMap( - existingMap: Map, - onDeleteExistingValue: (key: string, existingValue: T) => void) { - if (existingMap) { - // Remove all - existingMap.forEach((existingValue, key) => { - existingMap.delete(key); - onDeleteExistingValue(key, existingValue); - }); - } + /** + * clears already present map by calling onDeleteExistingValue callback before deleting that key/value + */ + export function clearMap(map: Map, onDeleteExistingValue: (key: string, existingValue: T) => void) { + // Remove all + map.forEach((existingValue, key) => { + onDeleteExistingValue(key, existingValue); + }); + map.clear(); } - export function mutateExistingMapWithNewSet( - existingMap: Map, newMap: Map, - createNewValue: (key: string) => T, - onDeleteExistingValue: (key: string, existingValue: T) => void - ): Map { - return mutateExistingMap( - existingMap, newMap, - /*createNewValue*/(key, _valueInNewMap) => createNewValue(key), - onDeleteExistingValue, - ); + export interface MutateMapOptions { + createNewValue(key: string, valueInNewMap: U): T; + onDeleteExistingValue(key: string, existingValue: T, isNotSame?: boolean): void; + + /** + * If present this is called if there is value for the key in new map and existing map + */ + onExistingValue?(existingValue: T, valueInNewMap: U): void; + /** + * If there onExistingValue is not provided, this callback if present will be called to + * detemine if the value in the map needs to be deleted + */ + shouldDeleteExistingValue?(key: string, existingValue: T, valueInNewMap: U): boolean; } - export function mutateExistingMapWithSameExistingValues( - existingMap: Map, newMap: Map, - createNewValue: (key: string, valueInNewMap: U) => T, - onDeleteExistingValue: (key: string, existingValue: T) => void, - onExistingValue?: (existingValue: T, valueInNewMap: U) => void - ): Map { - return mutateExistingMap( - existingMap, newMap, - createNewValue, onDeleteExistingValue, - /*isSameValue*/ undefined, /*onDeleteExistingMismatchValue*/ undefined, - onExistingValue - ); - } - - export function mutateExistingMap( - existingMap: Map, newMap: Map, - createNewValue: (key: string, valueInNewMap: U) => T, - onDeleteExistingValue: (key: string, existingValue: T) => void, - isSameValue?: (existingValue: T, valueInNewMap: U) => boolean, - OnDeleteExistingMismatchValue?: (key: string, existingValue: T) => void, - onExistingValue?: (existingValue: T, valueInNewMap: U) => void - ): Map { + /** + * Mutates the map with newMap such that keys in map will be same as newMap. + */ + export function mutateMap(map: Map, newMap: ReadonlyMap, options: MutateMapOptions) { // If there are new values update them if (newMap) { - if (existingMap) { - // Needs update - existingMap.forEach((existingValue, key) => { - const valueInNewMap = newMap.get(key); - // Existing value - remove it - if (valueInNewMap === undefined) { - existingMap.delete(key); - onDeleteExistingValue(key, existingValue); - } - // different value - remove it - else if (isSameValue && !isSameValue(existingValue, valueInNewMap)) { - existingMap.delete(key); - OnDeleteExistingMismatchValue(key, existingValue); - } - else if (onExistingValue) { - onExistingValue(existingValue, valueInNewMap); - } - }); - } - else { - // Create new - existingMap = createMap(); - } - - // Add new values that are not already present - newMap.forEach((valueInNewMap, key) => { - if (!existingMap.has(key)) { - // New values - existingMap.set(key, createNewValue(key, valueInNewMap)); + const { createNewValue, onDeleteExistingValue, onExistingValue, shouldDeleteExistingValue } = options; + // Needs update + map.forEach((existingValue, key) => { + const valueInNewMap = newMap.get(key); + // Not present any more in new map, remove it + if (valueInNewMap === undefined) { + map.delete(key); + onDeleteExistingValue(key, existingValue); + } + // If present notify about existing values + else if (onExistingValue) { + onExistingValue(existingValue, valueInNewMap); + } + // different value, delete it here if this value cant be kept around + // Note that if the value is deleted here, new value will be created in newMap.forEach loop for this key + else if (shouldDeleteExistingValue && !shouldDeleteExistingValue(key, existingValue, valueInNewMap)) { + map.delete(key); } }); - return existingMap; + // Add new values that are not already present + newMap.forEach((valueInNewMap, key) => { + if (!map.has(key)) { + // New values + map.set(key, createNewValue(key, valueInNewMap)); + } + }); + } + else { + clearMap(map, options.onDeleteExistingValue); } - - cleanExistingMap(existingMap, onDeleteExistingValue); - return undefined; } } @@ -3964,6 +3801,20 @@ namespace ts { return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent); } + export function isEmptyBindingPattern(node: BindingName): node is BindingPattern { + if (isBindingPattern(node)) { + return every(node.elements, isEmptyBindingElement); + } + return false; + } + + export function isEmptyBindingElement(node: BindingElement): boolean { + if (isOmittedExpression(node)) { + return true; + } + return isEmptyBindingPattern(node.name); + } + function walkUpBindingElementsAndPatterns(node: Node): Node { while (node && (node.kind === SyntaxKind.BindingElement || isBindingPattern(node))) { node = node.parent; @@ -5231,6 +5082,19 @@ namespace ts { return isUnaryExpressionKind(skipPartiallyEmittedExpressions(node).kind); } + /* @internal */ + export function isUnaryExpressionWithWrite(expr: Node): expr is PrefixUnaryExpression | PostfixUnaryExpression { + switch (expr.kind) { + case SyntaxKind.PostfixUnaryExpression: + return true; + case SyntaxKind.PrefixUnaryExpression: + return (expr).operator === SyntaxKind.PlusPlusToken || + (expr).operator === SyntaxKind.MinusMinusToken; + default: + return false; + } + } + function isExpressionKind(kind: SyntaxKind) { return kind === SyntaxKind.ConditionalExpression || kind === SyntaxKind.YieldExpression @@ -5445,7 +5309,17 @@ namespace ts { const kind = node.kind; return isStatementKindButNotDeclarationKind(kind) || isDeclarationStatementKind(kind) - || kind === SyntaxKind.Block; + || isBlockStatement(node); + } + + function isBlockStatement(node: Node): node is Block { + if (node.kind !== SyntaxKind.Block) return false; + if (node.parent !== undefined) { + if (node.parent.kind === SyntaxKind.TryStatement || node.parent.kind === SyntaxKind.CatchClause) { + return false; + } + } + return !isFunctionBlock(node); } // Module references diff --git a/src/compiler/watchedProgram.ts b/src/compiler/watchedProgram.ts index d4301ea839d..ca0292fa533 100644 --- a/src/compiler/watchedProgram.ts +++ b/src/compiler/watchedProgram.ts @@ -241,7 +241,7 @@ namespace ts { let needsReload: boolean; // true if the config file changed and needs to reload it from the disk let missingFilesMap: Map; // Map of file watchers for the missing files let configFileWatcher: FileWatcher; // watcher for the config file - let watchedWildCardDirectories: Map; // map of watchers for the wild card directories in the config file + let watchedWildcardDirectories: Map; // map of watchers for the wild card directories in the config file let timerToUpdateProgram: any; // timer callback to recompile the program const sourceFilesCache = createMap(); // Cache that stores the source file and version info @@ -301,7 +301,7 @@ namespace ts { builder.onProgramUpdateGraph(program, hasInvalidatedResolution); // Update watches - missingFilesMap = updateMissingFilePathsWatch(program, missingFilesMap, watchMissingFilePath, closeMissingFilePathWatcher); + updateMissingFilePathsWatch(program, missingFilesMap || (missingFilesMap = createMap()), watchMissingFilePath, closeMissingFilePathWatcher); if (missingFilePathsRequestedForRelease) { // These are the paths that program creater told us as not in use any more but were missing on the disk. // We didnt remove the entry for them from sourceFiles cache so that we dont have to do File IO, @@ -591,21 +591,22 @@ namespace ts { } function watchConfigFileWildCardDirectories() { - const wildcards = createMapFromTemplate(configFileWildCardDirectories); - watchedWildCardDirectories = updateWatchingWildcardDirectories( - watchedWildCardDirectories, wildcards, - watchWildCardDirectory, stopWatchingWildCardDirectory + updateWatchingWildcardDirectories( + watchedWildcardDirectories || (watchedWildcardDirectories = createMap()), + createMapFromTemplate(configFileWildCardDirectories), + watchWildCardDirectory, + stopWatchingWildCardDirectory ); } - function watchWildCardDirectory(directory: string, recursive: boolean) { + function watchWildCardDirectory(directory: string, flags: WatchDirectoryFlags) { return host.watchDirectory(directory, fileName => onFileAddOrRemoveInWatchedDirectory(getNormalizedAbsolutePath(fileName, directory)), - recursive); + (flags & WatchDirectoryFlags.Recursive) !== 0); } - function stopWatchingWildCardDirectory(_directory: string, fileWatcher: FileWatcher, _recursive: boolean, _recursiveChanged: boolean) { - fileWatcher.close(); + function stopWatchingWildCardDirectory(_directory: string, { watcher }: WildcardDirectoryWatchers, _recursiveChanged: boolean) { + watcher.close(); } function onFileAddOrRemoveInWatchedDirectory(fileName: string) { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index cd501b43959..b025fb33121 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -187,6 +187,9 @@ namespace FourSlash { // The current caret position in the active file public currentCaretPosition = 0; + // The position of the end of the current selection, or -1 if nothing is selected + public selectionEnd = -1; + public lastKnownMarker = ""; // The file that's currently 'opened' @@ -433,11 +436,19 @@ namespace FourSlash { public goToPosition(pos: number) { this.currentCaretPosition = pos; + this.selectionEnd = -1; + } + + public select(startMarker: string, endMarker: string) { + const start = this.getMarkerByName(startMarker), end = this.getMarkerByName(endMarker); + this.goToPosition(start.position); + this.selectionEnd = end.position; } public moveCaretRight(count = 1) { this.currentCaretPosition += count; this.currentCaretPosition = Math.min(this.currentCaretPosition, this.getFileContent(this.activeFile.fileName).length); + this.selectionEnd = -1; } // Opens a file given its 0-based index or fileName @@ -451,7 +462,7 @@ namespace FourSlash { this.languageServiceAdapterHost.openFile(fileToOpen.fileName, content, scriptKindName); } - public verifyErrorExistsBetweenMarkers(startMarkerName: string, endMarkerName: string, negative: boolean) { + public verifyErrorExistsBetweenMarkers(startMarkerName: string, endMarkerName: string, shouldExist: boolean) { const startMarker = this.getMarkerByName(startMarkerName); const endMarker = this.getMarkerByName(endMarkerName); const predicate = (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => @@ -459,9 +470,9 @@ namespace FourSlash { const exists = this.anyErrorInRange(predicate, startMarker, endMarker); - if (exists !== negative) { - this.printErrorLog(negative, this.getAllDiagnostics()); - throw new Error(`Failure between markers: '${startMarkerName}', '${endMarkerName}'`); + if (exists !== shouldExist) { + this.printErrorLog(shouldExist, this.getAllDiagnostics()); + throw new Error(`${shouldExist ? "Expected" : "Did not expect"} failure between markers: '${startMarkerName}', '${endMarkerName}'`); } } @@ -483,10 +494,11 @@ namespace FourSlash { } private getAllDiagnostics(): ts.Diagnostic[] { - return ts.flatMap(this.languageServiceAdapterHost.getFilenames(), fileName => this.getDiagnostics(fileName)); + return ts.flatMap(this.languageServiceAdapterHost.getFilenames(), fileName => + ts.isAnySupportedFileExtension(fileName) ? this.getDiagnostics(fileName) : []); } - public verifyErrorExistsAfterMarker(markerName: string, negative: boolean, after: boolean) { + public verifyErrorExistsAfterMarker(markerName: string, shouldExist: boolean, after: boolean) { const marker: Marker = this.getMarkerByName(markerName); let predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean; @@ -502,30 +514,15 @@ namespace FourSlash { const exists = this.anyErrorInRange(predicate, marker); const diagnostics = this.getAllDiagnostics(); - if (exists !== negative) { - this.printErrorLog(negative, diagnostics); - throw new Error("Failure at marker: " + markerName); + if (exists !== shouldExist) { + this.printErrorLog(shouldExist, diagnostics); + throw new Error(`${shouldExist ? "Expected" : "Did not expect"} failure at marker '${markerName}'`); } } - private anyErrorInRange(predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean, startMarker: Marker, endMarker?: Marker) { - - const errors = this.getDiagnostics(startMarker.fileName); - let exists = false; - - const startPos = startMarker.position; - let endPos: number = undefined; - if (endMarker !== undefined) { - endPos = endMarker.position; - } - - errors.forEach(function (error: ts.Diagnostic) { - if (predicate(error.start, error.start + error.length, startPos, endPos)) { - exists = true; - } - }); - - return exists; + private anyErrorInRange(predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean, startMarker: Marker, endMarker?: Marker): boolean { + return this.getDiagnostics(startMarker.fileName).some(({ start, length }) => + predicate(start, start + length, startMarker.position, endMarker === undefined ? undefined : endMarker.position)); } private printErrorLog(expectErrors: boolean, errors: ts.Diagnostic[]) { @@ -550,6 +547,7 @@ namespace FourSlash { public verifyNoErrors() { ts.forEachKey(this.inputFiles, fileName => { + if (!ts.isAnySupportedFileExtension(fileName)) return; const errors = this.getDiagnostics(fileName); if (errors.length) { this.printErrorLog(/*expectErrors*/ false, errors); @@ -980,9 +978,9 @@ namespace FourSlash { } for (const reference of expectedReferences) { - const {fileName, start, end} = reference; + const { fileName, start, end } = reference; if (reference.marker && reference.marker.data) { - const {isWriteAccess, isDefinition} = reference.marker.data; + const { isWriteAccess, isDefinition } = reference.marker.data; this.verifyReferencesWorker(actualReferences, fileName, start, end, isWriteAccess, isDefinition); } else { @@ -1193,7 +1191,7 @@ namespace FourSlash { displayParts: ts.SymbolDisplayPart[], documentation: ts.SymbolDisplayPart[], tags: ts.JSDocTagInfo[] - ) { + ) { const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition); assert.equal(actualQuickInfo.kind, kind, this.messageAtLastKnownMarker("QuickInfo kind")); @@ -1789,19 +1787,16 @@ namespace FourSlash { // We get back a set of edits, but langSvc.editScript only accepts one at a time. Use this to keep track // of the incremental offset from each edit to the next. We assume these edit ranges don't overlap - edits = edits.sort((a, b) => a.span.start - b.span.start); - for (let i = 0; i < edits.length - 1; i++) { - const firstEditSpan = edits[i].span; - const firstEditEnd = firstEditSpan.start + firstEditSpan.length; - assert.isTrue(firstEditEnd <= edits[i + 1].span.start); - } + // Copy this so we don't ruin someone else's copy + edits = JSON.parse(JSON.stringify(edits)); // Get a snapshot of the content of the file so we can make sure any formatting edits didn't destroy non-whitespace characters const oldContent = this.getFileContent(fileName); let runningOffset = 0; - for (const edit of edits) { - const offsetStart = edit.span.start + runningOffset; + for (let i = 0; i < edits.length; i++) { + const edit = edits[i]; + const offsetStart = edit.span.start; const offsetEnd = offsetStart + edit.span.length; this.editScriptAndUpdateMarkers(fileName, offsetStart, offsetEnd, edit.newText); const editDelta = edit.newText.length - edit.span.length; @@ -1816,8 +1811,13 @@ namespace FourSlash { } } runningOffset += editDelta; - // TODO: Consider doing this at least some of the time for higher fidelity. Currently causes a failure (bug 707150) - // this.languageService.getScriptLexicalStructure(fileName); + + // Update positions of any future edits affected by this change + for (let j = i + 1; j < edits.length; j++) { + if (edits[j].span.start >= edits[i].span.start) { + edits[j].span.start += editDelta; + } + } } if (isFormattingEdit) { @@ -1901,7 +1901,7 @@ namespace FourSlash { this.goToPosition(len); } - public goToRangeStart({fileName, start}: Range) { + public goToRangeStart({ fileName, start }: Range) { this.openFile(fileName); this.goToPosition(start); } @@ -2075,7 +2075,7 @@ namespace FourSlash { return result; } - private rangeText({fileName, start, end}: Range): string { + private rangeText({ fileName, start, end }: Range): string { return this.getFileContent(fileName).slice(start, end); } @@ -2361,7 +2361,7 @@ namespace FourSlash { private applyCodeActions(actions: ts.CodeAction[], index?: number): void { if (index === undefined) { if (!(actions && actions.length === 1)) { - this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `${Harness.IO.newLine()} "${a.description}"`) : "" }`); + this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `${Harness.IO.newLine()} "${a.description}"`) : ""}`); } index = 0; } @@ -2736,6 +2736,30 @@ namespace FourSlash { } } + private getSelection() { + return ({ + pos: this.currentCaretPosition, + end: this.selectionEnd === -1 ? this.currentCaretPosition : this.selectionEnd + }); + } + + public verifyRefactorAvailable(negative: boolean, name?: string, subName?: string) { + const selection = this.getSelection(); + + let refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, selection) || []; + if (name) { + refactors = refactors.filter(r => r.name === name && (subName === undefined || r.actions.some(a => a.name === subName))); + } + const isAvailable = refactors.length > 0; + + if (negative && isAvailable) { + this.raiseError(`verifyApplicableRefactorAvailableForRange failed - expected no refactor but found some: ${refactors.map(r => r.name).join(", ")}`); + } + else if (!negative && !isAvailable) { + this.raiseError(`verifyApplicableRefactorAvailableForRange failed - expected a refactor but found none.`); + } + } + public verifyApplicableRefactorAvailableForRange(negative: boolean) { const ranges = this.getRanges(); if (!(ranges && ranges.length === 1)) { @@ -2752,6 +2776,20 @@ namespace FourSlash { } } + public applyRefactor(refactorName: string, actionName: string) { + const range = this.getSelection(); + const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range); + const refactor = ts.find(refactors, r => r.name === refactorName); + if (!refactor) { + this.raiseError(`The expected refactor: ${refactorName} is not available at the marker location.`); + } + + const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName); + for (const edit of editInfo.edits) { + this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false); + } + } + public verifyFileAfterApplyingRefactorAtMarker( markerName: string, expectedContent: string, @@ -3496,6 +3534,10 @@ namespace FourSlashInterface { public file(indexOrName: any, content?: string, scriptKindName?: string): void { this.state.openFile(indexOrName, content, scriptKindName); } + + public select(startMarker: string, endMarker: string) { + this.state.select(startMarker, endMarker); + } } export class VerifyNegatable { @@ -3617,6 +3659,10 @@ namespace FourSlashInterface { public applicableRefactorAvailableForRange() { this.state.verifyApplicableRefactorAvailableForRange(this.negative); } + + public refactorAvailable(name?: string, subName?: string) { + this.state.verifyRefactorAvailable(this.negative, name, subName); + } } export class Verify extends VerifyNegatable { @@ -4012,6 +4058,10 @@ namespace FourSlashInterface { public disableFormatting() { this.state.enableFormatting = false; } + + public applyRefactor(refactorName: string, actionName: string) { + this.state.applyRefactor(refactorName, actionName); + } } export class Debug { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index c0181199cbe..063f275f66a 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -67,17 +67,16 @@ namespace Utils { export let currentExecutionEnvironment = getExecutionEnvironment(); - const Buffer: typeof global.Buffer = currentExecutionEnvironment !== ExecutionEnvironment.Browser - ? require("buffer").Buffer - : undefined; + // Thanks to browserify, Buffer is always available nowadays + const Buffer: typeof global.Buffer = require("buffer").Buffer; export function encodeString(s: string): string { - return Buffer ? (new Buffer(s)).toString("utf8") : s; + return Buffer.from(s).toString("utf8"); } export function byteLength(s: string, encoding?: string): number { // stub implementation if Buffer is not available (in-browser case) - return Buffer ? Buffer.byteLength(s, encoding) : s.length; + return Buffer.byteLength(s, encoding); } export function evalFile(fileContents: string, fileName: string, nodeContext?: any) { @@ -133,17 +132,18 @@ namespace Utils { return content; } - export function memoize(f: T): T { - const cache: { [idx: string]: any } = {}; + export function memoize(f: T, memoKey: (...anything: any[]) => string): T { + const cache = ts.createMap(); - return (function(this: any) { - const key = Array.prototype.join.call(arguments); - const cachedResult = cache[key]; - if (cachedResult) { - return cachedResult; + return (function(this: any, ...args: any[]) { + const key = memoKey(...args); + if (cache.has(key)) { + return cache.get(key); } else { - return cache[key] = f.apply(this, arguments); + const value = f.apply(this, args); + cache.set(key, value); + return value; } }); } @@ -420,7 +420,7 @@ namespace Utils { const maxHarnessFrames = 1; - export function filterStack(error: Error, stackTraceLimit: number = Infinity) { + export function filterStack(error: Error, stackTraceLimit = Infinity) { const stack = (error).stack; if (stack) { const lines = stack.split(/\r\n?|\n/g); @@ -564,7 +564,7 @@ namespace Harness { } export let listFiles: typeof IO.listFiles = (path, spec?, options?) => { - options = options || <{ recursive?: boolean; }>{}; + options = options || {}; function filesInFolder(folder: string): string[] { let paths: string[] = []; @@ -686,7 +686,7 @@ namespace Harness { return dirPath; } - export let directoryName: typeof IO.directoryName = Utils.memoize(directoryNameImpl); + export let directoryName: typeof IO.directoryName = Utils.memoize(directoryNameImpl, path => path); export function resolvePath(path: string) { const response = Http.getFileFromServerSync(serverRoot + path + "?resolve=true"); @@ -703,21 +703,22 @@ namespace Harness { return response.status === 200; } - export let listFiles = Utils.memoize((path: string, spec?: RegExp): string[] => { + export const listFiles = Utils.memoize((path: string, spec?: RegExp, options?: { recursive?: boolean }): string[] => { const response = Http.getFileFromServerSync(serverRoot + path); if (response.status === 200) { - const results = response.responseText.split(","); + let results = response.responseText.split(","); if (spec) { - return results.filter(file => spec.test(file)); + results = results.filter(file => spec.test(file)); } - else { - return results; + if (options && !options.recursive) { + results = results.filter(file => (ts.getDirectoryPath(ts.normalizeSlashes(file)) === path)); } + return results; } else { return [""]; } - }); + }, (path: string, spec?: RegExp, options?: { recursive?: boolean }) => `${path}|${spec}|${options ? options.recursive : undefined}`); export function readFile(file: string): string | undefined { const response = Http.getFileFromServerSync(serverRoot + file); @@ -1198,13 +1199,21 @@ namespace Harness { return { result, options }; } - export function compileDeclarationFiles(inputFiles: TestFile[], + export interface DeclarationCompilationContext { + declInputFiles: TestFile[]; + declOtherFiles: TestFile[]; + harnessSettings: TestCaseParser.CompilerSettings & HarnessOptions; + options: ts.CompilerOptions; + currentDirectory: string; + } + + export function prepareDeclarationCompilationContext(inputFiles: TestFile[], otherFiles: TestFile[], result: CompilerResult, harnessSettings: TestCaseParser.CompilerSettings & HarnessOptions, options: ts.CompilerOptions, // Current directory is needed for rwcRunner to be able to use currentDirectory defined in json file - currentDirectory: string) { + currentDirectory: string): DeclarationCompilationContext | undefined { if (options.declaration && result.errors.length === 0 && result.declFilesCode.length !== result.files.length) { throw new Error("There were no errors and declFiles generated did not match number of js files generated"); } @@ -1216,8 +1225,7 @@ namespace Harness { if (options.declaration && result.errors.length === 0 && result.declFilesCode.length > 0) { ts.forEach(inputFiles, file => addDtsFile(file, declInputFiles)); ts.forEach(otherFiles, file => addDtsFile(file, declOtherFiles)); - const output = compileFiles(declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory || harnessSettings["currentDirectory"]); - return { declInputFiles, declOtherFiles, declResult: output.result }; + return { declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory: currentDirectory || harnessSettings["currentDirectory"] }; } function addDtsFile(file: TestFile, dtsFiles: TestFile[]) { @@ -1263,6 +1271,15 @@ namespace Harness { } } + export function compileDeclarationFiles(context: DeclarationCompilationContext | undefined) { + if (!context) { + return; + } + const { declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory } = context; + const output = compileFiles(declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory); + return { declInputFiles, declOtherFiles, declResult: output.result }; + } + function normalizeLineEndings(text: string, lineEnding: string): string { let normalized = text.replace(/\r\n?/g, "\n"); if (lineEnding !== "\n") { @@ -1277,10 +1294,19 @@ namespace Harness { export function getErrorBaseline(inputFiles: TestFile[], diagnostics: ts.Diagnostic[]) { diagnostics.sort(ts.compareDiagnostics); - const outputLines: string[] = []; + let outputLines = ""; // Count up all errors that were found in files other than lib.d.ts so we don't miss any let totalErrorsReportedInNonLibraryFiles = 0; + let firstLine = true; + function newLine() { + if (firstLine) { + firstLine = false; + return ""; + } + return "\r\n"; + } + function outputErrorText(error: ts.Diagnostic) { const message = ts.flattenDiagnosticMessageText(error.messageText, Harness.IO.newLine()); @@ -1289,7 +1315,7 @@ namespace Harness { .map(s => s.length > 0 && s.charAt(s.length - 1) === "\r" ? s.substr(0, s.length - 1) : s) .filter(s => s.length > 0) .map(s => "!!! " + ts.DiagnosticCategory[error.category].toLowerCase() + " TS" + error.code + ": " + s); - errLines.forEach(e => outputLines.push(e)); + errLines.forEach(e => outputLines += (newLine() + e)); // do not count errors from lib.d.ts here, they are computed separately as numLibraryDiagnostics // if lib.d.ts is explicitly included in input files and there are some errors in it (i.e. because of duplicate identifiers) @@ -1315,7 +1341,7 @@ namespace Harness { // Header - outputLines.push("==== " + inputFile.unitName + " (" + fileErrors.length + " errors) ===="); + outputLines += (newLine() + "==== " + inputFile.unitName + " (" + fileErrors.length + " errors) ===="); // Make sure we emit something for every error let markedErrorCount = 0; @@ -1344,7 +1370,7 @@ namespace Harness { nextLineStart = lineStarts[lineIndex + 1]; } // Emit this line from the original file - outputLines.push(" " + line); + outputLines += (newLine() + " " + line); fileErrors.forEach(err => { // Does any error start or continue on to this line? Emit squiggles const end = ts.textSpanEnd(err); @@ -1356,7 +1382,7 @@ namespace Harness { // Calculate the start of the squiggle const squiggleStart = Math.max(0, relativeOffset); // TODO/REVIEW: this doesn't work quite right in the browser if a multi file test has files whose names are just the right length relative to one another - outputLines.push(" " + line.substr(0, squiggleStart).replace(/[^\s]/g, " ") + new Array(Math.min(length, line.length - squiggleStart) + 1).join("~")); + outputLines += (newLine() + " " + line.substr(0, squiggleStart).replace(/[^\s]/g, " ") + new Array(Math.min(length, line.length - squiggleStart) + 1).join("~")); // If the error ended here, or we're at the end of the file, emit its message if ((lineIndex === lines.length - 1) || nextLineStart > end) { @@ -1387,7 +1413,7 @@ namespace Harness { assert.equal(totalErrorsReportedInNonLibraryFiles + numLibraryDiagnostics + numTest262HarnessDiagnostics, diagnostics.length, "total number of errors"); return minimalDiagnosticsToString(diagnostics) + - Harness.IO.newLine() + Harness.IO.newLine() + outputLines.join("\r\n"); + Harness.IO.newLine() + Harness.IO.newLine() + outputLines; } export function doErrorBaseline(baselinePath: string, inputFiles: TestFile[], errors: ts.Diagnostic[]) { @@ -1590,9 +1616,10 @@ namespace Harness { } } - const declFileCompilationResult = - Harness.Compiler.compileDeclarationFiles( - toBeCompiled, otherFiles, result, harnessSettings, options, /*currentDirectory*/ undefined); + const declFileContext = Harness.Compiler.prepareDeclarationCompilationContext( + toBeCompiled, otherFiles, result, harnessSettings, options, /*currentDirectory*/ undefined + ); + const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(declFileContext); if (declFileCompilationResult && declFileCompilationResult.declResult.errors.length) { jsCode += "\r\n\r\n//// [DtsFileErrors]\r\n"; @@ -1967,7 +1994,7 @@ namespace Harness { IO.writeFile(actualFileName + ".delete", ""); } else { - IO.writeFile(actualFileName, actual); + IO.writeFile(actualFileName, encoded_actual); } throw new Error(`The baseline file ${relativeFileName} has changed.`); } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 22878f5a89e..3133ba3d133 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -193,7 +193,9 @@ namespace Harness.LanguageService { } getCurrentDirectory(): string { return virtualFileSystemRoot; } getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; } - getScriptFileNames(): string[] { return this.getFilenames(); } + getScriptFileNames(): string[] { + return this.getFilenames().filter(ts.isAnySupportedFileExtension); + } getScriptSnapshot(fileName: string): ts.IScriptSnapshot { const script = this.getScriptInfo(fileName); return script ? new ScriptSnapshot(script) : undefined; @@ -681,11 +683,11 @@ namespace Harness.LanguageService { } info(message: string): void { - return this.host.log(message); + this.host.log(message); } - msg(message: string) { - return this.host.log(message); + err(message: string): void { + this.host.log(message); } loggingEnabled() { @@ -700,17 +702,12 @@ namespace Harness.LanguageService { return false; } - - endGroup(): void { - } + group() { throw ts.notImplemented(); } perftrc(message: string): void { return this.host.log(message); } - startGroup(): void { - } - setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any { return setTimeout(callback, ms, args); } @@ -795,7 +792,7 @@ namespace Harness.LanguageService { default: return { module: undefined, - error: "Could not resolve module" + error: new Error("Could not resolve module") }; } @@ -828,6 +825,7 @@ namespace Harness.LanguageService { host: serverHost, cancellationToken: ts.server.nullCancellationToken, useSingleInferredProject: false, + useInferredProjectPerProjectRoot: false, typingsInstaller: undefined, byteLength: Utils.byteLength, hrtime: process.hrtime, diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index fd8d06427cf..480feb547bf 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -426,12 +426,12 @@ class ProjectRunner extends RunnerBase { compilerResult.program ? ts.filter(compilerResult.program.getSourceFiles(), sourceFile => !Harness.isDefaultLibraryFile(sourceFile.fileName)) : []), - sourceFile => { + (sourceFile): Harness.Compiler.TestFile => ({ unitName: ts.isRootedDiskPath(sourceFile.fileName) ? RunnerBase.removeFullPaths(sourceFile.fileName) : sourceFile.fileName, content: sourceFile.text - }); + })); return Harness.Compiler.getErrorBaseline(inputFiles, compilerResult.errors); } diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index 98663863a93..a1dc50349e4 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -208,6 +208,14 @@ namespace RWC { }, baselineOpts); }); + it("has the expected types", () => { + // We don't need to pass the extension here because "doTypeAndSymbolBaseline" will append appropriate extension of ".types" or ".symbols" + Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles + .concat(otherFiles) + .filter(file => !!compilerResult.program.getSourceFile(file.unitName)) + .filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts); + }); + // Ideally, a generated declaration file will have no errors. But we allow generated // declaration file errors as part of the baseline. it("has the expected errors in generated declaration files", () => { @@ -217,8 +225,12 @@ namespace RWC { return null; } - const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles( - inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory); + const declContext = Harness.Compiler.prepareDeclarationCompilationContext( + inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory + ); + // Reset compilerResult before calling into `compileDeclarationFiles` so the memory from the original compilation can be freed + compilerResult = undefined; + const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(declContext); return Harness.Compiler.minimalDiagnosticsToString(declFileCompilationResult.declResult.errors) + Harness.IO.newLine() + Harness.IO.newLine() + @@ -226,14 +238,6 @@ namespace RWC { }, baselineOpts); } }); - - it("has the expected types", () => { - // We don't need to pass the extension here because "doTypeAndSymbolBaseline" will append appropriate extension of ".types" or ".symbols" - Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles - .concat(otherFiles) - .filter(file => !!compilerResult.program.getSourceFile(file.unitName)) - .filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts); - }); }); } } diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index 12efa5717a3..152246149f0 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -52,29 +52,18 @@ namespace ts { } function createProject(rootFile: string, serverHost: server.ServerHost): { project: server.Project, rootScriptInfo: server.ScriptInfo } { - const logger: server.Logger = { - close: noop, - hasLevel: () => false, - loggingEnabled: () => false, - perftrc: noop, - info: noop, - startGroup: noop, - endGroup: noop, - msg: noop, - getLogFileName: (): string => undefined - }; - const svcOpts: server.ProjectServiceOptions = { host: serverHost, - logger, + logger: projectSystem.nullLogger, cancellationToken: { isCancellationRequested: () => false }, useSingleInferredProject: false, + useInferredProjectPerProjectRoot: false, typingsInstaller: undefined }; const projectService = new server.ProjectService(svcOpts); const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */ true, /*containingProject*/ undefined); - const project = projectService.createInferredProjectWithRootFileIfNecessary(rootScriptInfo); + const project = projectService.assignScriptInfoToInferredProject(rootScriptInfo); project.setCompilerOptions({ module: ts.ModuleKind.AMD, noLib: true } ); return { project, diff --git a/src/harness/unittests/compileOnSave.ts b/src/harness/unittests/compileOnSave.ts index 184e4804cb8..84ee2ebf138 100644 --- a/src/harness/unittests/compileOnSave.ts +++ b/src/harness/unittests/compileOnSave.ts @@ -36,6 +36,7 @@ namespace ts.projectSystem { host, cancellationToken: nullCancellationToken, useSingleInferredProject: false, + useInferredProjectPerProjectRoot: false, typingsInstaller: typingsInstaller || server.nullTypingsInstaller, byteLength: Utils.byteLength, hrtime: process.hrtime, @@ -514,18 +515,20 @@ namespace ts.projectSystem { }; const host = createServerHost([f], { newLine }); const session = createSession(host); - session.executeCommand({ + const openRequest: server.protocol.OpenRequest = { seq: 1, type: "request", - command: "open", + command: server.protocol.CommandTypes.Open, arguments: { file: f.path } - }); - session.executeCommand({ + }; + session.executeCommand(openRequest); + const emitFileRequest: server.protocol.CompileOnSaveEmitFileRequest = { seq: 2, type: "request", - command: "compileOnSaveEmitFile", + command: server.protocol.CommandTypes.CompileOnSaveEmitFile, arguments: { file: f.path } - }); + }; + session.executeCommand(emitFileRequest); const emitOutput = host.readFile(path + ts.Extension.Js); assert.equal(emitOutput, f.content + newLine, "content of emit output should be identical with the input + newline"); } @@ -546,7 +549,7 @@ namespace ts.projectSystem { }; const host = createServerHost([file1, file2, configFile, libFile], { newLine: "\r\n" }); const typingsInstaller = createTestTypingsInstaller(host); - const session = createSession(host, typingsInstaller); + const session = createSession(host, { typingsInstaller }); openFilesForSession([file1, file2], session); const compileFileRequest = makeSessionRequest(CommandNames.CompileOnSaveEmitFile, { file: file1.path, projectFileName: configFile.path }); diff --git a/src/harness/unittests/convertCompilerOptionsFromJson.ts b/src/harness/unittests/convertCompilerOptionsFromJson.ts index 4b2fad32d7b..14c0cb7347d 100644 --- a/src/harness/unittests/convertCompilerOptionsFromJson.ts +++ b/src/harness/unittests/convertCompilerOptionsFromJson.ts @@ -67,14 +67,14 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { module: ModuleKind.CommonJS, target: ScriptTarget.ES5, noImplicitAny: false, sourceMap: false, lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"] }, - errors: [] + errors: [] } ); }); @@ -92,7 +92,7 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { module: ModuleKind.CommonJS, target: ScriptTarget.ES5, noImplicitAny: false, @@ -100,7 +100,7 @@ namespace ts { allowJs: false, lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"] }, - errors: [] + errors: [] } ); }); @@ -117,7 +117,7 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { module: ModuleKind.CommonJS, target: ScriptTarget.ES5, noImplicitAny: false, @@ -146,7 +146,7 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { target: ScriptTarget.ES5, noImplicitAny: false, sourceMap: false, @@ -174,7 +174,7 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { target: ScriptTarget.ES5, noImplicitAny: false, sourceMap: false, @@ -201,7 +201,7 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { noImplicitAny: false, sourceMap: false, }, @@ -227,7 +227,7 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { noImplicitAny: false, sourceMap: false, }, @@ -255,7 +255,7 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { module: ModuleKind.CommonJS, target: ScriptTarget.ES5, noImplicitAny: false, @@ -286,7 +286,7 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { module: ModuleKind.CommonJS, target: ScriptTarget.ES5, noImplicitAny: false, @@ -317,7 +317,7 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { module: ModuleKind.CommonJS, target: ScriptTarget.ES5, noImplicitAny: false, @@ -348,7 +348,7 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { module: ModuleKind.CommonJS, target: ScriptTarget.ES5, noImplicitAny: false, @@ -379,7 +379,7 @@ namespace ts { } }, "tsconfig.json", { - compilerOptions: { + compilerOptions: { module: ModuleKind.CommonJS, target: ScriptTarget.ES5, noImplicitAny: false, @@ -415,8 +415,8 @@ namespace ts { it("Convert default tsconfig.json to compiler-options ", () => { assertCompilerOptions({}, "tsconfig.json", { - compilerOptions: {} as CompilerOptions, - errors: [] + compilerOptions: {}, + errors: [] } ); }); @@ -434,7 +434,7 @@ namespace ts { } }, "jsconfig.json", { - compilerOptions: { + compilerOptions: { allowJs: true, maxNodeModuleJsDepth: 2, allowSyntheticDefaultImports: true, @@ -445,7 +445,7 @@ namespace ts { sourceMap: false, lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"] }, - errors: [] + errors: [] } ); }); @@ -463,7 +463,7 @@ namespace ts { } }, "jsconfig.json", { - compilerOptions: { + compilerOptions: { allowJs: false, maxNodeModuleJsDepth: 2, allowSyntheticDefaultImports: true, @@ -474,7 +474,7 @@ namespace ts { sourceMap: false, lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"] }, - errors: [] + errors: [] } ); }); @@ -516,7 +516,7 @@ namespace ts { allowSyntheticDefaultImports: true, skipLibCheck: true }, - errors: [] + errors: [] } ); }); diff --git a/src/harness/unittests/extractMethods.ts b/src/harness/unittests/extractMethods.ts new file mode 100644 index 00000000000..d096cd3d375 --- /dev/null +++ b/src/harness/unittests/extractMethods.ts @@ -0,0 +1,591 @@ +/// +/// + +namespace ts { + interface Range { + start: number; + end: number; + name: string; + } + + interface Test { + source: string; + ranges: Map; + } + + function extractTest(source: string): Test { + const activeRanges: Range[] = []; + let text = ""; + let lastPos = 0; + let pos = 0; + const ranges = createMap(); + + while (pos < source.length) { + if (source.charCodeAt(pos) === CharacterCodes.openBracket && + (source.charCodeAt(pos + 1) === CharacterCodes.hash || source.charCodeAt(pos + 1) === CharacterCodes.$)) { + const saved = pos; + pos += 2; + const s = pos; + consumeIdentifier(); + const e = pos; + if (source.charCodeAt(pos) === CharacterCodes.bar) { + pos++; + text += source.substring(lastPos, saved); + const name = s === e + ? source.charCodeAt(saved + 1) === CharacterCodes.hash ? "selection" : "extracted" + : source.substring(s, e); + activeRanges.push({ name, start: text.length, end: undefined }); + lastPos = pos; + continue; + } + else { + pos = saved; + } + } + else if (source.charCodeAt(pos) === CharacterCodes.bar && source.charCodeAt(pos + 1) === CharacterCodes.closeBracket) { + text += source.substring(lastPos, pos); + activeRanges[activeRanges.length - 1].end = text.length; + const range = activeRanges.pop(); + if (range.name in ranges) { + throw new Error(`Duplicate name of range ${range.name}`); + } + ranges.set(range.name, range); + pos += 2; + lastPos = pos; + continue; + } + pos++; + } + text += source.substring(lastPos, pos); + + function consumeIdentifier() { + while (isIdentifierPart(source.charCodeAt(pos), ScriptTarget.Latest)) { + pos++; + } + } + return { source: text, ranges }; + } + + const newLineCharacter = "\n"; + function getRuleProvider(action?: (opts: FormatCodeSettings) => void) { + const options = { + indentSize: 4, + tabSize: 4, + newLineCharacter, + convertTabsToSpaces: true, + indentStyle: ts.IndentStyle.Smart, + insertSpaceAfterConstructor: false, + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false, + insertSpaceBeforeFunctionParenthesis: false, + placeOpenBraceOnNewLineForFunctions: false, + placeOpenBraceOnNewLineForControlBlocks: false, + }; + if (action) { + action(options); + } + const rulesProvider = new formatting.RulesProvider(); + rulesProvider.ensureUpToDate(options); + return rulesProvider; + } + + function testExtractRangeFailed(caption: string, s: string, expectedErrors: string[]) { + return it(caption, () => { + const t = extractTest(s); + const file = createSourceFile("a.ts", t.source, ScriptTarget.Latest, /*setParentNodes*/ true); + const selectionRange = t.ranges.get("selection"); + if (!selectionRange) { + throw new Error(`Test ${s} does not specify selection range`); + } + const result = refactor.extractMethod.getRangeToExtract(file, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + assert(result.targetRange === undefined, "failure expected"); + const sortedErrors = result.errors.map(e => e.messageText).sort(); + assert.deepEqual(sortedErrors, expectedErrors.sort(), "unexpected errors"); + }); + } + + function testExtractRange(s: string): void { + const t = extractTest(s); + const f = createSourceFile("a.ts", t.source, ScriptTarget.Latest, /*setParentNodes*/ true); + const selectionRange = t.ranges.get("selection"); + if (!selectionRange) { + throw new Error(`Test ${s} does not specify selection range`); + } + const result = refactor.extractMethod.getRangeToExtract(f, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + const expectedRange = t.ranges.get("extracted"); + if (expectedRange) { + let start: number, end: number; + if (ts.isArray(result.targetRange.range)) { + start = result.targetRange.range[0].getStart(f); + end = ts.lastOrUndefined(result.targetRange.range).getEnd(); + } + else { + start = result.targetRange.range.getStart(f); + end = result.targetRange.range.getEnd(); + } + assert.equal(start, expectedRange.start, "incorrect start of range"); + assert.equal(end, expectedRange.end, "incorrect end of range"); + } + else { + assert.isTrue(!result.targetRange, `expected range to extract to be undefined`); + } + } + + describe("extractMethods", () => { + it("get extract range from selection", () => { + testExtractRange(` + [#| + [$|var x = 1; + var y = 2;|]|] + `); + testExtractRange(` + [#| + var x = 1; + var y = 2|]; + `); + testExtractRange(` + [#|var x = 1|]; + var y = 2; + `); + testExtractRange(` + if ([#|[#extracted|a && b && c && d|]|]) { + } + `); + testExtractRange(` + if [#|(a && b && c && d|]) { + } + `); + testExtractRange(` + if (a && b && c && d) { + [#| [$|var x = 1; + console.log(x);|] |] + } + `); + testExtractRange(` + [#| + if (a) { + return 100; + } |] + `); + testExtractRange(` + function foo() { + [#| [$|if (a) { + } + return 100|] |] + } + `); + testExtractRange(` + [#| + [$|l1: + if (x) { + break l1; + }|]|] + `); + testExtractRange(` + [#| + [$|l2: + { + if (x) { + } + break l2; + }|]|] + `); + testExtractRange(` + while (true) { + [#| if(x) { + } + break; |] + } + `); + testExtractRange(` + while (true) { + [#| if(x) { + } + continue; |] + } + `); + testExtractRange(` + l3: + { + [#| + if (x) { + } + break l3; |] + } + `); + testExtractRange(` + function f() { + while (true) { + [#| + if (x) { + return; + } |] + } + } + `); + testExtractRange(` + function f() { + while (true) { + [#| + [$|if (x) { + } + return;|] + |] + } + } + `); + testExtractRange(` + function f() { + return [#| [$|1 + 2|] |]+ 3; + } + } + `); + testExtractRange(` + function f() { + return [$|1 + [#|2 + 3|]|]; + } + } + `); + testExtractRange(` + function f() { + return [$|1 + 2 + [#|3 + 4|]|]; + } + } + `); + }); + + testExtractRangeFailed("extractRangeFailed1", + ` +namespace A { + function f() { + [#| + let x = 1 + if (x) { + return 10; + } + |] + } +} + `, + [ + "Cannot extract range containing conditional return statement." + ]); + + testExtractRangeFailed("extractRangeFailed2", + ` +namespace A { + function f() { + while (true) { + [#| + let x = 1 + if (x) { + break; + } + |] + } + } +} + `, + [ + "Cannot extract range containing conditional break or continue statements." + ]); + + testExtractRangeFailed("extractRangeFailed3", + ` +namespace A { + function f() { + while (true) { + [#| + let x = 1 + if (x) { + continue; + } + |] + } + } +} + `, + [ + "Cannot extract range containing conditional break or continue statements." + ]); + + testExtractRangeFailed("extractRangeFailed4", + ` +namespace A { + function f() { + l1: { + [#| + let x = 1 + if (x) { + break l1; + } + |] + } + } +} + `, + [ + "Cannot extract range containing labeled break or continue with target outside of the range." + ]); + + testExtractRangeFailed("extractRangeFailed5", + ` +namespace A { + function f() { + [#| + try { + f2() + return 10; + } + catch (e) { + } + |] + } + function f2() { + } +} + `, + [ + "Cannot extract range containing conditional return statement." + ]); + + testExtractRangeFailed("extractRangeFailed6", + ` +namespace A { + function f() { + [#| + try { + f2() + } + catch (e) { + return 10; + } + |] + } + function f2() { + } +} + `, + [ + "Cannot extract range containing conditional return statement." + ]); + + testExtractMethod("extractMethod1", + `namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + [#| + let y = 5; + let z = x; + a = y; + foo();|] + } + } +}`); + testExtractMethod("extractMethod2", + `namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + [#| + let y = 5; + let z = x; + return foo();|] + } + } +}`); + testExtractMethod("extractMethod3", + `namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + [#| + let y = 5; + yield z; + return foo();|] + } + } +}`); + testExtractMethod("extractMethod4", + `namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + [#| + let y = 5; + if (z) { + await z1; + } + return foo();|] + } + } +}`); + testExtractMethod("extractMethod5", + `namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + [#| + let y = 5; + let z = x; + a = y; + foo();|] + } + } +}`); + testExtractMethod("extractMethod6", + `namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + [#| + let y = 5; + let z = x; + a = y; + return foo();|] + } + } +}`); + testExtractMethod("extractMethod7", + `namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + [#| + let y = 5; + let z = x; + a = y; + return C.foo();|] + } + } +}`); + testExtractMethod("extractMethod8", + `namespace A { + let x = 1; + namespace B { + function a() { + let a1 = 1; + return 1 + [#|a1 + x|] + 100; + } + } +}`); + testExtractMethod("extractMethod9", + `namespace A { + export interface I { x: number }; + namespace B { + function a() { + [#|let a1: I = { x: 1 }; + return a1.x + 10;|] + } + } +}`); + testExtractMethod("extractMethod10", + `namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + [#|let a1: I = { x: 1 }; + return a1.x + 10;|] + } + } +}`); + testExtractMethod("extractMethod11", + `namespace A { + let y = 1; + class C { + a() { + let z = 1; + [#|let a1 = { x: 1 }; + y = 10; + z = 42; + return a1.x + 10;|] + } + } +}`); + testExtractMethod("extractMethod12", + `namespace A { + let y = 1; + class C { + b() {} + a() { + let z = 1; + [#|let a1 = { x: 1 }; + y = 10; + z = 42; + this.b(); + return a1.x + 10;|] + } + } +}`); + }); + + + function testExtractMethod(caption: string, text: string) { + it(caption, () => { + Harness.Baseline.runBaseline(`extractMethod/${caption}.js`, () => { + const t = extractTest(text); + const selectionRange = t.ranges.get("selection"); + if (!selectionRange) { + throw new Error(`Test ${caption} does not specify selection range`); + } + const f = { + path: "/a.ts", + content: t.source + }; + const host = projectSystem.createServerHost([f]); + const projectService = projectSystem.createProjectService(host); + projectService.openClientFile(f.path); + const program = projectService.inferredProjects[0].getLanguageService().getProgram(); + const sourceFile = program.getSourceFile(f.path); + const context: RefactorContext = { + cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, + newLineCharacter, + program, + file: sourceFile, + startPosition: -1, + rulesProvider: getRuleProvider() + }; + const result = refactor.extractMethod.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + assert.equal(result.errors, undefined, "expect no errors"); + const results = refactor.extractMethod.getPossibleExtractions(result.targetRange, context); + const data: string[] = []; + data.push(`==ORIGINAL==`); + data.push(sourceFile.text); + for (const r of results) { + const changes = refactor.extractMethod.getPossibleExtractions(result.targetRange, context, results.indexOf(r))[0].changes; + data.push(`==SCOPE::${r.scopeDescription}==`); + data.push(textChanges.applyChanges(sourceFile.text, changes[0].textChanges)); + } + return data.join(newLineCharacter); + }); + }); + } +} diff --git a/src/harness/unittests/matchFiles.ts b/src/harness/unittests/matchFiles.ts index e0454671930..f2c2369ef37 100644 --- a/src/harness/unittests/matchFiles.ts +++ b/src/harness/unittests/matchFiles.ts @@ -73,6 +73,7 @@ namespace ts { "c:/dev/a.d.ts", "c:/dev/a.js", "c:/dev/b.ts", + "c:/dev/x/a.ts", "c:/dev/node_modules/a.ts", "c:/dev/bower_components/a.ts", "c:/dev/jspm_packages/a.ts" @@ -109,23 +110,21 @@ namespace ts { } { const actual = ts.parseJsonConfigFileContent(json, host, basePath, existingOptions, configFileName, resolutionStack); - expected.errors = map(expected.errors, error => { - return { - category: error.category, - code: error.code, - file: undefined, - length: undefined, - messageText: error.messageText, - start: undefined, - }; - }); + expected.errors = expected.errors.map(error => ({ + category: error.category, + code: error.code, + file: undefined, + length: undefined, + messageText: error.messageText, + start: undefined, + })); assertParsed(actual, expected); } } function createDiagnosticForConfigFile(json: any, start: number, length: number, diagnosticMessage: DiagnosticMessage, arg0: string) { const text = JSON.stringify(json); - const file = { + const file = { // tslint:disable-line no-object-literal-type-assertion fileName: caseInsensitiveTsconfigPath, kind: SyntaxKind.SourceFile, text @@ -141,7 +140,8 @@ namespace ts { errors: [], fileNames: [ "c:/dev/a.ts", - "c:/dev/b.ts" + "c:/dev/b.ts", + "c:/dev/x/a.ts" ], wildcardDirectories: { "c:/dev": ts.WatchDirectoryFlags.Recursive @@ -462,7 +462,6 @@ namespace ts { }; validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath); }); - it("same named declarations are excluded", () => { const json = { include: [ @@ -651,71 +650,127 @@ namespace ts { }; validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath); }); - it("with common package folders and no exclusions", () => { - const json = { - include: [ - "**/a.ts" - ] - }; - const expected: ts.ParsedCommandLine = { - options: {}, - errors: [], - fileNames: [ - "c:/dev/a.ts", - "c:/dev/bower_components/a.ts", - "c:/dev/jspm_packages/a.ts", - "c:/dev/node_modules/a.ts" - ], - wildcardDirectories: { - "c:/dev": ts.WatchDirectoryFlags.Recursive - }, - }; - validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); - }); - it("with common package folders and exclusions", () => { - const json = { - include: [ - "**/a.ts" - ], - exclude: [ - "a.ts" - ] - }; - const expected: ts.ParsedCommandLine = { - options: {}, - errors: [], - fileNames: [ - "c:/dev/bower_components/a.ts", - "c:/dev/jspm_packages/a.ts", - "c:/dev/node_modules/a.ts" - ], - wildcardDirectories: { - "c:/dev": ts.WatchDirectoryFlags.Recursive - }, - }; - validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); - }); - it("with common package folders and empty exclude", () => { - const json = { - include: [ - "**/a.ts" - ], - exclude: [] - }; - const expected: ts.ParsedCommandLine = { - options: {}, - errors: [], - fileNames: [ - "c:/dev/a.ts", - "c:/dev/bower_components/a.ts", - "c:/dev/jspm_packages/a.ts", - "c:/dev/node_modules/a.ts" - ], - wildcardDirectories: { - "c:/dev": ts.WatchDirectoryFlags.Recursive - }, - }; - validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + describe("with common package folders", () => { + it("and no exclusions", () => { + const json = { + include: [ + "**/a.ts" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/a.ts", + "c:/dev/x/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); + it("and exclusions", () => { + const json = { + include: [ + "**/?.ts" + ], + exclude: [ + "a.ts" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/b.ts", + "c:/dev/x/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); + it("and empty exclude", () => { + const json = { + include: [ + "**/a.ts" + ], + exclude: [] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/a.ts", + "c:/dev/x/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); + it("and explicit recursive include", () => { + const json = { + include: [ + "**/a.ts", + "**/node_modules/a.ts" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/a.ts", + "c:/dev/x/a.ts", + "c:/dev/node_modules/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); + it("and wildcard include", () => { + const json = { + include: [ + "*/a.ts" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/x/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); + it("and explicit wildcard include", () => { + const json = { + include: [ + "*/a.ts", + "node_modules/a.ts" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/x/a.ts", + "c:/dev/node_modules/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); }); it("exclude .js files when allowJs=false", () => { const json = { @@ -1066,6 +1121,7 @@ namespace ts { }; validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath); }); + describe("with trailing recursive directory", () => { it("in includes", () => { const json = { @@ -1264,6 +1320,7 @@ namespace ts { }); }); }); + describe("with files or folders that begin with a .", () => { it("that are not explicitly included", () => { const json = { diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index ffae1b5f4a9..79aafb4986d 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -26,10 +26,19 @@ namespace ts { interface File { name: string; content?: string; + symlinks?: string[]; } function createModuleResolutionHost(hasDirectoryExists: boolean, ...files: File[]): ModuleResolutionHost { - const map = arrayToMap(files, f => f.name); + const map = createMap(); + for (const file of files) { + map.set(file.name, file); + if (file.symlinks) { + for (const symlink of file.symlinks) { + map.set(symlink, file); + } + } + } if (hasDirectoryExists) { const directories = createMap(); @@ -46,6 +55,7 @@ namespace ts { } return { readFile, + realpath, directoryExists: path => directories.has(path), fileExists: path => { assert.isTrue(directories.has(getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`); @@ -54,12 +64,15 @@ namespace ts { }; } else { - return { readFile, fileExists: path => map.has(path) }; + return { readFile, realpath, fileExists: path => map.has(path) }; } function readFile(path: string): string | undefined { const file = map.get(path); return file && file.content; } + function realpath(path: string): string { + return map.get(path).name; + } } describe("Node module resolution - relative paths", () => { @@ -233,8 +246,8 @@ namespace ts { test(/*hasDirectoryExists*/ true); function test(hasDirectoryExists: boolean) { - const containingFile = { name: "/a/node_modules/b/c/node_modules/d/e.ts" }; - const moduleFile = { name: "/a/node_modules/foo/index.d.ts" }; + const containingFile: File = { name: "/a/node_modules/b/c/node_modules/d/e.ts" }; + const moduleFile: File = { name: "/a/node_modules/foo/index.d.ts" }; const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile)); checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true), [ "/a/node_modules/b/c/node_modules/d/node_modules/foo.ts", @@ -289,6 +302,19 @@ namespace ts { ]); } }); + + testPreserveSymlinks(/*preserveSymlinks*/ false); + testPreserveSymlinks(/*preserveSymlinks*/ true); + function testPreserveSymlinks(preserveSymlinks: boolean) { + it(`preserveSymlinks: ${preserveSymlinks}`, () => { + const realFileName = "/linked/index.d.ts"; + const symlinkFileName = "/app/node_modulex/linked/index.d.ts"; + const host = createModuleResolutionHost(/*hasDirectoryExists*/ true, { name: realFileName, symlinks: [symlinkFileName] }); + const resolution = nodeModuleNameResolver("linked", "/app/app.ts", { preserveSymlinks }, host); + const resolvedFileName = preserveSymlinks ? symlinkFileName : realFileName; + checkResolvedModule(resolution.resolvedModule, { resolvedFileName, isExternalLibraryImport: true, extension: Extension.Dts }); + }); + } }); describe("Module resolution - relative imports", () => { diff --git a/src/harness/unittests/projectErrors.ts b/src/harness/unittests/projectErrors.ts index 0143aee7701..d72168383c1 100644 --- a/src/harness/unittests/projectErrors.ts +++ b/src/harness/unittests/projectErrors.ts @@ -4,12 +4,12 @@ namespace ts.projectSystem { describe("Project errors", () => { - function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: string[]) { + function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: ReadonlyArray): void { assert.isTrue(projectFiles !== undefined, "missing project files"); checkProjectErrorsWorker(projectFiles.projectErrors, expectedErrors); } - function checkProjectErrorsWorker(errors: Diagnostic[], expectedErrors: string[]) { + function checkProjectErrorsWorker(errors: ReadonlyArray, expectedErrors: ReadonlyArray): void { assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`); if (expectedErrors.length) { for (let i = 0; i < errors.length; i++) { @@ -23,11 +23,9 @@ namespace ts.projectSystem { function checkDiagnosticsWithLinePos(errors: server.protocol.DiagnosticWithLinePosition[], expectedErrors: string[]) { assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`); if (expectedErrors.length) { - for (let i = 0; i < errors.length; i++) { - const actualMessage = errors[i].message; - const expectedMessage = expectedErrors[i]; - assert.isTrue(actualMessage.indexOf(errors[i].message) === 0, `error message does not match, expected ${actualMessage} to start with ${expectedMessage}`); - } + zipWith(errors, expectedErrors, ({ message: actualMessage }, expectedMessage) => { + assert.isTrue(startsWith(actualMessage, actualMessage), `error message does not match, expected ${actualMessage} to start with ${expectedMessage}`); + }); } } @@ -40,12 +38,11 @@ namespace ts.projectSystem { path: "/a/b/applib.ts", content: "" }; - // only file1 exists - expect error const host = createServerHost([file1, libFile]); const session = createSession(host); const projectService = session.getProjectService(); const projectFileName = "/a/b/test.csproj"; - const compilerOptionsRequest = { + const compilerOptionsRequest: server.protocol.CompilerOptionsDiagnosticsRequest = { type: "request", command: server.CommandNames.CompilerOptionsDiagnosticsFull, seq: 2, @@ -61,19 +58,20 @@ namespace ts.projectSystem { checkNumberOfProjects(projectService, { externalProjects: 1 }); const diags = session.executeCommand(compilerOptionsRequest).response; + // only file1 exists - expect error checkDiagnosticsWithLinePos(diags, ["File '/a/b/applib.ts' not found."]); } - // only file2 exists - expect error host.reloadFS([file2, libFile]); { + // only file2 exists - expect error checkNumberOfProjects(projectService, { externalProjects: 1 }); const diags = session.executeCommand(compilerOptionsRequest).response; checkDiagnosticsWithLinePos(diags, ["File '/a/b/app.ts' not found."]); } - // both files exist - expect no errors host.reloadFS([file1, file2, libFile]); { + // both files exist - expect no errors checkNumberOfProjects(projectService, { externalProjects: 1 }); const diags = session.executeCommand(compilerOptionsRequest).response; checkDiagnosticsWithLinePos(diags, []); @@ -99,7 +97,7 @@ namespace ts.projectSystem { openFilesForSession([file1], session); checkNumberOfProjects(projectService, { configuredProjects: 1 }); const project = configuredProjectAt(projectService, 0); - const compilerOptionsRequest = { + const compilerOptionsRequest: server.protocol.CompilerOptionsDiagnosticsRequest = { type: "request", command: server.CommandNames.CompilerOptionsDiagnosticsFull, seq: 2, diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index a434ed8025d..f2e269763ab 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -109,7 +109,10 @@ namespace ts { function createTestCompilerHost(texts: NamedSourceText[], target: ScriptTarget, oldProgram?: ProgramWithSourceTexts): TestCompilerHost { const files = arrayToMap(texts, t => t.name, t => { if (oldProgram) { - const oldFile = oldProgram.getSourceFile(t.name); + let oldFile = oldProgram.getSourceFile(t.name); + if (oldFile && oldFile.redirectInfo) { + oldFile = oldFile.redirectInfo.unredirected; + } if (oldFile && oldFile.sourceText.getVersion() === t.text.getVersion()) { return oldFile; } @@ -171,11 +174,16 @@ namespace ts { return program; } + function updateProgramText(files: ReadonlyArray, fileName: string, newProgramText: string) { + const file = find(files, f => f.name === fileName)!; + file.text = file.text.updateProgram(newProgramText); + } + function checkResolvedTypeDirective(expected: ResolvedTypeReferenceDirective, actual: ResolvedTypeReferenceDirective): boolean { if (!expected === !actual) { if (expected) { - assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); - assert.isTrue(expected.primary === actual.primary, `'primary': expected '${expected.primary}' to be equal to '${actual.primary}'`); + assert.equal(expected.resolvedFileName, actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); + assert.equal(expected.primary, actual.primary, `'primary': expected '${expected.primary}' to be equal to '${actual.primary}'`); } return true; } @@ -238,7 +246,7 @@ namespace ts { const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => { files[0].text = files[0].text.updateProgram("var x = 100"); }); - assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely); + assert.equal(program_1.structureIsReused, StructureIsReused.Completely); const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts")); const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts")); assert.equal(program1Diagnostics.length, program2Diagnostics.length); @@ -249,7 +257,7 @@ namespace ts { const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => { files[0].text = files[0].text.updateProgram("var x = 100"); }); - assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely); + assert.equal(program_1.structureIsReused, StructureIsReused.Completely); const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts")); const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts")); assert.equal(program1Diagnostics.length, program2Diagnostics.length); @@ -263,19 +271,19 @@ namespace ts { `; files[0].text = files[0].text.updateReferences(newReferences); }); - assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules); + assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules); }); it("fails if change affects type references", () => { const program_1 = newProgram(files, ["a.ts"], { types: ["a"] }); updateProgram(program_1, ["a.ts"], { types: ["b"] }, noop); - assert.isTrue(program_1.structureIsReused === StructureIsReused.Not); + assert.equal(program_1.structureIsReused, StructureIsReused.Not); }); it("succeeds if change doesn't affect type references", () => { const program_1 = newProgram(files, ["a.ts"], { types: ["a"] }); updateProgram(program_1, ["a.ts"], { types: ["a"] }, noop); - assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely); + assert.equal(program_1.structureIsReused, StructureIsReused.Completely); }); it("fails if change affects imports", () => { @@ -283,7 +291,7 @@ namespace ts { updateProgram(program_1, ["a.ts"], { target }, files => { files[2].text = files[2].text.updateImportsAndExports("import x from 'b'"); }); - assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules); + assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules); }); it("fails if change affects type directives", () => { @@ -295,25 +303,25 @@ namespace ts { /// `; files[0].text = files[0].text.updateReferences(newReferences); }); - assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules); + assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules); }); it("fails if module kind changes", () => { const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS }); updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.AMD }, noop); - assert.isTrue(program_1.structureIsReused === StructureIsReused.Not); + assert.equal(program_1.structureIsReused, StructureIsReused.Not); }); it("fails if rootdir changes", () => { const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/b" }); updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/c" }, noop); - assert.isTrue(program_1.structureIsReused === StructureIsReused.Not); + assert.equal(program_1.structureIsReused, StructureIsReused.Not); }); it("fails if config path changes", () => { const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/b/tsconfig.json" }); updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, noop); - assert.isTrue(program_1.structureIsReused === StructureIsReused.Not); + assert.equal(program_1.structureIsReused, StructureIsReused.Not); }); it("succeeds if missing files remain missing", () => { @@ -357,7 +365,7 @@ namespace ts { const program_2 = updateProgram(program_1, ["a.ts"], options, files => { files[0].text = files[0].text.updateProgram("var x = 2"); }); - assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely); + assert.equal(program_1.structureIsReused, StructureIsReused.Completely); // content of resolution cache should not change checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") })); @@ -367,7 +375,7 @@ namespace ts { const program_3 = updateProgram(program_2, ["a.ts"], options, files => { files[0].text = files[0].text.updateImportsAndExports(""); }); - assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules); + assert.equal(program_2.structureIsReused, StructureIsReused.SafeModules); checkResolvedModulesCache(program_3, "a.ts", /*expectedContent*/ undefined); const program_4 = updateProgram(program_3, ["a.ts"], options, files => { @@ -376,7 +384,7 @@ namespace ts { `; files[0].text = files[0].text.updateImportsAndExports(newImports); }); - assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules); + assert.equal(program_3.structureIsReused, StructureIsReused.SafeModules); checkResolvedModulesCache(program_4, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts"), "c": undefined })); }); @@ -394,7 +402,7 @@ namespace ts { const program_2 = updateProgram(program_1, ["/a.ts"], options, files => { files[0].text = files[0].text.updateProgram("var x = 2"); }); - assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely); + assert.equal(program_1.structureIsReused, StructureIsReused.Completely); // content of resolution cache should not change checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); @@ -405,7 +413,7 @@ namespace ts { files[0].text = files[0].text.updateReferences(""); }); - assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules); + assert.equal(program_2.structureIsReused, StructureIsReused.SafeModules); checkResolvedTypeDirectivesCache(program_3, "/a.ts", /*expectedContent*/ undefined); updateProgram(program_3, ["/a.ts"], options, files => { @@ -414,7 +422,7 @@ namespace ts { `; files[0].text = files[0].text.updateReferences(newReferences); }); - assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules); + assert.equal(program_3.structureIsReused, StructureIsReused.SafeModules); checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); }); @@ -454,7 +462,7 @@ namespace ts { "initialProgram: execute module resolution normally."); const initialProgramDiagnostics = initialProgram.getSemanticDiagnostics(initialProgram.getSourceFile("file1.ts")); - assert(initialProgramDiagnostics.length === 1, `initialProgram: import should fail.`); + assert.lengthOf(initialProgramDiagnostics, 1, `initialProgram: import should fail.`); } const afterNpmInstallProgram = updateProgram(initialProgram, rootFiles.map(f => f.name), options, f => { @@ -478,7 +486,7 @@ namespace ts { "afterNpmInstallProgram: execute module resolution normally."); const afterNpmInstallProgramDiagnostics = afterNpmInstallProgram.getSemanticDiagnostics(afterNpmInstallProgram.getSourceFile("file1.ts")); - assert(afterNpmInstallProgramDiagnostics.length === 0, `afterNpmInstallProgram: program is well-formed with import.`); + assert.lengthOf(afterNpmInstallProgramDiagnostics, 0, `afterNpmInstallProgram: program is well-formed with import.`); } }); @@ -617,10 +625,10 @@ namespace ts { "File 'f1.ts' exist - use it as a name resolution result.", "======== Module name './f1' was successfully resolved to 'f1.ts'. ========" ], - "program_1: execute module reoslution normally."); + "program_1: execute module resolution normally."); const program_1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("f2.ts")); - assert(program_1Diagnostics.length === expectedErrors, `initial program should be well-formed`); + assert.lengthOf(program_1Diagnostics, expectedErrors, `initial program should be well-formed`); } const indexOfF1 = 6; const program_2 = updateProgram(program_1, program_1.getRootFileNames(), options, f => { @@ -630,7 +638,7 @@ namespace ts { { const program_2Diagnostics = program_2.getSemanticDiagnostics(program_2.getSourceFile("f2.ts")); - assert(program_2Diagnostics.length === expectedErrors, `removing no-default-lib shouldn't affect any types used.`); + assert.lengthOf(program_2Diagnostics, expectedErrors, `removing no-default-lib shouldn't affect any types used.`); assert.deepEqual(program_2.host.getTrace(), [ "======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========", @@ -659,7 +667,7 @@ namespace ts { { const program_3Diagnostics = program_3.getSemanticDiagnostics(program_3.getSourceFile("f2.ts")); - assert(program_3Diagnostics.length === expectedErrors, `typerefs2 was unused, so diagnostics should be unaffected.`); + assert.lengthOf(program_3Diagnostics, expectedErrors, `typerefs2 was unused, so diagnostics should be unaffected.`); assert.deepEqual(program_3.host.getTrace(), [ "======== Resolving module './b1' from 'f1.ts'. ========", @@ -684,7 +692,7 @@ namespace ts { { const program_4Diagnostics = program_4.getSemanticDiagnostics(program_4.getSourceFile("f2.ts")); - assert(program_4Diagnostics.length === expectedErrors, `a1.ts was unused, so diagnostics should be unaffected.`); + assert.lengthOf(program_4Diagnostics, expectedErrors, `a1.ts was unused, so diagnostics should be unaffected.`); assert.deepEqual(program_4.host.getTrace(), [ "======== Resolving module './b1' from 'f1.ts'. ========", @@ -708,7 +716,7 @@ namespace ts { { const program_5Diagnostics = program_5.getSemanticDiagnostics(program_5.getSourceFile("f2.ts")); - assert(program_5Diagnostics.length === ++expectedErrors, `import of BB in f1 fails. BB is of type any. Add one error`); + assert.lengthOf(program_5Diagnostics, ++expectedErrors, `import of BB in f1 fails. BB is of type any. Add one error`); assert.deepEqual(program_5.host.getTrace(), [ "======== Resolving module './b1' from 'f1.ts'. ========", @@ -725,7 +733,7 @@ namespace ts { { const program_6Diagnostics = program_6.getSemanticDiagnostics(program_6.getSourceFile("f2.ts")); - assert(program_6Diagnostics.length === expectedErrors, `import of BB in f1 fails.`); + assert.lengthOf(program_6Diagnostics, expectedErrors, `import of BB in f1 fails.`); assert.deepEqual(program_6.host.getTrace(), [ "======== Resolving module './b1' from 'f1.ts'. ========", @@ -749,7 +757,7 @@ namespace ts { { const program_7Diagnostics = program_7.getSemanticDiagnostics(program_7.getSourceFile("f2.ts")); - assert(program_7Diagnostics.length === expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`); + assert.lengthOf(program_7Diagnostics, expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`); assert.deepEqual(program_7.host.getTrace(), [ "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========", @@ -762,6 +770,98 @@ namespace ts { ], "program_7 should reuse module resolutions in f2 since it is unchanged"); } }); + + describe("redirects", () => { + const axIndex = "/node_modules/a/node_modules/x/index.d.ts"; + const axPackage = "/node_modules/a/node_modules/x/package.json"; + const bxIndex = "/node_modules/b/node_modules/x/index.d.ts"; + const bxPackage = "/node_modules/b/node_modules/x/package.json"; + const root = "/a.ts"; + const compilerOptions = { target, moduleResolution: ModuleResolutionKind.NodeJs }; + + function createRedirectProgram(options?: { bText: string, bVersion: string }): ProgramWithSourceTexts { + const files: NamedSourceText[] = [ + { + name: "/node_modules/a/index.d.ts", + text: SourceText.New("", 'import X from "x";', "export function a(x: X): void;"), + }, + { + name: axIndex, + text: SourceText.New("", "", "export default class X { private x: number; }"), + }, + { + name: axPackage, + text: SourceText.New("", "", JSON.stringify({ name: "x", version: "1.2.3" })), + }, + { + name: "/node_modules/b/index.d.ts", + text: SourceText.New("", 'import X from "x";', "export const b: X;"), + }, + { + name: bxIndex, + text: SourceText.New("", "", options ? options.bText : "export default class X { private x: number; }"), + }, + { + name: bxPackage, + text: SourceText.New("", "", JSON.stringify({ name: "x", version: options ? options.bVersion : "1.2.3" })), + }, + { + name: root, + text: SourceText.New("", 'import { a } from "a"; import { b } from "b";', "a(b)"), + }, + ]; + + return newProgram(files, [root], compilerOptions); + } + + function updateRedirectProgram(program: ProgramWithSourceTexts, updater: (files: NamedSourceText[]) => void): ProgramWithSourceTexts { + return updateProgram(program, [root], compilerOptions, updater); + } + + it("No changes -> redirect not broken", () => { + const program_1 = createRedirectProgram(); + + const program_2 = updateRedirectProgram(program_1, files => { + updateProgramText(files, root, "const x = 1;"); + }); + assert.equal(program_1.structureIsReused, StructureIsReused.Completely); + assert.deepEqual(program_2.getSemanticDiagnostics(), emptyArray); + }); + + it("Target changes -> redirect broken", () => { + const program_1 = createRedirectProgram(); + assert.deepEqual(program_1.getSemanticDiagnostics(), emptyArray); + + const program_2 = updateRedirectProgram(program_1, files => { + updateProgramText(files, axIndex, "export default class X { private x: number; private y: number; }"); + updateProgramText(files, axPackage, JSON.stringify('{ name: "x", version: "1.2.4" }')); + }); + assert.equal(program_1.structureIsReused, StructureIsReused.Not); + assert.lengthOf(program_2.getSemanticDiagnostics(), 1); + }); + + it("Underlying changes -> redirect broken", () => { + const program_1 = createRedirectProgram(); + + const program_2 = updateRedirectProgram(program_1, files => { + updateProgramText(files, bxIndex, "export default class X { private x: number; private y: number; }"); + updateProgramText(files, bxPackage, JSON.stringify({ name: "x", version: "1.2.4" })); + }); + assert.equal(program_1.structureIsReused, StructureIsReused.Not); + assert.lengthOf(program_2.getSemanticDiagnostics(), 1); + }); + + it("Previously duplicate packages -> program structure not reused", () => { + const program_1 = createRedirectProgram({ bVersion: "1.2.4", bText: "export = class X { private x: number; }" }); + + const program_2 = updateRedirectProgram(program_1, files => { + updateProgramText(files, bxIndex, "export default class X { private x: number; }"); + updateProgramText(files, bxPackage, JSON.stringify({ name: "x", version: "1.2.3" })); + }); + assert.equal(program_1.structureIsReused, StructureIsReused.Not); + assert.deepEqual(program_2.getSemanticDiagnostics(), []); + }); + }); }); describe("host is optional", () => { diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 31de6356090..0a60935e28f 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -28,18 +28,6 @@ namespace ts.server { createHash: Harness.mockHash, }; - const mockLogger: Logger = { - close: noop, - hasLevel(): boolean { return false; }, - loggingEnabled(): boolean { return false; }, - perftrc: noop, - info: noop, - startGroup: noop, - endGroup: noop, - msg: noop, - getLogFileName: (): string => undefined - }; - class TestSession extends Session { getProjectService() { return this.projectService; @@ -55,10 +43,11 @@ namespace ts.server { host: mockHost, cancellationToken: nullCancellationToken, useSingleInferredProject: false, + useInferredProjectPerProjectRoot: false, typingsInstaller: undefined, byteLength: Utils.byteLength, hrtime: process.hrtime, - logger: mockLogger, + logger: projectSystem.nullLogger, canUseEvents: true }; return new TestSession(opts); @@ -93,14 +82,15 @@ namespace ts.server { session.executeCommand(req); - expect(lastSent).to.deep.equal({ + const expected: protocol.Response = { command: CommandNames.Unknown, type: "response", seq: 0, message: "Unrecognized JSON command: foobar", request_seq: 0, success: false - }); + }; + expect(lastSent).to.deep.equal(expected); }); it("should return a tuple containing the response and if a response is required on success", () => { const req: protocol.ConfigureRequest = { @@ -405,10 +395,11 @@ namespace ts.server { host: mockHost, cancellationToken: nullCancellationToken, useSingleInferredProject: false, + useInferredProjectPerProjectRoot: false, typingsInstaller: undefined, byteLength: Utils.byteLength, hrtime: process.hrtime, - logger: mockLogger, + logger: projectSystem.nullLogger, canUseEvents: true }); this.addProtocolHandler(this.customHandler, () => { @@ -472,10 +463,11 @@ namespace ts.server { host: mockHost, cancellationToken: nullCancellationToken, useSingleInferredProject: false, + useInferredProjectPerProjectRoot: false, typingsInstaller: undefined, byteLength: Utils.byteLength, hrtime: process.hrtime, - logger: mockLogger, + logger: projectSystem.nullLogger, canUseEvents: true }); this.addProtocolHandler("echo", (req: protocol.Request) => ({ diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 8d6587fc2b3..e8cba10f088 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -4,19 +4,15 @@ namespace ts.tscWatch { - export import WatchedSystem = ts.TestFSWithWatch.TestServerHost; - export type TestServerHostCreationParameters = ts.TestFSWithWatch.TestServerHostCreationParameters; - export type File = ts.TestFSWithWatch.File; - export type FileOrFolder = ts.TestFSWithWatch.FileOrFolder; - export type Folder = ts.TestFSWithWatch.Folder; - export type FSEntry = ts.TestFSWithWatch.FSEntry; - export import createWatchedSystem = ts.TestFSWithWatch.createWatchedSystem; - export import checkFileNames = ts.TestFSWithWatch.checkFileNames; - export import libFile = ts.TestFSWithWatch.libFile; - export import checkWatchedFiles = ts.TestFSWithWatch.checkWatchedFiles; - export import checkWatchedDirectories = ts.TestFSWithWatch.checkWatchedDirectories; - export import checkOutputContains = ts.TestFSWithWatch.checkOutputContains; - export import checkOutputDoesNotContain = ts.TestFSWithWatch.checkOutputDoesNotContain; + import WatchedSystem = ts.TestFSWithWatch.TestServerHost; + type FileOrFolder = ts.TestFSWithWatch.FileOrFolder; + import createWatchedSystem = ts.TestFSWithWatch.createWatchedSystem; + import checkFileNames = ts.TestFSWithWatch.checkFileNames; + import libFile = ts.TestFSWithWatch.libFile; + import checkWatchedFiles = ts.TestFSWithWatch.checkWatchedFiles; + import checkWatchedDirectories = ts.TestFSWithWatch.checkWatchedDirectories; + import checkOutputContains = ts.TestFSWithWatch.checkOutputContains; + import checkOutputDoesNotContain = ts.TestFSWithWatch.checkOutputDoesNotContain; export function checkProgramActualFiles(program: Program, expectedFiles: string[]) { checkFileNames(`Program actual files`, program.getSourceFiles().map(file => file.fileName), expectedFiles); diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 39776e379c9..446634c0535 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -8,16 +8,12 @@ namespace ts.projectSystem { import CommandNames = server.CommandNames; export import TestServerHost = ts.TestFSWithWatch.TestServerHost; - export type TestServerHostCreationParameters = ts.TestFSWithWatch.TestServerHostCreationParameters; - export type File = ts.TestFSWithWatch.File; export type FileOrFolder = ts.TestFSWithWatch.FileOrFolder; - export type Folder = ts.TestFSWithWatch.Folder; - export type FSEntry = ts.TestFSWithWatch.FSEntry; export import createServerHost = ts.TestFSWithWatch.createServerHost; export import checkFileNames = ts.TestFSWithWatch.checkFileNames; export import libFile = ts.TestFSWithWatch.libFile; export import checkWatchedFiles = ts.TestFSWithWatch.checkWatchedFiles; - export import checkWatchedDirectories = ts.TestFSWithWatch.checkWatchedDirectories; + import checkWatchedDirectories = ts.TestFSWithWatch.checkWatchedDirectories; import safeList = ts.TestFSWithWatch.safeList; const customSafeList = { @@ -36,14 +32,13 @@ namespace ts.projectSystem { } export const nullLogger: server.Logger = { - close: () => void 0, - hasLevel: () => void 0, + close: noop, + hasLevel: () => false, loggingEnabled: () => false, - perftrc: () => void 0, - info: () => void 0, - startGroup: () => void 0, - endGroup: () => void 0, - msg: () => void 0, + perftrc: noop, + info: noop, + err: noop, + group: noop, getLogFileName: (): string => undefined }; @@ -122,7 +117,7 @@ namespace ts.projectSystem { return map(fileNames, toExternalFile); } - export class TestServerEventManager { + class TestServerEventManager { public events: server.ProjectServiceEvent[] = []; handler: server.ProjectServiceEventHandler = (event: server.ProjectServiceEvent) => { @@ -158,26 +153,31 @@ namespace ts.projectSystem { } } - export function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller, projectServiceEventHandler?: server.ProjectServiceEventHandler, cancellationToken?: server.ServerCancellationToken, throttleWaitMilliseconds?: number) { - if (typingsInstaller === undefined) { - typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host); + export function createSession(host: server.ServerHost, opts: Partial = {}) { + if (opts.typingsInstaller === undefined) { + opts.typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/ 5, host); } - const opts: server.SessionOptions = { + + if (opts.eventHandler !== undefined) { + opts.canUseEvents = true; + } + + const sessionOptions: server.SessionOptions = { host, - cancellationToken: cancellationToken || server.nullCancellationToken, + cancellationToken: server.nullCancellationToken, useSingleInferredProject: false, - typingsInstaller, + useInferredProjectPerProjectRoot: false, + typingsInstaller: undefined, byteLength: Utils.byteLength, hrtime: process.hrtime, logger: nullLogger, - canUseEvents: projectServiceEventHandler !== undefined, - eventHandler: projectServiceEventHandler, - throttleWaitMilliseconds + canUseEvents: false }; - return new TestSession(opts); + + return new TestSession({ ...sessionOptions, ...opts }); } - export interface CreateProjectServiceParameters { + interface CreateProjectServiceParameters { cancellationToken?: HostCancellationToken; logger?: server.Logger; useSingleInferredProject?: boolean; @@ -187,9 +187,16 @@ namespace ts.projectSystem { export class TestProjectService extends server.ProjectService { constructor(host: server.ServerHost, logger: server.Logger, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean, - typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler) { + typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler, opts: Partial = {}) { super({ - host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, eventHandler + host, + logger, + cancellationToken, + useSingleInferredProject, + useInferredProjectPerProjectRoot: false, + typingsInstaller, + eventHandler, + ...opts }); } @@ -208,11 +215,11 @@ namespace ts.projectSystem { assert.equal(projectService.configuredProjects.size, expected, `expected ${expected} configured project(s)`); } - export function checkNumberOfExternalProjects(projectService: server.ProjectService, expected: number) { + function checkNumberOfExternalProjects(projectService: server.ProjectService, expected: number) { assert.equal(projectService.externalProjects.length, expected, `expected ${expected} external project(s)`); } - export function checkNumberOfInferredProjects(projectService: server.ProjectService, expected: number) { + function checkNumberOfInferredProjects(projectService: server.ProjectService, expected: number) { assert.equal(projectService.inferredProjects.length, expected, `expected ${expected} inferred project(s)`); } @@ -235,7 +242,7 @@ namespace ts.projectSystem { checkFileNames(`${server.ProjectKind[project.projectKind]} project, actual files`, project.getFileNames(), expectedFiles); } - export function checkProjectRootFiles(project: server.Project, expectedFiles: string[]) { + function checkProjectRootFiles(project: server.Project, expectedFiles: string[]) { checkFileNames(`${server.ProjectKind[project.projectKind]} project, rootFileNames`, project.getRootFiles(), expectedFiles); } @@ -299,7 +306,21 @@ namespace ts.projectSystem { } } - describe("tsserver-project-system", () => { + type ErrorInformation = { diagnosticMessage: DiagnosticMessage, errorTextArguments?: string[] }; + function getProtocolDiagnosticMessage({ diagnosticMessage, errorTextArguments = [] }: ErrorInformation) { + return formatStringFromArgs(diagnosticMessage.message, errorTextArguments); + } + + function verifyDiagnostics(actual: server.protocol.Diagnostic[], expected: ErrorInformation[]) { + const expectedErrors = expected.map(getProtocolDiagnosticMessage); + assert.deepEqual(actual.map(diag => flattenDiagnosticMessageText(diag.text, "\n")), expectedErrors); + } + + function verifyNoDiagnostics(actual: server.protocol.Diagnostic[]) { + verifyDiagnostics(actual, []); + } + + describe("tsserverProjectSystem", () => { const commonFile1: FileOrFolder = { path: "/a/b/commonFile1.ts", content: "let x = 1" @@ -410,6 +431,45 @@ namespace ts.projectSystem { checkWatchedDirectories(host, [getDirectoryPath(configFile.path)], /*recursive*/ true); }); + it("create configured project with the file list", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: ` + { + "compilerOptions": {}, + "include": ["*.ts"] + }` + }; + const file1: FileOrFolder = { + path: "/a/b/f1.ts", + content: "let x = 1" + }; + const file2: FileOrFolder = { + path: "/a/b/f2.ts", + content: "let y = 1" + }; + const file3: FileOrFolder = { + path: "/a/b/c/f3.ts", + content: "let z = 1" + }; + + const host = createServerHost([configFile, libFile, file1, file2, file3]); + const projectService = createProjectService(host); + const { configFileName, configFileErrors } = projectService.openClientFile(file1.path); + + assert(configFileName, "should find config file"); + assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`); + checkNumberOfInferredProjects(projectService, 0); + checkNumberOfConfiguredProjects(projectService, 1); + + const project = configuredProjectAt(projectService, 0); + checkProjectActualFiles(project, [file1.path, libFile.path, file2.path, configFile.path]); + checkProjectRootFiles(project, [file1.path, file2.path]); + // watching all files except one that was open + checkWatchedFiles(host, [configFile.path, file2.path, libFile.path]); + checkWatchedDirectories(host, [getDirectoryPath(configFile.path)], /*recursive*/ false); + }); + it("add and then remove a config file in a folder with loose files", () => { const configFile: FileOrFolder = { path: "/a/b/tsconfig.json", @@ -594,10 +654,13 @@ namespace ts.projectSystem { server.CommandNames.SemanticDiagnosticsSync, { file: file1.path } ); - let diags = session.executeCommand(getErrRequest).response; // Two errors: CommonFile2 not found and cannot find name y - assert.equal(diags.length, 2, diags.map(diag => flattenDiagnosticMessageText(diag.text, "\n")).join("\n")); + let diags: server.protocol.Diagnostic[] = session.executeCommand(getErrRequest).response; + verifyDiagnostics(diags, [ + { diagnosticMessage: Diagnostics.Cannot_find_name_0, errorTextArguments: ["y"] }, + { diagnosticMessage: Diagnostics.File_0_not_found, errorTextArguments: [commonFile2.path] } + ]); host.reloadFS([file1, commonFile2, libFile]); host.runQueuedTimeoutCallbacks(); @@ -605,8 +668,8 @@ namespace ts.projectSystem { assert.strictEqual(projectService.inferredProjects[0], project, "Inferred project should be same"); checkProjectRootFiles(project, [file1.path]); checkProjectActualFiles(project, [file1.path, libFile.path, commonFile2.path]); - diags = session.executeCommand(getErrRequest).response; - assert.equal(diags.length, 0); + diags = session.executeCommand(getErrRequest).response; + verifyNoDiagnostics(diags); }); it("should create new inferred projects for files excluded from a configured project", () => { @@ -1934,13 +1997,16 @@ namespace ts.projectSystem { filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath); let lastEvent: server.ProjectLanguageServiceStateEvent; - const session = createSession(host, /*typingsInstaller*/ undefined, e => { - if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ContextEvent || e.eventName === server.ProjectInfoTelemetryEvent) { - return; + const session = createSession(host, { + canUseEvents: true, + eventHandler: e => { + if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ContextEvent || e.eventName === server.ProjectInfoTelemetryEvent) { + return; + } + assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent); + assert.equal(e.data.project.getProjectName(), config.path, "project name"); + lastEvent = e; } - assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent); - assert.equal(e.data.project.getProjectName(), config.path, "project name"); - lastEvent = e; }); session.executeCommand({ seq: 0, @@ -1983,12 +2049,15 @@ namespace ts.projectSystem { host.getFileSize = (filePath: string) => filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath); let lastEvent: server.ProjectLanguageServiceStateEvent; - const session = createSession(host, /*typingsInstaller*/ undefined, e => { - if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectInfoTelemetryEvent) { - return; + const session = createSession(host, { + canUseEvents: true, + eventHandler: e => { + if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectInfoTelemetryEvent) { + return; + } + assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent); + lastEvent = e; } - assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent); - lastEvent = e; }); session.executeCommand({ seq: 0, @@ -2593,15 +2662,18 @@ namespace ts.projectSystem { server.CommandNames.SemanticDiagnosticsSync, { file: file1.path } ); - let diags = session.executeCommand(getErrRequest).response; - assert.equal(diags.length, 0); + let diags: server.protocol.Diagnostic[] = session.executeCommand(getErrRequest).response; + verifyNoDiagnostics(diags); const moduleFileOldPath = moduleFile.path; const moduleFileNewPath = "/a/b/moduleFile1.ts"; moduleFile.path = moduleFileNewPath; host.reloadFS([moduleFile, file1]); host.runQueuedTimeoutCallbacks(); - diags = session.executeCommand(getErrRequest).response; + diags = session.executeCommand(getErrRequest).response; + verifyDiagnostics(diags, [ + { diagnosticMessage: Diagnostics.Cannot_find_module_0, errorTextArguments: ["./moduleFile"] } + ]); assert.equal(diags.length, 1); moduleFile.path = moduleFileOldPath; @@ -2616,8 +2688,8 @@ namespace ts.projectSystem { session.executeCommand(changeRequest); host.runQueuedTimeoutCallbacks(); - diags = session.executeCommand(getErrRequest).response; - assert.equal(diags.length, 0); + diags = session.executeCommand(getErrRequest).response; + verifyNoDiagnostics(diags); }); it("should restore the states for configured projects", () => { @@ -2641,22 +2713,24 @@ namespace ts.projectSystem { server.CommandNames.SemanticDiagnosticsSync, { file: file1.path } ); - let diags = session.executeCommand(getErrRequest).response; - assert.equal(diags.length, 0); + let diags: server.protocol.Diagnostic[] = session.executeCommand(getErrRequest).response; + verifyNoDiagnostics(diags); const moduleFileOldPath = moduleFile.path; const moduleFileNewPath = "/a/b/moduleFile1.ts"; moduleFile.path = moduleFileNewPath; host.reloadFS([moduleFile, file1, configFile]); host.runQueuedTimeoutCallbacks(); - diags = session.executeCommand(getErrRequest).response; - assert.equal(diags.length, 1); + diags = session.executeCommand(getErrRequest).response; + verifyDiagnostics(diags, [ + { diagnosticMessage: Diagnostics.Cannot_find_module_0, errorTextArguments: ["./moduleFile"] } + ]); moduleFile.path = moduleFileOldPath; host.reloadFS([moduleFile, file1, configFile]); host.runQueuedTimeoutCallbacks(); - diags = session.executeCommand(getErrRequest).response; - assert.equal(diags.length, 0); + diags = session.executeCommand(getErrRequest).response; + verifyNoDiagnostics(diags); }); it("should property handle missing config files", () => { @@ -2722,8 +2796,10 @@ namespace ts.projectSystem { server.CommandNames.SemanticDiagnosticsSync, { file: file1.path } ); - let diags = session.executeCommand(getErrRequest).response; - assert.equal(diags.length, 1); + let diags: server.protocol.Diagnostic[] = session.executeCommand(getErrRequest).response; + verifyDiagnostics(diags, [ + { diagnosticMessage: Diagnostics.Cannot_find_module_0, errorTextArguments: ["./moduleFile"] } + ]); host.reloadFS([file1, moduleFile]); host.runQueuedTimeoutCallbacks(); @@ -2736,8 +2812,8 @@ namespace ts.projectSystem { session.executeCommand(changeRequest); // Recheck - diags = session.executeCommand(getErrRequest).response; - assert.equal(diags.length, 0); + diags = session.executeCommand(getErrRequest).response; + verifyNoDiagnostics(diags); }); }); @@ -2760,7 +2836,10 @@ namespace ts.projectSystem { }; const host = createServerHost([file, configFile]); - const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler); + const session = createSession(host, { + canUseEvents: true, + eventHandler: serverEventManager.handler + }); openFilesForSession([file], session); serverEventManager.checkEventCountOfType("configFileDiag", 1); @@ -2787,7 +2866,10 @@ namespace ts.projectSystem { }; const host = createServerHost([file, configFile]); - const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler); + const session = createSession(host, { + canUseEvents: true, + eventHandler: serverEventManager.handler + }); openFilesForSession([file], session); serverEventManager.checkEventCountOfType("configFileDiag", 1); }); @@ -2806,7 +2888,10 @@ namespace ts.projectSystem { }; const host = createServerHost([file, configFile]); - const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler); + const session = createSession(host, { + canUseEvents: true, + eventHandler: serverEventManager.handler + }); openFilesForSession([file], session); serverEventManager.checkEventCountOfType("configFileDiag", 1); @@ -3193,6 +3278,93 @@ namespace ts.projectSystem { checkNumberOfProjects(projectService, { inferredProjects: 1 }); checkProjectActualFiles(projectService.inferredProjects[0], [f.path]); }); + + it("inferred projects per project root", () => { + const file1 = { path: "/a/file1.ts", content: "let x = 1;", projectRootPath: "/a" }; + const file2 = { path: "/a/file2.ts", content: "let y = 2;", projectRootPath: "/a" }; + const file3 = { path: "/b/file2.ts", content: "let x = 3;", projectRootPath: "/b" }; + const file4 = { path: "/c/file3.ts", content: "let z = 4;" }; + const host = createServerHost([file1, file2, file3, file4]); + const session = createSession(host, { + useSingleInferredProject: true, + useInferredProjectPerProjectRoot: true + }); + session.executeCommand({ + seq: 1, + type: "request", + command: CommandNames.CompilerOptionsForInferredProjects, + arguments: { + options: { + allowJs: true, + target: ScriptTarget.ESNext + } + } + }); + session.executeCommand({ + seq: 2, + type: "request", + command: CommandNames.CompilerOptionsForInferredProjects, + arguments: { + options: { + allowJs: true, + target: ScriptTarget.ES2015 + }, + projectRootPath: "/b" + } + }); + session.executeCommand({ + seq: 3, + type: "request", + command: CommandNames.Open, + arguments: { + file: file1.path, + fileContent: file1.content, + scriptKindName: "JS", + projectRootPath: file1.projectRootPath + } + }); + session.executeCommand({ + seq: 4, + type: "request", + command: CommandNames.Open, + arguments: { + file: file2.path, + fileContent: file2.content, + scriptKindName: "JS", + projectRootPath: file2.projectRootPath + } + }); + session.executeCommand({ + seq: 5, + type: "request", + command: CommandNames.Open, + arguments: { + file: file3.path, + fileContent: file3.content, + scriptKindName: "JS", + projectRootPath: file3.projectRootPath + } + }); + session.executeCommand({ + seq: 6, + type: "request", + command: CommandNames.Open, + arguments: { + file: file4.path, + fileContent: file4.content, + scriptKindName: "JS" + } + }); + + const projectService = session.getProjectService(); + checkNumberOfProjects(projectService, { inferredProjects: 3 }); + checkProjectActualFiles(projectService.inferredProjects[0], [file4.path]); + checkProjectActualFiles(projectService.inferredProjects[1], [file1.path, file2.path]); + checkProjectActualFiles(projectService.inferredProjects[2], [file3.path]); + assert.equal(projectService.inferredProjects[0].getCompilerOptions().target, ScriptTarget.ESNext); + assert.equal(projectService.inferredProjects[1].getCompilerOptions().target, ScriptTarget.ESNext); + assert.equal(projectService.inferredProjects[2].getCompilerOptions().target, ScriptTarget.ES2015); + }); }); describe("No overwrite emit error", () => { @@ -3386,7 +3558,7 @@ namespace ts.projectSystem { resetRequest: noop }; - const session = createSession(host, /*typingsInstaller*/ undefined, /*projectServiceEventHandler*/ undefined, cancellationToken); + const session = createSession(host, { cancellationToken }); expectedRequestId = session.getNextSeq(); session.executeCommandSeq({ @@ -3426,7 +3598,11 @@ namespace ts.projectSystem { const cancellationToken = new TestServerCancellationToken(); const host = createServerHost([f1, config]); - const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken); + const session = createSession(host, { + canUseEvents: true, + eventHandler: () => { }, + cancellationToken + }); { session.executeCommandSeq({ command: "open", @@ -3559,7 +3735,12 @@ namespace ts.projectSystem { }; const cancellationToken = new TestServerCancellationToken(/*cancelAfterRequest*/ 3); const host = createServerHost([f1, config]); - const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken, /*throttleWaitMilliseconds*/ 0); + const session = createSession(host, { + canUseEvents: true, + eventHandler: () => { }, + cancellationToken, + throttleWaitMilliseconds: 0 + }); { session.executeCommandSeq({ command: "open", diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index 0654a357d70..b2d3903d13f 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -943,25 +943,26 @@ namespace ts.projectSystem { import * as cmd from "commander ` }; - session.executeCommand({ + const openRequest: server.protocol.OpenRequest = { seq: 1, type: "request", - command: "open", + command: server.protocol.CommandTypes.Open, arguments: { file: f.path, fileContent: f.content } - }); + }; + session.executeCommand(openRequest); const projectService = session.getProjectService(); checkNumberOfProjects(projectService, { inferredProjects: 1 }); const proj = projectService.inferredProjects[0]; const version1 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion(); // make a change that should not affect the structure of the program - session.executeCommand({ + const changeRequest: server.protocol.ChangeRequest = { seq: 2, type: "request", - command: "change", + command: server.protocol.CommandTypes.Change, arguments: { file: f.path, insertString: "\nlet x = 1;", @@ -970,7 +971,8 @@ namespace ts.projectSystem { endLine: 2, endOffset: 0 } - }); + }; + session.executeCommand(changeRequest); host.checkTimeoutQueueLengthAndRun(2); // This enqueues the updategraph and refresh inferred projects const version2 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion(); assert.equal(version1, version2, "set of unresolved imports should not change"); diff --git a/src/harness/unittests/versionCache.ts b/src/harness/unittests/versionCache.ts index 10790d41302..bbd23f25dac 100644 --- a/src/harness/unittests/versionCache.ts +++ b/src/harness/unittests/versionCache.ts @@ -49,6 +49,12 @@ var q:Point=p;`; validateEditAtLineCharIndex = undefined; }); + it("handles empty lines array", () => { + const lineIndex = new server.LineIndex(); + lineIndex.load([]); + assert.deepEqual(lineIndex.positionToLineOffset(0), { line: 1, offset: 1 }); + }); + it(`change 9 1 0 1 {"y"}`, () => { validateEditAtLineCharIndex(9, 1, 0, "y"); }); diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 95ef3266fb1..b77618df360 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -1,7 +1,7 @@ /// namespace ts.TestFSWithWatch { - export const { content: libFileContent } = Harness.getDefaultLibraryFile(Harness.IO); + const { content: libFileContent } = Harness.getDefaultLibraryFile(Harness.IO); export const libFile: FileOrFolder = { path: "/a/lib/lib.d.ts", content: libFileContent @@ -19,11 +19,11 @@ namespace ts.TestFSWithWatch { }) }; - export function getExecutingFilePathFromLibFile(): string { + function getExecutingFilePathFromLibFile(): string { return combinePaths(getDirectoryPath(libFile.path), "tsc.js"); } - export interface TestServerHostCreationParameters { + interface TestServerHostCreationParameters { useCaseSensitiveFileNames?: boolean; executingFilePath?: string; currentDirectory?: string; @@ -62,48 +62,40 @@ namespace ts.TestFSWithWatch { fileSize?: number; } - export interface FSEntry { + interface FSEntry { path: Path; fullPath: string; } - export interface File extends FSEntry { + interface File extends FSEntry { content: string; fileSize?: number; } - export interface Folder extends FSEntry { + interface Folder extends FSEntry { entries: FSEntry[]; } - export function isFolder(s: FSEntry): s is Folder { + function isFolder(s: FSEntry): s is Folder { return s && isArray((s).entries); } - export function isFile(s: FSEntry): s is File { + function isFile(s: FSEntry): s is File { return s && isString((s).content); } - function invokeDirectoryWatcher(callbacks: DirectoryWatcherCallback[], getRelativeFilePath: () => string) { + function invokeWatcherCallbacks(callbacks: T[], invokeCallback: (cb: T) => void): void { if (callbacks) { + // The array copy is made to ensure that even if one of the callback removes the callbacks, + // we dont miss any callbacks following it const cbs = callbacks.slice(); for (const cb of cbs) { - const fileName = getRelativeFilePath(); - cb(fileName); + invokeCallback(cb); } } } - function invokeFileWatcher(callbacks: FileWatcherCallback[], fileName: string, eventId: FileWatcherEventKind) { - if (callbacks) { - const cbs = callbacks.slice(); - for (const cb of cbs) { - cb(fileName, eventId); - } - } - } - - export function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { + function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map: Actual keys: ${arrayFrom(map.keys())} Expected: ${expectedKeys}`); for (const name of expectedKeys) { assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`); @@ -145,7 +137,7 @@ namespace ts.TestFSWithWatch { } } - export class Callbacks { + class Callbacks { private map: TimeOutCallback[] = []; private nextId = 1; @@ -181,7 +173,7 @@ namespace ts.TestFSWithWatch { } } - export type TimeOutCallback = () => any; + type TimeOutCallback = () => any; export class TestServerHost implements server.ServerHost { args: string[] = []; @@ -318,8 +310,12 @@ namespace ts.TestFSWithWatch { } else { Debug.assert(fileOrFolder.entries.length === 0); - invokeDirectoryWatcher(this.watchedDirectories.get(fileOrFolder.path), () => this.getRelativePathToDirectory(fileOrFolder.fullPath, fileOrFolder.fullPath)); - invokeDirectoryWatcher(this.watchedDirectoriesRecursive.get(fileOrFolder.path), () => this.getRelativePathToDirectory(fileOrFolder.fullPath, fileOrFolder.fullPath)); + const relativePath = this.getRelativePathToDirectory(fileOrFolder.fullPath, fileOrFolder.fullPath); + // Invoke directory and recursive directory watcher for the folder + // Here we arent invoking recursive directory watchers for the base folders + // since that is something we would want to do for both file as well as folder we are deleting + invokeWatcherCallbacks(this.watchedDirectories.get(fileOrFolder.path), cb => cb(relativePath)); + invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(fileOrFolder.path), cb => cb(relativePath)); } if (basePath !== fileOrFolder.path) { @@ -332,22 +328,31 @@ namespace ts.TestFSWithWatch { } } - private invokeFileWatcher(fileFullPath: string, eventId: FileWatcherEventKind) { + private invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind) { const callbacks = this.watchedFiles.get(this.toPath(fileFullPath)); - invokeFileWatcher(callbacks, getBaseFileName(fileFullPath), eventId); + const fileName = getBaseFileName(fileFullPath); + invokeWatcherCallbacks(callbacks, cb => cb(fileName, eventKind)); } private getRelativePathToDirectory(directoryFullPath: string, fileFullPath: string) { return getRelativePathToDirectoryOrUrl(directoryFullPath, fileFullPath, this.currentDirectory, this.getCanonicalFileName, /*isAbsolutePathAnUrl*/ false); } + /** + * This will call the directory watcher for the folderFullPath and recursive directory watchers for this and base folders + */ private invokeDirectoryWatcher(folderFullPath: string, fileName: string) { - invokeDirectoryWatcher(this.watchedDirectories.get(this.toPath(folderFullPath)), () => this.getRelativePathToDirectory(folderFullPath, fileName)); + const relativePath = this.getRelativePathToDirectory(folderFullPath, fileName); + invokeWatcherCallbacks(this.watchedDirectories.get(this.toPath(folderFullPath)), cb => cb(relativePath)); this.invokeRecursiveDirectoryWatcher(folderFullPath, fileName); } + /** + * This will call the recursive directory watcher for this directory as well as all the base directories + */ private invokeRecursiveDirectoryWatcher(fullPath: string, fileName: string) { - invokeDirectoryWatcher(this.watchedDirectoriesRecursive.get(this.toPath(fullPath)), () => this.getRelativePathToDirectory(fullPath, fileName)); + const relativePath = this.getRelativePathToDirectory(fullPath, fileName); + invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(this.toPath(fullPath)), cb => cb(relativePath)); const basePath = getDirectoryPath(fullPath); if (this.getCanonicalFileName(fullPath) !== this.getCanonicalFileName(basePath)) { this.invokeRecursiveDirectoryWatcher(basePath, fileName); @@ -501,7 +506,7 @@ namespace ts.TestFSWithWatch { const baseFolder = this.fs.get(base) as Folder; Debug.assert(isFolder(baseFolder)); - Debug.assert(!this.fs.get(folder.path), isFile(this.fs.get(folder.path)) ? `Found the file ${folder.path}` : `Found the folder ${folder.path}`); + Debug.assert(!this.fs.get(folder.path)); this.addFileOrFolderInFolder(baseFolder, folder); } diff --git a/src/lib/es2015.symbol.wellknown.d.ts b/src/lib/es2015.symbol.wellknown.d.ts index 578cf0acbc2..b7c2610e652 100644 --- a/src/lib/es2015.symbol.wellknown.d.ts +++ b/src/lib/es2015.symbol.wellknown.d.ts @@ -110,7 +110,7 @@ interface Map { readonly [Symbol.toStringTag]: "Map"; } -interface WeakMap{ +interface WeakMap { readonly [Symbol.toStringTag]: "WeakMap"; } diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 48a97fa6658..d42b9140a8f 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -216,7 +216,7 @@ interface ObjectConstructor { * Returns the names of the enumerable properties and methods of an object. * @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object. */ - keys(o: any): string[]; + keys(o: {}): string[]; } /** diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index bf255c7defa..8507b6f5cd2 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -22,7 +22,7 @@ namespace ts.server { export interface ConfigFileDiagEvent { eventName: typeof ConfigFileDiagEvent; - data: { triggerFile: string, configFileName: string, diagnostics: Diagnostic[] }; + data: { triggerFile: string, configFileName: string, diagnostics: ReadonlyArray }; } export interface ProjectLanguageServiceStateEvent { @@ -198,7 +198,7 @@ namespace ts.server { /** * This helper function processes a list of projects and return the concatenated, sortd and deduplicated output of processing each project. */ - export function combineProjectOutput(projects: Project[], action: (project: Project) => T[], comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) { + export function combineProjectOutput(projects: ReadonlyArray, action: (project: Project) => ReadonlyArray, comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) { const result = flatMap(projects, action).sort(comparer); return projects.length > 1 ? deduplicate(result, areEqual) : result; } @@ -211,7 +211,7 @@ namespace ts.server { export interface OpenConfiguredProjectResult { configFileName?: NormalizedPath; - configFileErrors?: Diagnostic[]; + configFileErrors?: ReadonlyArray; } interface FilePropertyReader { @@ -244,7 +244,7 @@ namespace ts.server { export const enum WatchType { ConfigFilePath = "Config file for the program", MissingFilePath = "Missing file from program", - WildCardDirectories = "Wild card directory", + WildcardDirectories = "Wild card directory", TypeRoot = "Type root of the project", ClosedScriptInfo = "Closed Script info", ConfigFileForInferredRoot = "Config file for the inferred project root", @@ -270,42 +270,51 @@ namespace ts.server { ReloadingFiles = "Reloading configured projects for files", ReloadingInferredRootFiles = "Reloading configured projects for only inferred root files", UpdatedCallback = "Updated the callback", - TrackingFileAdded = "Tracking file added", - TrackingFileRemoved = "Tracking file removed", - InferredRootAdded = "Inferred Root file added", - InferredRootRemoved = "Inferred Root file removed", + OpenFilesImpactedByConfigFileAdd = "File added to open files impacted by this config file", + OpenFilesImpactedByConfigFileRemove = "File removed from open files impacted by this config file", + RootOfInferredProjectTrue = "Open file was set as Inferred root", + RootOfInferredProjectFalse = "Open file was set as not inferred root", } /* @internal */ export type ServerDirectoryWatcherCallback = (path: NormalizedPath) => void; - type ConfigFileExistence = { + interface ConfigFileExistenceInfo { /** * Cached value of existence of config file + * It is true if there is configured project open for this file. + * It can be either true or false if this is the config file that is being watched by inferred project + * to decide when to update the structure so that it knows about updating the project for its files + * (config file may include the inferred project files after the change and hence may be wont need to be in inferred project) */ exists: boolean; /** - * The value in the open files map is true if the file is inferred project root - * Otherwise its false + * openFilesImpactedByConfigFiles is a map of open files that would be impacted by this config file + * because these are the paths being looked up for their default configured project location + * The value in the map is true if the open file is root of the inferred project + * It is false when the open file that would still be impacted by existance of + * this config file but it is not the root of inferred project */ - trackingOpenFilesMap: Map; + openFilesImpactedByConfigFile: Map; /** - * The file watcher corresponding to this config file for the inferred project root - * The watcher is present only when there is no open configured project for this config file + * The file watcher watching the config file because there is open script info that is root of + * inferred project and will be impacted by change in the status of the config file + * The watcher is present only when there is no open configured project for the config file */ - configFileWatcher?: FileWatcher; - }; + configFileWatcherForRootOfInferredProject?: FileWatcher; + } export interface ProjectServiceOptions { host: ServerHost; logger: Logger; cancellationToken: HostCancellationToken; useSingleInferredProject: boolean; + useInferredProjectPerProjectRoot: boolean; typingsInstaller: ITypingsInstaller; eventHandler?: ProjectServiceEventHandler; throttleWaitMilliseconds?: number; - globalPlugins?: string[]; - pluginProbeLocations?: string[]; + globalPlugins?: ReadonlyArray; + pluginProbeLocations?: ReadonlyArray; allowLocalPluginLoads?: boolean; } @@ -342,9 +351,19 @@ namespace ts.server { readonly openFiles: ScriptInfo[] = []; private compilerOptionsForInferredProjects: CompilerOptions; - private compileOnSaveForInferredProjects: boolean; + private compilerOptionsForInferredProjectsPerProjectRoot = createMap(); + /** + * Project size for configured or external projects + */ private readonly projectToSizeMap: Map = createMap(); - private readonly mapOfConfigFilePresence: Map; + /** + * This is a map of config file paths existance that doesnt need query to disk + * - The entry can be present because there is inferred project that needs to watch addition of config file to folder + * In this case the exists could be true/false based on config file is present or not + * - Or it is present if we have configured project open with config file at that location + * In this case the exists property is always true + */ + private readonly configFileExistenceInfoCache = createMap(); private readonly throttledOperations: ThrottledOperations; private readonly hostConfiguration: HostConfiguration; @@ -361,6 +380,7 @@ namespace ts.server { public readonly logger: Logger; public readonly cancellationToken: HostCancellationToken; public readonly useSingleInferredProject: boolean; + public readonly useInferredProjectPerProjectRoot: boolean; public readonly typingsInstaller: ITypingsInstaller; public readonly throttleWaitMilliseconds?: number; private readonly eventHandler?: ProjectServiceEventHandler; @@ -377,6 +397,7 @@ namespace ts.server { this.logger = opts.logger; this.cancellationToken = opts.cancellationToken; this.useSingleInferredProject = opts.useSingleInferredProject; + this.useInferredProjectPerProjectRoot = opts.useInferredProjectPerProjectRoot; this.typingsInstaller = opts.typingsInstaller || nullTypingsInstaller; this.throttleWaitMilliseconds = opts.throttleWaitMilliseconds; this.eventHandler = opts.eventHandler; @@ -388,7 +409,6 @@ namespace ts.server { this.currentDirectory = this.host.getCurrentDirectory(); this.toCanonicalFileName = createGetCanonicalFileName(this.host.useCaseSensitiveFileNames); - this.mapOfConfigFilePresence = createMap(); this.throttledOperations = new ThrottledOperations(this.host); this.typingsInstaller.attach(this); @@ -413,22 +433,26 @@ namespace ts.server { return this.changedFiles; } + /* @internal */ ensureInferredProjectsUpToDate_TestOnly() { - this.ensureInferredProjectsUpToDate(); + this.ensureProjectStructuresUptoDate(); } + /* @internal */ getCompilerOptionsForInferredProjects() { return this.compilerOptionsForInferredProjects; } + /* @internal */ onUpdateLanguageServiceStateForProject(project: Project, languageServiceEnabled: boolean) { if (!this.eventHandler) { return; } - this.eventHandler({ + const event: ProjectLanguageServiceStateEvent = { eventName: ProjectLanguageServiceStateEvent, data: { project, languageServiceEnabled } - }); + }; + this.eventHandler(event); } updateTypingsForProject(response: SetTypings | InvalidateCachedTypings): void { @@ -465,14 +489,13 @@ namespace ts.server { const projectName = project.getProjectName(); this.pendingProjectUpdates.set(projectName, project); this.throttledOperations.schedule(projectName, /*delay*/ 250, () => { - const project = this.pendingProjectUpdates.get(projectName); - if (project) { - this.pendingProjectUpdates.delete(projectName); + if (this.pendingProjectUpdates.delete(projectName)) { project.updateGraph(); } }); } + /* @internal */ delayUpdateProjectGraphAndInferredProjectsRefresh(project: Project) { this.delayUpdateProjectGraph(project); this.delayInferredProjectsRefresh(); @@ -485,26 +508,51 @@ namespace ts.server { this.delayInferredProjectsRefresh(); } - setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions): void { - this.compilerOptionsForInferredProjects = convertCompilerOptions(projectCompilerOptions); + setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions, projectRootPath?: string): void { + Debug.assert(projectRootPath === undefined || this.useInferredProjectPerProjectRoot, "Setting compiler options per project root path is only supported when useInferredProjectPerProjectRoot is enabled"); + + const compilerOptions = convertCompilerOptions(projectCompilerOptions); + // always set 'allowNonTsExtensions' for inferred projects since user cannot configure it from the outside // previously we did not expose a way for user to change these settings and this option was enabled by default - this.compilerOptionsForInferredProjects.allowNonTsExtensions = true; - this.compileOnSaveForInferredProjects = projectCompilerOptions.compileOnSave; - for (const proj of this.inferredProjects) { - proj.setCompilerOptions(this.compilerOptionsForInferredProjects); - proj.compileOnSaveEnabled = projectCompilerOptions.compileOnSave; - proj.markAsDirty(); + compilerOptions.allowNonTsExtensions = true; + + if (projectRootPath) { + this.compilerOptionsForInferredProjectsPerProjectRoot.set(projectRootPath, compilerOptions); } - this.delayUpdateProjectGraphs(this.inferredProjects); + else { + this.compilerOptionsForInferredProjects = compilerOptions; + } + + const projectsToUpdate: Project[] = []; + for (const project of this.inferredProjects) { + // Only update compiler options in the following cases: + // - Inferred projects without a projectRootPath, if the new options do not apply to + // a workspace root + // - Inferred projects with a projectRootPath, if the new options do not apply to a + // workspace root and there is no more specific set of options for that project's + // root path + // - Inferred projects with a projectRootPath, if the new options apply to that + // project root path. + if (projectRootPath ? + project.projectRootPath === projectRootPath : + !project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) { + project.setCompilerOptions(compilerOptions); + project.compileOnSaveEnabled = compilerOptions.compileOnSave; + project.markAsDirty(); + projectsToUpdate.push(project); + } + } + + this.delayUpdateProjectGraphs(projectsToUpdate); } - findProject(projectName: string): Project { + findProject(projectName: string): Project | undefined { if (projectName === undefined) { return undefined; } if (isInferredProjectName(projectName)) { - this.ensureInferredProjectsUpToDate(); + this.ensureProjectStructuresUptoDate(); return findProjectByName(projectName, this.inferredProjects); } return this.findExternalProjectByProjectName(projectName) || this.findConfiguredProjectByProjectName(toNormalizedPath(projectName)); @@ -512,22 +560,29 @@ namespace ts.server { getDefaultProjectForFile(fileName: NormalizedPath, refreshInferredProjects: boolean) { if (refreshInferredProjects) { - this.ensureInferredProjectsUpToDate(); + this.ensureProjectStructuresUptoDate(); } const scriptInfo = this.getScriptInfoForNormalizedPath(fileName); return scriptInfo && scriptInfo.getDefaultProject(); } getScriptInfoEnsuringProjectsUptoDate(uncheckedFileName: string) { - this.ensureInferredProjectsUpToDate(); + this.ensureProjectStructuresUptoDate(); return this.getScriptInfo(uncheckedFileName); } /** * Ensures the project structures are upto date - * @param refreshInferredProjects when true updates the inferred projects even if there is no pending work + * This means, + * - if there are changedFiles (the files were updated but their containing project graph was not upto date), + * their project graph is updated + * - If there are pendingProjectUpdates (scheduled to be updated with delay so they can batch update the graph if there are several changes in short time span) + * their project graph is updated + * - If there were project graph updates and/or there was pending inferred project update and/or called forced the inferred project structure refresh + * Inferred projects are created/updated/deleted based on open files states + * @param forceInferredProjectsRefresh when true updates the inferred projects even if there is no pending work to update the files/project structures */ - private ensureInferredProjectsUpToDate(refreshInferredProjects?: boolean) { + private ensureProjectStructuresUptoDate(forceInferredProjectsRefresh?: boolean) { if (this.changedFiles) { let projectsToUpdate: Project[]; if (this.changedFiles.length === 1) { @@ -550,7 +605,7 @@ namespace ts.server { this.updateProjectGraphs(projectsToUpdate); } - if (this.pendingInferredProjectUpdate || refreshInferredProjects) { + if (this.pendingInferredProjectUpdate || forceInferredProjectsRefresh) { this.pendingInferredProjectUpdate = false; this.refreshInferredProjects(); } @@ -587,27 +642,23 @@ namespace ts.server { private onSourceFileChanged(fileName: NormalizedPath, eventKind: FileWatcherEventKind) { const info = this.getScriptInfoForNormalizedPath(fileName); if (!info) { - this.logger.info(`Error: got watch notification for unknown file: ${fileName}`); - return; + this.logger.err(`Error: got watch notification for unknown file: ${fileName}`); } - - if (eventKind === FileWatcherEventKind.Deleted) { + else if (eventKind === FileWatcherEventKind.Deleted) { // File was deleted this.handleDeletedFile(info); } - else { - if (!info.isScriptOpen()) { - if (info.containingProjects.length === 0) { - // Orphan script info, remove it as we can always reload it on next open file request - this.stopWatchingScriptInfo(info, WatcherCloseReason.OrphanScriptInfoWithChange); - this.filenameToScriptInfo.delete(info.path); - } - else { - // file has been changed which might affect the set of referenced files in projects that include - // this file and set of inferred projects - info.reloadFromFile(); - this.delayUpdateProjectGraphs(info.containingProjects); - } + else if (!info.isScriptOpen()) { + if (info.containingProjects.length === 0) { + // Orphan script info, remove it as we can always reload it on next open file request + this.stopWatchingScriptInfo(info, WatcherCloseReason.OrphanScriptInfoWithChange); + this.filenameToScriptInfo.delete(info.path); + } + else { + // file has been changed which might affect the set of referenced files in projects that include + // this file and set of inferred projects + info.reloadFromFile(); + this.delayUpdateProjectGraphs(info.containingProjects); } } } @@ -634,10 +685,11 @@ namespace ts.server { // } // for (const openFile of this.openFiles) { - // this.eventHandler({ + // const event: ContextEvent = { // eventName: ContextEvent, // data: { project: openFile.getDefaultProject(), fileName: openFile.fileName } - // }); + // }; + // this.eventHandler(event); // } } } @@ -669,52 +721,52 @@ namespace ts.server { const configFileSpecs = project.configFileSpecs; const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFilename), project.getCompilerOptions(), project.getCachedServerHost(), this.hostConfiguration.extraFileExtensions); - const errors = project.getAllProjectErrors(); - const isErrorNoInputFiles = (error: Diagnostic) => error.code === Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code; - if (result.fileNames.length !== 0) { - filterMutate(errors, error => !isErrorNoInputFiles(error)); - } - else if (!configFileSpecs.filesSpecs && !some(errors, isErrorNoInputFiles)) { - errors.push(getErrorForNoInputFiles(configFileSpecs, configFilename)); - } + project.updateErrorOnNoInputFiles(result.fileNames.length !== 0); this.updateNonInferredProjectFiles(project, result.fileNames, fileNamePropertyReader, /*clientFileName*/ undefined); this.delayUpdateProjectGraphAndInferredProjectsRefresh(project); } private onConfigChangedForConfiguredProject(project: ConfiguredProject, eventKind: FileWatcherEventKind) { - const configFilePresenceInfo = this.mapOfConfigFilePresence.get(project.canonicalConfigFilePath); + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(project.canonicalConfigFilePath); if (eventKind === FileWatcherEventKind.Deleted) { // Update the cached status - // No action needed on tracking open files since the existing config file anyways didnt affect the tracking file - configFilePresenceInfo.exists = false; + // We arent updating or removing the cached config file presence info as that will be taken care of by + // setConfigFilePresenceByClosedConfigFile when the project is closed (depending on tracking open files) + configFileExistenceInfo.exists = false; this.removeProject(project); // Reload the configured projects for the open files in the map as they are affectected by this config file - this.logConfigFileWatchUpdate(project.getConfigFilePath(), configFilePresenceInfo, ConfigFileWatcherStatus.ReloadingFiles); - // Since the configured project was deleted, we want to reload projects for all the open files - this.delayReloadConfiguredProjectForFiles(configFilePresenceInfo.trackingOpenFilesMap, /*ignoreIfNotInferredProjectRoot*/ false); + // Since the configured project was deleted, we want to reload projects for all the open files including files + // that are not root of the inferred project + this.logConfigFileWatchUpdate(project.getConfigFilePath(), project.canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.ReloadingFiles); + this.delayReloadConfiguredProjectForFiles(configFileExistenceInfo, /*ignoreIfNotInferredProjectRoot*/ false); } else { - this.logConfigFileWatchUpdate(project.getConfigFilePath(), configFilePresenceInfo, ConfigFileWatcherStatus.ReloadingInferredRootFiles); + this.logConfigFileWatchUpdate(project.getConfigFilePath(), project.canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.ReloadingInferredRootFiles); project.pendingReload = true; this.delayUpdateProjectGraph(project); - // As we scheduled the updated project graph, we would need to only schedule the project reload for the inferred project roots - this.delayReloadConfiguredProjectForFiles(configFilePresenceInfo.trackingOpenFilesMap, /*ignoreIfNotInferredProjectRoot*/ true); + // As we scheduled the update on configured project graph, + // we would need to schedule the project reload for only the root of inferred projects + this.delayReloadConfiguredProjectForFiles(configFileExistenceInfo, /*ignoreIfNotInferredProjectRoot*/ true); } } /** - * This is the callback function for the config file add/remove/change at any location that matters to open - * script info but doesnt have configured project open for the config file + * This is the callback function for the config file add/remove/change at any location + * that matters to open script info but doesnt have configured project open + * for the config file */ private onConfigFileChangeForOpenScriptInfo(configFileName: NormalizedPath, eventKind: FileWatcherEventKind) { // This callback is called only if we dont have config file project for this config file - const cononicalConfigPath = normalizedPathToPath(configFileName, this.currentDirectory, this.toCanonicalFileName); - const configFilePresenceInfo = this.mapOfConfigFilePresence.get(cononicalConfigPath); - configFilePresenceInfo.exists = (eventKind !== FileWatcherEventKind.Deleted); - this.logConfigFileWatchUpdate(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.ReloadingFiles); - // The tracking opens files would only contaion the inferred root so no need to check - this.delayReloadConfiguredProjectForFiles(configFilePresenceInfo.trackingOpenFilesMap, /*ignoreIfNotInferredProjectRoot*/ false); + const canonicalConfigPath = normalizedPathToPath(configFileName, this.currentDirectory, this.toCanonicalFileName); + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigPath); + configFileExistenceInfo.exists = (eventKind !== FileWatcherEventKind.Deleted); + this.logConfigFileWatchUpdate(configFileName, canonicalConfigPath, configFileExistenceInfo, ConfigFileWatcherStatus.ReloadingFiles); + + // Because there is no configured project open for the config file, the tracking open files map + // will only have open files that need the re-detection of the project and hence + // reload projects for all the tracking open files in the map + this.delayReloadConfiguredProjectForFiles(configFileExistenceInfo, /*ignoreIfNotInferredProjectRoot*/ false); } private removeProject(project: Project) { @@ -732,7 +784,7 @@ namespace ts.server { case ProjectKind.Configured: this.configuredProjects.delete((project).canonicalConfigFilePath); this.projectToSizeMap.delete((project as ConfiguredProject).canonicalConfigFilePath); - this.setConfigFilePresenceByClosedConfigFile(project); + this.setConfigFileExistenceInfoByClosedConfiguredProject(project); break; case ProjectKind.Inferred: unorderedRemoveItem(this.inferredProjects, project); @@ -740,47 +792,50 @@ namespace ts.server { } } - private assignScriptInfoToInferredProjectIfNecessary(info: ScriptInfo, addToListOfOpenFiles: boolean): void { - if (info.containingProjects.length === 0) { - // create new inferred project p with the newly opened file as root - // or add root to existing inferred project if 'useOneInferredProject' is true - this.createInferredProjectWithRootFileIfNecessary(info); + /*@internal*/ + assignScriptInfoToInferredProject(info: ScriptInfo, projectRootPath?: string) { + Debug.assert(info.containingProjects.length === 0); - // if useOneInferredProject is not set then try to fixup ownership of open files - // check 'defaultProject !== inferredProject' is necessary to handle cases - // when creation inferred project for some file has added other open files into this project - // (i.e.as referenced files) - // we definitely don't want to delete the project that was just created - // Also note that we need to create a copy of the array since the list of project will change + const project = this.getOrCreateInferredProjectForProjectRootPathIfEnabled(info, projectRootPath) || + this.getOrCreateSingleInferredProjectIfEnabled() || + this.createInferredProject(); + + project.addRoot(info); + project.updateGraph(); + + if (!this.useSingleInferredProject && !project.projectRootPath) { + // Note that we need to create a copy of the array since the list of project can change for (const inferredProject of this.inferredProjects.slice(0, this.inferredProjects.length - 1)) { - Debug.assert(!this.useSingleInferredProject); - // Remove this file from the root of inferred project if its part of more than 2 projects + Debug.assert(inferredProject !== project); + // Remove the inferred project if the root of it is now part of newly created inferred project + // e.g through references + // Which means if any root of inferred project is part of more than 1 project can be removed // This logic is same as iterating over all open files and calling - // this.removRootOfInferredProjectIfNowPartOfOtherProject(f); + // this.removeRootOfInferredProjectIfNowPartOfOtherProject(f); // Since this is also called from refreshInferredProject and closeOpen file // to update inferred projects of the open file, this iteration might be faster // instead of scanning all open files - const root = inferredProject.getRootScriptInfos(); - Debug.assert(root.length === 1); - if (root[0].containingProjects.length > 1) { + const roots = inferredProject.getRootScriptInfos(); + Debug.assert(roots.length === 1 || !!inferredProject.projectRootPath); + if (roots.length === 1 && roots[0].containingProjects.length > 1) { this.removeProject(inferredProject); } } } - else { - for (const p of info.containingProjects) { - // file is the part of configured project - if (p.projectKind === ProjectKind.Configured) { - if (addToListOfOpenFiles) { - ((p)).addOpenRef(); - } - } + + return project; + } + + private addToListOfOpenFiles(info: ScriptInfo) { + Debug.assert(info.containingProjects.length !== 0); + for (const p of info.containingProjects) { + // file is the part of configured project, addref the project + if (p.projectKind === ProjectKind.Configured) { + ((p)).addOpenRef(); } } - if (addToListOfOpenFiles) { - this.openFiles.push(info); - } + this.openFiles.push(info); } /** @@ -832,17 +887,16 @@ namespace ts.server { this.removeProject(project); } - // collect orphanted files and try to re-add them as newly opened - // treat orphaned files as newly opened - // for all open files + // collect orphaned files and assign them to inferred project just like we treat open of a file for (const f of this.openFiles) { if (f.containingProjects.length === 0) { - this.assignScriptInfoToInferredProjectIfNecessary(f, /*addToListOfOpenFiles*/ false); + this.assignScriptInfoToInferredProject(f); } } - // Cleanup script infos that arent part of any project is postponed to - // next file open so that if file from same project is opened we wont end up creating same script infos + // Cleanup script infos that arent part of any project (eg. those could be closed script infos not referenced by any project) + // is postponed to next file open so that if file from same project is opened, + // we wont end up creating same script infos } // If the current info is being just closed - add the watcher file to track changes @@ -866,177 +920,164 @@ namespace ts.server { } private configFileExists(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: ScriptInfo) { - let configFilePresenceInfo = this.mapOfConfigFilePresence.get(canonicalConfigFilePath); - if (configFilePresenceInfo) { - // By default the info is belong to the config file. - // Only adding the info as a root to inferred project will make it the root - if (!configFilePresenceInfo.trackingOpenFilesMap.has(info.path)) { - configFilePresenceInfo.trackingOpenFilesMap.set(info.path, false); - this.logConfigFileWatchUpdate(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.TrackingFileAdded); + let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); + if (configFileExistenceInfo) { + // By default the info would get impacted by presence of config file since its in the detection path + // Only adding the info as a root to inferred project will need the existence to be watched by file watcher + if (!configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) { + configFileExistenceInfo.openFilesImpactedByConfigFile.set(info.path, false); + this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.OpenFilesImpactedByConfigFileAdd); } - return configFilePresenceInfo.exists; + return configFileExistenceInfo.exists; } - // Theorotically we should be adding watch for the directory here itself. + // Theoretically we should be adding watch for the directory here itself. // In practice there will be very few scenarios where the config file gets added // somewhere inside the another config file directory. // And technically we could handle that case in configFile's directory watcher in some cases // But given that its a rare scenario it seems like too much overhead. (we werent watching those directories earlier either) - // So what we are now watching is: configFile if the project is open - // And the whole chain of config files only for the inferred project roots - // Cache the host value of file exists and add the info tio to the tracked root - const trackingOpenFilesMap = createMap(); - trackingOpenFilesMap.set(info.path, false); + // So what we are now watching is: configFile if the configured project corresponding to it is open + // Or the whole chain of config files for the roots of the inferred projects + + // Cache the host value of file exists and add the info to map of open files impacted by this config file + const openFilesImpactedByConfigFile = createMap(); + openFilesImpactedByConfigFile.set(info.path, false); const exists = this.host.fileExists(configFileName); - configFilePresenceInfo = { exists, trackingOpenFilesMap }; - this.mapOfConfigFilePresence.set(canonicalConfigFilePath, configFilePresenceInfo); - this.logConfigFileWatchUpdate(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.TrackingFileAdded); + configFileExistenceInfo = { exists, openFilesImpactedByConfigFile }; + this.configFileExistenceInfoCache.set(canonicalConfigFilePath, configFileExistenceInfo); + this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.OpenFilesImpactedByConfigFileAdd); return exists; } - private setConfigFilePresenceByNewConfiguredProject(project: ConfiguredProject) { - const configFilePresenceInfo = this.mapOfConfigFilePresence.get(project.canonicalConfigFilePath); - if (configFilePresenceInfo) { - Debug.assert(configFilePresenceInfo.exists); + private setConfigFileExistenceByNewConfiguredProject(project: ConfiguredProject) { + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(project.canonicalConfigFilePath); + if (configFileExistenceInfo) { + Debug.assert(configFileExistenceInfo.exists); // close existing watcher - if (configFilePresenceInfo.configFileWatcher) { + if (configFileExistenceInfo.configFileWatcherForRootOfInferredProject) { const configFileName = project.getConfigFilePath(); this.closeFileWatcher( WatchType.ConfigFileForInferredRoot, /*project*/ undefined, configFileName, - configFilePresenceInfo.configFileWatcher, WatcherCloseReason.ConfigProjectCreated + configFileExistenceInfo.configFileWatcherForRootOfInferredProject, WatcherCloseReason.ConfigProjectCreated ); - configFilePresenceInfo.configFileWatcher = undefined; - this.logConfigFileWatchUpdate(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.UpdatedCallback); + configFileExistenceInfo.configFileWatcherForRootOfInferredProject = undefined; + this.logConfigFileWatchUpdate(configFileName, project.canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.UpdatedCallback); } } else { - // We could be in this scenario if it is the external project tracked configured file + // We could be in this scenario if project is the configured project tracked by external project // Since that route doesnt check if the config file is present or not - this.mapOfConfigFilePresence.set(project.canonicalConfigFilePath, { + this.configFileExistenceInfoCache.set(project.canonicalConfigFilePath, { exists: true, - trackingOpenFilesMap: createMap() + openFilesImpactedByConfigFile: createMap() }); } } - private configFileExistenceTracksInferredRoot(configFilePresenceInfo: ConfigFileExistence) { - return forEachEntry(configFilePresenceInfo.trackingOpenFilesMap, (value, __key) => value); + /** + * Returns true if the configFileExistenceInfo is needed/impacted by open files that are root of inferred project + */ + private configFileExistenceImpactsRootOfInferredProject(configFileExistenceInfo: ConfigFileExistenceInfo) { + return forEachEntry(configFileExistenceInfo.openFilesImpactedByConfigFile, (isRootOfInferredProject, __key) => isRootOfInferredProject); } - private setConfigFilePresenceByClosedConfigFile(closedProject: ConfiguredProject) { - const configFilePresenceInfo = this.mapOfConfigFilePresence.get(closedProject.canonicalConfigFilePath); - Debug.assert(!!configFilePresenceInfo); - const trackingOpenFilesMap = configFilePresenceInfo.trackingOpenFilesMap; - if (trackingOpenFilesMap.size) { + private setConfigFileExistenceInfoByClosedConfiguredProject(closedProject: ConfiguredProject) { + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(closedProject.canonicalConfigFilePath); + Debug.assert(!!configFileExistenceInfo); + if (configFileExistenceInfo.openFilesImpactedByConfigFile.size) { const configFileName = closedProject.getConfigFilePath(); - if (this.configFileExistenceTracksInferredRoot(configFilePresenceInfo)) { - Debug.assert(!configFilePresenceInfo.configFileWatcher); - configFilePresenceInfo.configFileWatcher = this.addFileWatcher( + // If there are open files that are impacted by this config file existence + // but none of them are root of inferred project, the config file watcher will be + // created when any of the script infos are added as root of inferred project + if (this.configFileExistenceImpactsRootOfInferredProject(configFileExistenceInfo)) { + Debug.assert(!configFileExistenceInfo.configFileWatcherForRootOfInferredProject); + configFileExistenceInfo.configFileWatcherForRootOfInferredProject = this.addFileWatcher( WatchType.ConfigFileForInferredRoot, /*project*/ undefined, configFileName, (_filename, eventKind) => this.onConfigFileChangeForOpenScriptInfo(configFileName, eventKind) ); - this.logConfigFileWatchUpdate(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.UpdatedCallback); + this.logConfigFileWatchUpdate(configFileName, closedProject.canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.UpdatedCallback); } } else { - // There is no one tracking anymore. Remove the status - this.mapOfConfigFilePresence.delete(closedProject.canonicalConfigFilePath); + // There is not a single file open thats tracking the status of this config file. Remove from cache + this.configFileExistenceInfoCache.delete(closedProject.canonicalConfigFilePath); } } - private logConfigFileWatchUpdate(configFileName: NormalizedPath, configFilePresenceInfo: ConfigFileExistence, status: ConfigFileWatcherStatus) { - if (this.logger.loggingEnabled()) { - const inferredRoots: string[] = []; - const otherFiles: string[] = []; - configFilePresenceInfo.trackingOpenFilesMap.forEach((value, key: Path) => { - const info = this.getScriptInfoForPath(key); - if (value) { - inferredRoots.push(info.fileName); - } - else { - otherFiles.push(info.fileName); - } - }); - const watchType = status === ConfigFileWatcherStatus.UpdatedCallback || - status === ConfigFileWatcherStatus.ReloadingFiles || - status === ConfigFileWatcherStatus.ReloadingInferredRootFiles ? - (configFilePresenceInfo.configFileWatcher ? WatchType.ConfigFileForInferredRoot : WatchType.ConfigFilePath) : - ""; - this.logger.info(`ConfigFilePresence ${watchType}:: File: ${configFileName} Currently Tracking: InferredRootFiles: ${inferredRoots} OtherFiles: ${otherFiles} Status: ${status}`); + private logConfigFileWatchUpdate(configFileName: NormalizedPath, canonicalConfigFilePath: string, configFileExistenceInfo: ConfigFileExistenceInfo, status: ConfigFileWatcherStatus) { + if (!this.logger.loggingEnabled()) { + return; } + const inferredRoots: string[] = []; + const otherFiles: string[] = []; + configFileExistenceInfo.openFilesImpactedByConfigFile.forEach((isRootOfInferredProject, key) => { + const info = this.getScriptInfoForPath(key as Path); + (isRootOfInferredProject ? inferredRoots : otherFiles).push(info.fileName); + }); + + const watches: WatchType[] = []; + if (configFileExistenceInfo.configFileWatcherForRootOfInferredProject) { + watches.push(WatchType.ConfigFileForInferredRoot); + } + if (this.configuredProjects.has(canonicalConfigFilePath)) { + watches.push(WatchType.ConfigFilePath); + } + this.logger.info(`ConfigFilePresence:: Current Watches: ['${watches.join("','")}']:: File: ${configFileName} Currently impacted open files: RootsOfInferredProjects: ${inferredRoots} OtherOpenFiles: ${otherFiles} Status: ${status}`); } - private closeConfigFileWatcherIfInferredRoot(configFileName: NormalizedPath, canonicalConfigFilePath: string, - configFilePresenceInfo: ConfigFileExistence, infoIsInferredRoot: boolean, reason: WatcherCloseReason) { - // Close the config file watcher if it was the last inferred root - if (infoIsInferredRoot && - configFilePresenceInfo.configFileWatcher && - !this.configFileExistenceTracksInferredRoot(configFilePresenceInfo)) { + /** + * Close the config file watcher in the cached ConfigFileExistenceInfo + * if there arent any open files that are root of inferred project + */ + private closeConfigFileWatcherOfConfigFileExistenceInfo( + configFileName: NormalizedPath, configFileExistenceInfo: ConfigFileExistenceInfo, + reason: WatcherCloseReason + ) { + // Close the config file watcher if there are no more open files that are root of inferred project + if (configFileExistenceInfo.configFileWatcherForRootOfInferredProject && + !this.configFileExistenceImpactsRootOfInferredProject(configFileExistenceInfo)) { this.closeFileWatcher( WatchType.ConfigFileForInferredRoot, /*project*/ undefined, configFileName, - configFilePresenceInfo.configFileWatcher, reason - ); - configFilePresenceInfo.configFileWatcher = undefined; - } - - // If this was the last tracking file open for this config file, remove the cached value - if (!configFilePresenceInfo.trackingOpenFilesMap.size && - !this.getConfiguredProjectByCanonicalConfigFilePath(canonicalConfigFilePath)) { - this.mapOfConfigFilePresence.delete(canonicalConfigFilePath); - } - } - - private closeConfigFileWatchForClosedScriptInfo(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: ScriptInfo) { - const configFilePresenceInfo = this.mapOfConfigFilePresence.get(canonicalConfigFilePath); - if (configFilePresenceInfo) { - const isInferredRoot = configFilePresenceInfo.trackingOpenFilesMap.get(info.path); - - // Delete the info from tracking - configFilePresenceInfo.trackingOpenFilesMap.delete(info.path); - this.logConfigFileWatchUpdate(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.TrackingFileRemoved); - - // Close the config file watcher if it was the last inferred root - this.closeConfigFileWatcherIfInferredRoot(configFileName, canonicalConfigFilePath, - configFilePresenceInfo, isInferredRoot, WatcherCloseReason.FileClosed + configFileExistenceInfo.configFileWatcherForRootOfInferredProject, reason ); + configFileExistenceInfo.configFileWatcherForRootOfInferredProject = undefined; } } /** * This is called on file close, so that we stop watching the config file for this script info - * @param info */ private stopWatchingConfigFilesForClosedScriptInfo(info: ScriptInfo) { Debug.assert(!info.isScriptOpen()); - this.enumerateConfigFileLocations(info, (configFileName, canonicalConfigFilePath) => - this.closeConfigFileWatchForClosedScriptInfo(configFileName, canonicalConfigFilePath, info) - ); - } + this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => { + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); + if (configFileExistenceInfo) { + const infoIsRootOfInferredProject = configFileExistenceInfo.openFilesImpactedByConfigFile.get(info.path); - private watchConfigFileForInferredProjectRoot(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: ScriptInfo) { - let configFilePresenceInfo = this.mapOfConfigFilePresence.get(canonicalConfigFilePath); - if (!configFilePresenceInfo) { - // Create the cache - configFilePresenceInfo = { - exists: this.host.fileExists(configFileName), - trackingOpenFilesMap: createMap() - }; - this.mapOfConfigFilePresence.set(canonicalConfigFilePath, configFilePresenceInfo); - } + // Delete the info from map, since this file is no more open + configFileExistenceInfo.openFilesImpactedByConfigFile.delete(info.path); + this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.OpenFilesImpactedByConfigFileRemove); - // Set this file as inferred root - configFilePresenceInfo.trackingOpenFilesMap.set(info.path, true); - this.logConfigFileWatchUpdate(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.InferredRootAdded); + // If the script info was not root of inferred project, + // there wont be config file watch open because of this script info + if (infoIsRootOfInferredProject) { + // But if it is a root, it could be the last script info that is root of inferred project + // and hence we would need to close the config file watcher + this.closeConfigFileWatcherOfConfigFileExistenceInfo( + configFileName, configFileExistenceInfo, WatcherCloseReason.FileClosed + ); + } - // If there is no configured project for this config file, create the watcher - if (!configFilePresenceInfo.configFileWatcher && - !this.getConfiguredProjectByCanonicalConfigFilePath(canonicalConfigFilePath)) { - configFilePresenceInfo.configFileWatcher = this.addFileWatcher(WatchType.ConfigFileForInferredRoot, /*project*/ undefined, configFileName, - (_fileName, eventKind) => this.onConfigFileChangeForOpenScriptInfo(configFileName, eventKind) - ); - this.logConfigFileWatchUpdate(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.UpdatedCallback); - } + // If there are no open files that are impacted by configFileExistenceInfo after closing this script info + // there is no configured project present, remove the cached existence info + if (!configFileExistenceInfo.openFilesImpactedByConfigFile.size && + !this.getConfiguredProjectByCanonicalConfigFilePath(canonicalConfigFilePath)) { + Debug.assert(!configFileExistenceInfo.configFileWatcherForRootOfInferredProject); + this.configFileExistenceInfoCache.delete(canonicalConfigFilePath); + } + } + }); } /** @@ -1045,25 +1086,30 @@ namespace ts.server { /* @internal */ startWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo) { Debug.assert(info.isScriptOpen()); - this.enumerateConfigFileLocations(info, (configFileName, canonicalConfigFilePath) => - this.watchConfigFileForInferredProjectRoot(configFileName, canonicalConfigFilePath, info) - ); - } - - private closeWatchConfigFileForInferredProjectRoot(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: ScriptInfo, reason: WatcherCloseReason) { - const configFilePresenceInfo = this.mapOfConfigFilePresence.get(canonicalConfigFilePath); - if (configFilePresenceInfo) { - // Set this as not inferred root - if (configFilePresenceInfo.trackingOpenFilesMap.has(info.path)) { - configFilePresenceInfo.trackingOpenFilesMap.set(info.path, false); - this.logConfigFileWatchUpdate(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.InferredRootRemoved); + this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => { + let configFilePresenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); + if (!configFilePresenceInfo) { + // Create the cache + configFilePresenceInfo = { + exists: this.host.fileExists(configFileName), + openFilesImpactedByConfigFile: createMap() + }; + this.configFileExistenceInfoCache.set(canonicalConfigFilePath, configFilePresenceInfo); } - // Close the watcher if present - this.closeConfigFileWatcherIfInferredRoot(configFileName, canonicalConfigFilePath, - configFilePresenceInfo, /*infoIsInferredRoot*/ true, reason - ); - } + // Set this file as the root of inferred project + configFilePresenceInfo.openFilesImpactedByConfigFile.set(info.path, true); + this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFilePresenceInfo, ConfigFileWatcherStatus.RootOfInferredProjectTrue); + + // If there is no configured project for this config file, add the file watcher + if (!configFilePresenceInfo.configFileWatcherForRootOfInferredProject && + !this.getConfiguredProjectByCanonicalConfigFilePath(canonicalConfigFilePath)) { + configFilePresenceInfo.configFileWatcherForRootOfInferredProject = this.addFileWatcher(WatchType.ConfigFileForInferredRoot, /*project*/ undefined, configFileName, + (_fileName, eventKind) => this.onConfigFileChangeForOpenScriptInfo(configFileName, eventKind) + ); + this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFilePresenceInfo, ConfigFileWatcherStatus.UpdatedCallback); + } + }); } /** @@ -1071,9 +1117,21 @@ namespace ts.server { */ /* @internal */ stopWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo, reason: WatcherCloseReason) { - this.enumerateConfigFileLocations(info, (configFileName, canonicalConfigFilePath) => - this.closeWatchConfigFileForInferredProjectRoot(configFileName, canonicalConfigFilePath, info, reason) - ); + this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => { + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); + if (configFileExistenceInfo && configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) { + Debug.assert(info.isScriptOpen()); + + // Info is not root of inferred project any more + configFileExistenceInfo.openFilesImpactedByConfigFile.set(info.path, false); + this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.RootOfInferredProjectFalse); + + // Close the config file watcher + this.closeConfigFileWatcherOfConfigFileExistenceInfo( + configFileName, configFileExistenceInfo, reason + ); + } + }); } /** @@ -1084,7 +1142,7 @@ namespace ts.server { * The server must start searching from the directory containing * the newly opened file. */ - private enumerateConfigFileLocations(info: ScriptInfo, + private forEachConfigFileLocation(info: ScriptInfo, action: (configFileName: NormalizedPath, canonicalConfigFilePath: string) => boolean | void, projectRootPath?: NormalizedPath) { let searchPath = asNormalizedPath(getDirectoryPath(info.fileName)); @@ -1124,8 +1182,8 @@ namespace ts.server { private getConfigFileNameForFile(info: ScriptInfo, projectRootPath?: NormalizedPath) { Debug.assert(info.isScriptOpen()); this.logger.info(`Search path: ${getDirectoryPath(info.fileName)}`); - const configFileName = this.enumerateConfigFileLocations(info, - (configFileName: NormalizedPath, canonicalConfigFilePath: string) => + const configFileName = this.forEachConfigFileLocation(info, + (configFileName, canonicalConfigFilePath) => this.configFileExists(configFileName, canonicalConfigFilePath, info), projectRootPath ); @@ -1143,39 +1201,36 @@ namespace ts.server { return; } - this.logger.startGroup(); + this.logger.group(info => { + let counter = 0; + counter = printProjects(this.externalProjects, info, counter); + counter = printProjects(arrayFrom(this.configuredProjects.values()), info, counter); + printProjects(this.inferredProjects, info, counter); - let counter = 0; - counter = printProjects(this.logger, this.externalProjects, counter); - counter = printProjects(this.logger, arrayFrom(this.configuredProjects.values()), counter); - counter = printProjects(this.logger, this.inferredProjects, counter); + info("Open files: "); + for (const rootFile of this.openFiles) { + info(`\t${rootFile.fileName}`); + } + }); - this.logger.info("Open files: "); - for (const rootFile of this.openFiles) { - this.logger.info(`\t${rootFile.fileName}`); - } - - this.logger.endGroup(); - - function printProjects(logger: Logger, projects: Project[], counter: number) { + function printProjects(projects: Project[], info: (msg: string) => void, counter: number): number { for (const project of projects) { - // Print shouldnt update the graph. It should emit whatever state the project is currently in - logger.info(`Project '${project.getProjectName()}' (${ProjectKind[project.projectKind]}) ${counter}`); - logger.info(project.filesToString()); - logger.info("-----------------------------------------------"); + info(`Project '${project.getProjectName()}' (${ProjectKind[project.projectKind]}) ${counter}`); + info(project.filesToString()); + info("-----------------------------------------------"); counter++; } return counter; } } - private findConfiguredProjectByProjectName(configFileName: NormalizedPath) { + private findConfiguredProjectByProjectName(configFileName: NormalizedPath): ConfiguredProject | undefined { // make sure that casing of config file name is consistent const canonicalConfigFilePath = asNormalizedPath(this.toCanonicalFileName(configFileName)); return this.getConfiguredProjectByCanonicalConfigFilePath(canonicalConfigFilePath); } - private getConfiguredProjectByCanonicalConfigFilePath(canonicalConfigFilePath: string) { + private getConfiguredProjectByCanonicalConfigFilePath(canonicalConfigFilePath: string): ConfiguredProject | undefined { return this.configuredProjects.get(canonicalConfigFilePath); } @@ -1253,7 +1308,7 @@ namespace ts.server { return false; } - private createAndAddExternalProject(projectFileName: string, files: protocol.ExternalFile[], options: protocol.ExternalProjectCompilerOptions, typeAcquisition: TypeAcquisition) { + private createExternalProject(projectFileName: string, files: protocol.ExternalFile[], options: protocol.ExternalProjectCompilerOptions, typeAcquisition: TypeAcquisition) { const compilerOptions = convertCompilerOptions(options); const project = new ExternalProject( projectFileName, @@ -1263,7 +1318,7 @@ namespace ts.server { /*languageServiceEnabled*/ !this.exceededTotalSizeLimitForNonTsFiles(projectFileName, compilerOptions, files, externalFilePropertyReader), options.compileOnSave === undefined ? true : options.compileOnSave); - this.addFilesToNonInferredProjectAndUpdateGraph(project, files, externalFilePropertyReader, /*clientFileName*/ undefined, typeAcquisition, /*configFileErrors*/ undefined); + this.addFilesToNonInferredProjectAndUpdateGraph(project, files, externalFilePropertyReader, /*clientFileName*/ undefined, typeAcquisition); this.externalProjects.push(project); this.sendProjectTelemetry(project.externalProjectName, project); return project; @@ -1300,8 +1355,7 @@ namespace ts.server { } const configFilePath = project instanceof server.ConfiguredProject && project.getConfigFilePath(); - const base = getBaseFileName(configFilePath); - return base === "tsconfig.json" || base === "jsconfig.json" ? base : "other"; + return getBaseConfigFileName(configFilePath) || "other"; } function convertTypeAcquisition({ enable, include, exclude }: TypeAcquisition): ProjectInfoTypeAcquisitionData { @@ -1313,7 +1367,17 @@ namespace ts.server { } } - private createAndAddConfiguredProject(configFileName: NormalizedPath, projectOptions: ProjectOptions, configFileErrors: Diagnostic[], configFileSpecs: ConfigFileSpecs, cachedServerHost: CachedServerHost, clientFileName?: string) { + private addFilesToNonInferredProjectAndUpdateGraph(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader, clientFileName: string, typeAcquisition: TypeAcquisition): void { + this.updateNonInferredProjectFiles(project, files, propertyReader, clientFileName); + project.setTypeAcquisition(typeAcquisition); + // This doesnt need scheduling since its either creation or reload of the project + project.updateGraph(); + } + + private createConfiguredProject(configFileName: NormalizedPath, clientFileName?: string) { + const cachedServerHost = new CachedServerHost(this.host); + const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, cachedServerHost); + this.logger.info(`Opened configuration file ${configFileName}`); const languageServiceEnabled = !this.exceededTotalSizeLimitForNonTsFiles(configFileName, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader); const project = new ConfiguredProject( configFileName, @@ -1326,7 +1390,7 @@ namespace ts.server { cachedServerHost); project.configFileSpecs = configFileSpecs; - // TODO: (sheetalkamat) We should also watch the configFiles that are extended + // TODO: We probably should also watch the configFiles that are extended project.configFileWatcher = this.addFileWatcher(WatchType.ConfigFilePath, project, configFileName, (_fileName, eventKind) => this.onConfigChangedForConfiguredProject(project, eventKind) ); @@ -1335,37 +1399,24 @@ namespace ts.server { project.watchTypeRoots(); } - this.addFilesToNonInferredProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typeAcquisition, configFileErrors); + project.setProjectErrors(configFileErrors); + this.addFilesToNonInferredProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typeAcquisition); this.configuredProjects.set(project.canonicalConfigFilePath, project); - this.setConfigFilePresenceByNewConfiguredProject(project); + this.setConfigFileExistenceByNewConfiguredProject(project); this.sendProjectTelemetry(project.getConfigFilePath(), project, projectOptions); return project; } - private addFilesToNonInferredProjectAndUpdateGraph(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: Diagnostic[]): void { - project.setProjectErrors(configFileErrors); - this.updateNonInferredProjectFiles(project, files, propertyReader, clientFileName); - project.setTypeAcquisition(typeAcquisition); - // This doesnt need scheduling since its either creation or reload of the project - project.updateGraph(); - } - - private openConfigFile(configFileName: NormalizedPath, clientFileName?: string) { - const cachedServerHost = new CachedServerHost(this.host); - const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, cachedServerHost); - this.logger.info(`Opened configuration file ${configFileName}`); - return this.createAndAddConfiguredProject(configFileName, projectOptions, configFileErrors, configFileSpecs, cachedServerHost, clientFileName); - } - - private updateNonInferredProjectFiles(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader, clientFileName?: string) { + private updateNonInferredProjectFiles(project: ExternalProject | ConfiguredProject, files: T[], propertyReader: FilePropertyReader, clientFileName?: string) { const projectRootFilesMap = project.getRootFilesMap(); - const newRootScriptInfoMap: Map = createMap(); + const newRootScriptInfoMap = createMap(); - for (const f of newUncheckedFiles) { + for (const f of files) { const newRootFile = propertyReader.getFileName(f); const normalizedPath = toNormalizedPath(newRootFile); let scriptInfo: ScriptInfo | NormalizedPath; let path: Path; + // Use the project's lsHost so that it can use caching instead of reaching to disk for the query if (!project.lsHost.fileExists(newRootFile)) { path = normalizedPathToPath(normalizedPath, this.currentDirectory, this.toCanonicalFileName); const existingValue = projectRootFilesMap.get(path); @@ -1393,7 +1444,7 @@ namespace ts.server { newRootScriptInfoMap.set(path, scriptInfo); } - // project's root file map size is always going to be larger than new roots map + // project's root file map size is always going to be same or larger than new roots map // as we have already all the new files to the project if (projectRootFilesMap.size > newRootScriptInfoMap.size) { projectRootFilesMap.forEach((value, path) => { @@ -1403,42 +1454,56 @@ namespace ts.server { } else { projectRootFilesMap.delete(path); - project.markAsDirty(); } } }); } - project.markAsDirty(); // Just to ensure that even if root files dont change, the changes to the non root file are picked up + + // Just to ensure that even if root files dont change, the changes to the non root file are picked up, + // mark the project as dirty unconditionally + project.markAsDirty(); } - private updateNonInferredProject(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader, newOptions: CompilerOptions, newTypeAcquisition: TypeAcquisition, compileOnSave: boolean, configFileErrors: Diagnostic[]) { + private updateNonInferredProject(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader, newOptions: CompilerOptions, newTypeAcquisition: TypeAcquisition, compileOnSave: boolean) { project.setCompilerOptions(newOptions); // VS only set the CompileOnSaveEnabled option in the request if the option was changed recently // therefore if it is undefined, it should not be updated. if (compileOnSave !== undefined) { project.compileOnSaveEnabled = compileOnSave; } - this.addFilesToNonInferredProjectAndUpdateGraph(project, newUncheckedFiles, propertyReader, /*clientFileName*/ undefined, newTypeAcquisition, configFileErrors); + this.addFilesToNonInferredProjectAndUpdateGraph(project, newUncheckedFiles, propertyReader, /*clientFileName*/ undefined, newTypeAcquisition); } /** * Read the config file of the project again and update the project - * @param project */ /* @internal */ reloadConfiguredProject(project: ConfiguredProject) { // At this point, there is no reason to not have configFile in the host - - // note: the returned "success" is true does not mean the "configFileErrors" is empty. - // because we might have tolerated the errors and kept going. So always return the configFileErrors - // regardless the "success" here is true or not. const host = project.getCachedServerHost(); + + // Clear the cache since we are reloading the project from disk host.clearCache(); const configFileName = project.getConfigFilePath(); this.logger.info(`Reloading configured project ${configFileName}`); + + // Read updated contents from disk const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, host); + + // Update the project project.configFileSpecs = configFileSpecs; - this.updateConfiguredProject(project, projectOptions, configFileErrors); + project.setProjectErrors(configFileErrors); + if (this.exceededTotalSizeLimitForNonTsFiles(project.canonicalConfigFilePath, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader)) { + project.disableLanguageService(); + project.stopWatchingWildCards(WatcherCloseReason.ProjectReloadHitMaxSize); + project.stopWatchingTypeRoots(WatcherCloseReason.ProjectReloadHitMaxSize); + } + else { + project.enableLanguageService(); + project.watchWildcards(projectOptions.wildcardDirectories); + project.watchTypeRoots(); + } + this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typeAcquisition, projectOptions.compileOnSave); if (!this.eventHandler) { return; @@ -1450,34 +1515,65 @@ namespace ts.server { }); } - /** - * Updates the configured project with updated config file contents - * @param project - */ - private updateConfiguredProject(project: ConfiguredProject, projectOptions: ProjectOptions, configFileErrors: Diagnostic[]) { - if (this.exceededTotalSizeLimitForNonTsFiles(project.canonicalConfigFilePath, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader)) { - project.disableLanguageService(); - project.stopWatchingWildCards(WatcherCloseReason.ProjectReloadHitMaxSize); - project.stopWatchingTypeRoots(WatcherCloseReason.ProjectReloadHitMaxSize); + private getOrCreateInferredProjectForProjectRootPathIfEnabled(info: ScriptInfo, projectRootPath: string | undefined): InferredProject | undefined { + if (!this.useInferredProjectPerProjectRoot) { + return undefined; } - else { - project.enableLanguageService(); - project.watchWildcards(projectOptions.wildcardDirectories); - project.watchTypeRoots(); + + if (projectRootPath) { + // if we have an explicit project root path, find (or create) the matching inferred project. + for (const project of this.inferredProjects) { + if (project.projectRootPath === projectRootPath) { + return project; + } + } + return this.createInferredProject(/*isSingleInferredProject*/ false, projectRootPath); } - this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typeAcquisition, projectOptions.compileOnSave, configFileErrors); + + // we don't have an explicit root path, so we should try to find an inferred project + // that more closely contains the file. + let bestMatch: InferredProject; + for (const project of this.inferredProjects) { + // ignore single inferred projects (handled elsewhere) + if (!project.projectRootPath) continue; + // ignore inferred projects that don't contain the root's path + if (!containsPath(project.projectRootPath, info.path, this.host.getCurrentDirectory(), !this.host.useCaseSensitiveFileNames)) continue; + // ignore inferred projects that are higher up in the project root. + // TODO(rbuckton): Should we add the file as a root to these as well? + if (bestMatch && bestMatch.projectRootPath.length > project.projectRootPath.length) continue; + bestMatch = project; + } + + return bestMatch; } - createInferredProjectWithRootFileIfNecessary(root: ScriptInfo) { - const useExistingProject = this.useSingleInferredProject && this.inferredProjects.length; - const project = useExistingProject - ? this.inferredProjects[0] - : new InferredProject(this, this.documentRegistry, this.compilerOptionsForInferredProjects); + private getOrCreateSingleInferredProjectIfEnabled(): InferredProject | undefined { + if (!this.useSingleInferredProject) { + return undefined; + } - project.addRoot(root); - project.updateGraph(); + // If `useInferredProjectPerProjectRoot` is not enabled, then there will only be one + // inferred project for all files. If `useInferredProjectPerProjectRoot` is enabled + // then we want to put all files that are not opened with a `projectRootPath` into + // the same inferred project. + // + // To avoid the cost of searching through the array and to optimize for the case where + // `useInferredProjectPerProjectRoot` is not enabled, we will always put the inferred + // project for non-rooted files at the front of the array. + if (this.inferredProjects.length > 0 && this.inferredProjects[0].projectRootPath === undefined) { + return this.inferredProjects[0]; + } - if (!useExistingProject) { + return this.createInferredProject(/*isSingleInferredProject*/ true); + } + + private createInferredProject(isSingleInferredProject?: boolean, projectRootPath?: string): InferredProject { + const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects; + const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath); + if (isSingleInferredProject) { + this.inferredProjects.unshift(project); + } + else { this.inferredProjects.push(project); } return project; @@ -1576,8 +1672,8 @@ namespace ts.server { } if (args.extraFileExtensions) { this.hostConfiguration.extraFileExtensions = args.extraFileExtensions; - // We need to update the projects because of we might interprete more/less files - // depending on whether extra files extenstions are either added or removed + // We need to update the project structures again as it is possible that existing + // project structure could have more or less files depending on extensions permitted this.reloadProjects(); this.logger.info("Host file extension mappings updated"); } @@ -1600,13 +1696,15 @@ namespace ts.server { } /* @internal */ - closeDirectoryWatcher(watchType: WatchType, project: Project, directory: string, watcher: FileWatcher, recursive: boolean, reason: WatcherCloseReason) { + closeDirectoryWatcher(watchType: WatchType, project: Project, directory: string, watcher: FileWatcher, flags: WatchDirectoryFlags, reason: WatcherCloseReason) { + const recursive = (flags & WatchDirectoryFlags.Recursive) !== 0; this.logger.info(`DirectoryWatcher ${recursive ? "recursive" : ""}:: Close: ${directory} Project: ${project.getProjectName()} WatchType: ${watchType} Reason: ${reason}`); watcher.close(); } /* @internal */ - addDirectoryWatcher(watchType: WatchType, project: Project, directory: string, cb: ServerDirectoryWatcherCallback, recursive: boolean) { + addDirectoryWatcher(watchType: WatchType, project: Project, directory: string, cb: ServerDirectoryWatcherCallback, flags: WatchDirectoryFlags) { + const recursive = (flags & WatchDirectoryFlags.Recursive) !== 0; this.logger.info(`DirectoryWatcher ${recursive ? "recursive" : ""}:: Added: ${directory} Project: ${project.getProjectName()} WatchType: ${watchType}`); return this.host.watchDirectory(directory, fileName => { const path = toNormalizedPath(getNormalizedAbsolutePath(fileName, directory)); @@ -1624,29 +1722,34 @@ namespace ts.server { */ reloadProjects() { this.logger.info("reload projects."); - this.reloadConfiguredProjectForFiles(this.openFiles, /*delayReload*/ false); + this.reloadConfiguredsProjectForFiles(this.openFiles, /*delayReload*/ false); this.refreshInferredProjects(); } - delayReloadConfiguredProjectForFiles(openFilesMap: Map, ignoreIfNotInferredProjectRoot: boolean) { + private delayReloadConfiguredProjectForFiles(configFileExistenceInfo: ConfigFileExistenceInfo, ignoreIfNotRootOfInferredProject: boolean) { // Get open files to reload projects for - const openFiles = flatMapIter(openFilesMap.keys(), path => { - if (!ignoreIfNotInferredProjectRoot || openFilesMap.get(path)) { - return this.getScriptInfoForPath(path as Path); + const openFiles = mapDefinedIter( + configFileExistenceInfo.openFilesImpactedByConfigFile.entries(), + ([path, isRootOfInferredProject]) => { + if (!ignoreIfNotRootOfInferredProject || isRootOfInferredProject) { + const info = this.getScriptInfoForPath(path as Path); + Debug.assert(!!info); + return info; + } } - }); - this.reloadConfiguredProjectForFiles(openFiles, /*delayReload*/ true); + ); + this.reloadConfiguredsProjectForFiles(openFiles, /*delayReload*/ true); this.delayInferredProjectsRefresh(); } /** * This function goes through all the openFiles and tries to file the config file for them. * If the config file is found and it refers to existing project, it reloads it either immediately - * or schedules it for reload depending on delayedReload option + * or schedules it for reload depending on delayReload option * If the there is no existing project it just opens the configured project for the config file */ - reloadConfiguredProjectForFiles(openFiles: ScriptInfo[], delayReload: boolean) { - const mapUpdatedProjects = createMap(); + private reloadConfiguredsProjectForFiles(openFiles: ScriptInfo[], delayReload: boolean) { + const updatedProjects = createMap(); // try to reload config file for all open files for (const info of openFiles) { // This tries to search for a tsconfig.json for the given file. If we found it, @@ -1655,12 +1758,12 @@ namespace ts.server { // otherwise we create a new one. const configFileName = this.getConfigFileNameForFile(info); if (configFileName) { - let project = this.findConfiguredProjectByProjectName(configFileName); + const project = this.findConfiguredProjectByProjectName(configFileName); if (!project) { - project = this.openConfigFile(configFileName, info.fileName); - mapUpdatedProjects.set(configFileName, true); + this.createConfiguredProject(configFileName, info.fileName); + updatedProjects.set(configFileName, true); } - else if (!mapUpdatedProjects.has(configFileName)) { + else if (!updatedProjects.has(configFileName)) { if (delayReload) { project.pendingReload = true; this.delayUpdateProjectGraph(project); @@ -1668,19 +1771,22 @@ namespace ts.server { else { this.reloadConfiguredProject(project); } - mapUpdatedProjects.set(configFileName, true); + updatedProjects.set(configFileName, true); } } } } /** - * - script info can be never migrate to state - root file in inferred project, this is only a starting point - * - if script info has more that one containing projects - it is not a root file in inferred project because: - * - references in inferred project supercede the root part - * - root/reference in non-inferred project beats root in inferred project + * Remove the root of inferred project if script info is part of another project */ private removeRootOfInferredProjectIfNowPartOfOtherProject(info: ScriptInfo) { + // If the script info is root of inferred project, it could only be first containing project + // since info is added to inferred project and made root only when there are no other projects containing it + // So even if it is root of the inferred project and after project structure updates its now part + // of multiple project it needs to be removed from that inferred project because: + // - references in inferred project supercede the root part + // - root / reference in non - inferred project beats root in inferred project if (info.containingProjects.length > 1 && info.containingProjects[0].projectKind === ProjectKind.Inferred && info.containingProjects[0].isRoot(info)) { @@ -1695,21 +1801,23 @@ namespace ts.server { } /** - * This function is to update the project structure for every projects. + * This function is to update the project structure for every inferred project. * It is called on the premise that all the configured projects are * up to date. + * This will go through open files and assign them to inferred project if open file is not part of any other project + * After that all the inferred project graphs are updated */ private refreshInferredProjects() { this.logger.info("refreshInferredProjects: updating project structure from ..."); this.printProjects(); for (const info of this.openFiles) { - // collect all orphanted script infos from open files + // collect all orphaned script infos from open files if (info.containingProjects.length === 0) { - this.assignScriptInfoToInferredProjectIfNecessary(info, /*addToListOfOpenFiles*/ false); + this.assignScriptInfoToInferredProject(info); } - // Or remove the root of inferred project if is referenced in more than one projects else { + // Or remove the root of inferred project if is referenced in more than one projects this.removeRootOfInferredProjectIfNowPartOfOtherProject(info); } } @@ -1733,7 +1841,7 @@ namespace ts.server { openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult { let configFileName: NormalizedPath; - let configFileErrors: Diagnostic[]; + let configFileErrors: ReadonlyArray; const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent); let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName); @@ -1742,7 +1850,7 @@ namespace ts.server { if (configFileName) { project = this.findConfiguredProjectByProjectName(configFileName); if (!project) { - project = this.openConfigFile(configFileName, fileName); + project = this.createConfiguredProject(configFileName, fileName); // even if opening config file was successful, it could still // contain errors that were tolerated. @@ -1761,8 +1869,13 @@ namespace ts.server { project.markAsDirty(); } - // at this point if file is the part of some configured/external project then this project should be created - this.assignScriptInfoToInferredProjectIfNecessary(info, /*addToListOfOpenFiles*/ true); + // At this point if file is part of any any configured or external project, then it would be present in the containing projects + // So if it still doesnt have any containing projects, it needs to be part of inferred project + if (info.containingProjects.length === 0) { + this.assignScriptInfoToInferredProject(info, projectRootPath); + } + this.addToListOfOpenFiles(info); + // Delete the orphan files here because there might be orphan script infos (which are not part of project) // when some file/s were closed which resulted in project removal. // It was then postponed to cleanup these script infos so that they can be reused if @@ -1823,7 +1936,7 @@ namespace ts.server { if (!this.changedFiles) { this.changedFiles = [scriptInfo]; } - else if (this.changedFiles.indexOf(scriptInfo) < 0) { + else if (!contains(this.changedFiles, scriptInfo)) { this.changedFiles.push(scriptInfo); } } @@ -1837,7 +1950,7 @@ namespace ts.server { // if files were open or closed then explicitly refresh list of inferred projects // otherwise if there were only changes in files - record changed files in `changedFiles` and defer the update if (openFiles || closedFiles) { - this.ensureInferredProjectsUpToDate(/*refreshInferredProjects*/ true); + this.ensureProjectStructuresUptoDate(/*refreshInferredProjects*/ true); } } @@ -1860,7 +1973,7 @@ namespace ts.server { } this.externalProjectToConfiguredProjectMap.delete(fileName); if (shouldRefreshInferredProjects && !suppressRefresh) { - this.ensureInferredProjectsUpToDate(/*refreshInferredProjects*/ true); + this.ensureProjectStructuresUptoDate(/*refreshInferredProjects*/ true); } } else { @@ -1869,7 +1982,7 @@ namespace ts.server { if (externalProject) { this.removeProject(externalProject); if (!suppressRefresh) { - this.ensureInferredProjectsUpToDate(/*refreshInferredProjects*/ true); + this.ensureProjectStructuresUptoDate(/*refreshInferredProjects*/ true); } } } @@ -1893,7 +2006,7 @@ namespace ts.server { this.closeExternalProject(externalProjectName, /*suppressRefresh*/ true); }); - this.ensureInferredProjectsUpToDate(/*refreshInferredProjects*/ true); + this.ensureProjectStructuresUptoDate(/*refreshInferredProjects*/ true); } /** Makes a filename safe to insert in a RegExp */ @@ -1998,8 +2111,7 @@ namespace ts.server { const rootFiles: protocol.ExternalFile[] = []; for (const file of proj.rootFiles) { const normalized = toNormalizedPath(file.fileName); - const baseFileName = getBaseFileName(normalized); - if (baseFileName === "tsconfig.json" || baseFileName === "jsconfig.json") { + if (getBaseConfigFileName(normalized)) { if (this.host.fileExists(normalized)) { (tsConfigFiles || (tsConfigFiles = [])).push(normalized); } @@ -2026,7 +2138,7 @@ namespace ts.server { externalProject.enableLanguageService(); } // external project already exists and not config files were added - update the project and return; - this.updateNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, compilerOptions, proj.typeAcquisition, proj.options.compileOnSave, /*configFileErrors*/ undefined); + this.updateNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, compilerOptions, proj.typeAcquisition, proj.options.compileOnSave); return; } // some config files were added to external project (that previously were not there) @@ -2074,7 +2186,7 @@ namespace ts.server { let project = this.findConfiguredProjectByProjectName(tsconfigFile); if (!project) { // errors are stored in the project - project = this.openConfigFile(tsconfigFile); + project = this.createConfiguredProject(tsconfigFile); } if (project && !contains(exisingConfigFiles, tsconfigFile)) { // keep project alive even if no documents are opened - its lifetime is bound to the lifetime of containing external project @@ -2085,10 +2197,10 @@ namespace ts.server { else { // no config files - remove the item from the collection this.externalProjectToConfiguredProjectMap.delete(proj.projectFileName); - this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition); + this.createExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition); } if (!suppressRefreshOfInferredProjects) { - this.ensureInferredProjectsUpToDate(/*refreshInferredProjects*/ true); + this.ensureProjectStructuresUptoDate(/*refreshInferredProjects*/ true); } } } diff --git a/src/server/project.ts b/src/server/project.ts index 5ea54aca54e..9984788403b 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -54,7 +54,7 @@ namespace ts.server { /* @internal */ export interface ProjectFilesWithTSDiagnostics extends protocol.ProjectFiles { - projectErrors: Diagnostic[]; + projectErrors: ReadonlyArray; } export class UnresolvedImportsMap { @@ -184,7 +184,8 @@ namespace ts.server { log(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`); const result = host.require(resolvedPath, moduleName); if (result.error) { - log(`Failed to load module: ${JSON.stringify(result.error)}`); + const err = result.error.stack || result.error.message || JSON.stringify(result.error); + log(`Failed to load module '${moduleName}': ${err}`); return undefined; } return result.module; @@ -382,10 +383,12 @@ namespace ts.server { this.lsHost = undefined; // Clean up file watchers waiting for missing files - cleanExistingMap(this.missingFilesMap, (missingFilePath, fileWatcher) => { - this.projectService.closeFileWatcher(WatchType.MissingFilePath, this, missingFilePath, fileWatcher, WatcherCloseReason.ProjectClose); - }); - this.missingFilesMap = undefined; + if (this.missingFilesMap) { + clearMap(this.missingFilesMap, (missingFilePath, fileWatcher) => { + this.projectService.closeFileWatcher(WatchType.MissingFilePath, this, missingFilePath, fileWatcher, WatcherCloseReason.ProjectClose); + }); + this.missingFilesMap = undefined; + } // signal language service to release source files acquired from document registry this.languageService.dispose(); @@ -439,7 +442,7 @@ namespace ts.server { return map(this.program.getSourceFiles(), sourceFile => { const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.path); if (!scriptInfo) { - Debug.assert(false, `scriptInfo for a file '${sourceFile.fileName}' is missing.`); + Debug.fail(`scriptInfo for a file '${sourceFile.fileName}' is missing.`); } return scriptInfo; }); @@ -525,13 +528,12 @@ namespace ts.server { // add a root file to project addRoot(info: ScriptInfo) { - if (!this.isRoot(info)) { - this.rootFiles.push(info); - this.rootFilesMap.set(info.path, info); - info.attachToProject(this); + Debug.assert(!this.isRoot(info)); + this.rootFiles.push(info); + this.rootFilesMap.set(info.path, info); + info.attachToProject(this); - this.markAsDirty(); - } + this.markAsDirty(); } // add a root file to project @@ -563,7 +565,7 @@ namespace ts.server { this.projectStateVersion++; } - private extractUnresolvedImportsFromSourceFile(file: SourceFile, result: string[]) { + private extractUnresolvedImportsFromSourceFile(file: SourceFile, result: Push) { const cached = this.cachedUnresolvedImportsPerFile.get(file.path); if (cached) { // found cached result - use it and return @@ -624,7 +626,7 @@ namespace ts.server { for (const sourceFile of this.program.getSourceFiles()) { this.extractUnresolvedImportsFromSourceFile(sourceFile, result); } - this.lastCachedUnresolvedImportsList = toSortedArray(result); + this.lastCachedUnresolvedImportsList = toDeduplicatedSortedArray(result); } unresolvedImports = this.lastCachedUnresolvedImportsList; @@ -683,7 +685,9 @@ namespace ts.server { } // Update the missing file paths watcher - this.missingFilesMap = updateMissingFilePathsWatch(this.program, this.missingFilesMap, + updateMissingFilePathsWatch( + this.program, + this.missingFilesMap || (this.missingFilesMap = createMap()), // Watch the missing files missingFilePath => this.addMissingFileWatcher(missingFilePath), // Files that are no longer missing (e.g. because they are no longer required) @@ -922,7 +926,6 @@ namespace ts.server { * the file and its imports/references are put into an InferredProject. */ export class InferredProject extends Project { - private static readonly newName = (() => { let nextId = 1; return () => { @@ -958,7 +961,7 @@ namespace ts.server { super.setCompilerOptions(newOptions); } - constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions) { + constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions, public readonly projectRootPath?: string | undefined) { super(InferredProject.newName(), ProjectKind.Inferred, projectService, @@ -989,9 +992,11 @@ namespace ts.server { } isProjectWithSingleRoot() { - // - when useSingleInferredProject is not set, we can guarantee that this will be the only root + // - when useSingleInferredProject is not set and projectRootPath is not set, + // we can guarantee that this will be the only root // - other wise it has single root if it has single root script info - return !this.projectService.useSingleInferredProject || this.getRootScriptInfos().length === 1; + return (!this.projectRootPath && !this.projectService.useSingleInferredProject) || + this.getRootScriptInfos().length === 1; } getProjectRootPath() { @@ -999,8 +1004,7 @@ namespace ts.server { if (this.projectService.useSingleInferredProject) { return undefined; } - const rootFiles = this.getRootFiles(); - return getDirectoryPath(rootFiles[0]); + return this.projectRootPath || getDirectoryPath(this.getRootFiles()[0]); } close() { @@ -1017,7 +1021,10 @@ namespace ts.server { } } - type WildCardDirectoryWatchers = { watcher: FileWatcher, recursive: boolean }; + interface WildcardDirectoryWatcher { + watcher: FileWatcher; + flags: WatchDirectoryFlags; + } /** * If a file is opened, the server will look for a tsconfig (or jsconfig) @@ -1028,7 +1035,7 @@ namespace ts.server { private typeAcquisition: TypeAcquisition; /* @internal */ configFileWatcher: FileWatcher; - private directoriesWatchedForWildcards: Map | undefined; + private directoriesWatchedForWildcards: Map | undefined; private typeRootsWatchers: Map | undefined; readonly canonicalConfigFilePath: NormalizedPath; @@ -1183,55 +1190,64 @@ namespace ts.server { } watchWildcards(wildcardDirectories: Map) { - this.directoriesWatchedForWildcards = updateWatchingWildcardDirectories(this.directoriesWatchedForWildcards, + updateWatchingWildcardDirectories( + this.directoriesWatchedForWildcards || (this.directoriesWatchedForWildcards = createMap()), wildcardDirectories, // Create new directory watcher - (directory, recursive) => this.projectService.addDirectoryWatcher( - WatchType.WildCardDirectories, this, directory, + (directory, flags) => this.projectService.addDirectoryWatcher( + WatchType.WildcardDirectories, this, directory, path => this.projectService.onFileAddOrRemoveInWatchedDirectoryOfProject(this, path), - recursive + flags ), // Close directory watcher - (directory, watcher, recursive, recursiveChanged) => this.projectService.closeDirectoryWatcher( - WatchType.WildCardDirectories, this, directory, watcher, recursive, - recursiveChanged ? WatcherCloseReason.RecursiveChanged : WatcherCloseReason.NotNeeded + (directory, wildcardDirectoryWatcher, flagsChanged) => this.closeWildcardDirectoryWatcher( + directory, wildcardDirectoryWatcher, flagsChanged ? WatcherCloseReason.RecursiveChanged : WatcherCloseReason.NotNeeded ) ); } + private closeWildcardDirectoryWatcher(directory: string, { watcher, flags }: WildcardDirectoryWatcher, closeReason: WatcherCloseReason) { + this.projectService.closeDirectoryWatcher(WatchType.WildcardDirectories, this, directory, watcher, flags, closeReason); + } + stopWatchingWildCards(reason: WatcherCloseReason) { - cleanExistingMap( - this.directoriesWatchedForWildcards, - (directory, { watcher, recursive }) => - this.projectService.closeDirectoryWatcher(WatchType.WildCardDirectories, this, - directory, watcher, recursive, reason) - ); - this.directoriesWatchedForWildcards = undefined; + if (this.directoriesWatchedForWildcards) { + clearMap( + this.directoriesWatchedForWildcards, + (directory, wildcardDirectoryWatcher) => this.closeWildcardDirectoryWatcher(directory, wildcardDirectoryWatcher, reason) + ); + this.directoriesWatchedForWildcards = undefined; + } } watchTypeRoots() { const newTypeRoots = arrayToSet(this.getEffectiveTypeRoots(), dir => this.projectService.toCanonicalFileName(dir)); - this.typeRootsWatchers = mutateExistingMapWithNewSet( - this.typeRootsWatchers, newTypeRoots, - // Create new watch - root => this.projectService.addDirectoryWatcher(WatchType.TypeRoot, this, root, - path => this.projectService.onTypeRootFileChanged(this, path), /*recursive*/ false - ), - // Close existing watch thats not needed any more - (directory, watcher) => this.projectService.closeDirectoryWatcher( - WatchType.TypeRoot, this, directory, watcher, /*recursive*/ false, WatcherCloseReason.NotNeeded - ) + mutateMap( + this.typeRootsWatchers || (this.typeRootsWatchers = createMap()), + newTypeRoots, + { + // Create new watch + createNewValue: root => this.projectService.addDirectoryWatcher(WatchType.TypeRoot, this, root, + path => this.projectService.onTypeRootFileChanged(this, path), WatchDirectoryFlags.None + ), + // Close existing watch thats not needed any more + onDeleteExistingValue: (directory, watcher) => this.projectService.closeDirectoryWatcher( + WatchType.TypeRoot, this, directory, watcher, WatchDirectoryFlags.None, WatcherCloseReason.NotNeeded + ) + } ); } stopWatchingTypeRoots(reason: WatcherCloseReason) { - cleanExistingMap( - this.typeRootsWatchers, - (directory, watcher) => - this.projectService.closeDirectoryWatcher(WatchType.TypeRoot, this, - directory, watcher, /*recursive*/ false, reason) - ); - this.typeRootsWatchers = undefined; + if (this.typeRootsWatchers) { + clearMap( + this.typeRootsWatchers, + (directory, watcher) => + this.projectService.closeDirectoryWatcher(WatchType.TypeRoot, this, + directory, watcher, WatchDirectoryFlags.None, reason) + ); + this.typeRootsWatchers = undefined; + } } close() { @@ -1258,6 +1274,16 @@ namespace ts.server { getEffectiveTypeRoots() { return getEffectiveTypeRoots(this.getCompilerOptions(), this.lsHost.host) || []; } + + /*@internal*/ + updateErrorOnNoInputFiles(hasFileNames: boolean) { + if (hasFileNames) { + filterMutate(this.projectErrors, error => !isErrorNoInputFiles(error)); + } + else if (!this.configFileSpecs.filesSpecs && !some(this.projectErrors, isErrorNoInputFiles)) { + this.projectErrors.push(getErrorForNoInputFiles(this.configFileSpecs, this.getConfigFilePath())); + } + } } /** @@ -1290,10 +1316,6 @@ namespace ts.server { return this.typeAcquisition; } - setProjectErrors(projectErrors: Diagnostic[]) { - this.projectErrors = projectErrors; - } - setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void { if (!newTypeAcquisition) { // set default typings options diff --git a/src/server/protocol.ts b/src/server/protocol.ts index b6756a7d4ff..0b7405c7b69 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -914,7 +914,7 @@ namespace ts.server.protocol { /** * An array of span groups (one per file) that refer to the item to be renamed. */ - locs: SpanGroup[]; + locs: ReadonlyArray; } /** @@ -1304,6 +1304,13 @@ namespace ts.server.protocol { * Compiler options to be used with inferred projects. */ options: ExternalProjectCompilerOptions; + + /** + * Specifies the project root path used to scope compiler options. + * It is an error to provide this property if the server has not been started with + * `useInferredProjectPerProjectRoot` enabled. + */ + projectRootPath?: string; } /** @@ -2429,6 +2436,7 @@ namespace ts.server.protocol { paths?: MapLike; plugins?: PluginImport[]; preserveConstEnums?: boolean; + preserveSymlinks?: boolean; project?: string; reactNamespace?: string; removeComments?: boolean; diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 388dcd957dc..6d439e92385 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -16,7 +16,7 @@ namespace ts.server { public getVersion() { return this.svc - ? `SVC-${this.svcVersion}-${this.svc.getSnapshot().version}` + ? `SVC-${this.svcVersion}-${this.svc.getSnapshotVersion()}` : `Text-${this.textVersion}`; } @@ -62,22 +62,19 @@ namespace ts.server { } public getLineInfo(line: number): AbsolutePositionAndLineText { - return this.switchToScriptVersionCache().getSnapshot().index.lineNumberToInfo(line); + return this.switchToScriptVersionCache().getLineInfo(line); } /** * @param line 0 based index */ - lineToTextSpan(line: number) { + lineToTextSpan(line: number): TextSpan { if (!this.svc) { const lineMap = this.getLineMap(); const start = lineMap[line]; // -1 since line is 1-based const end = line + 1 < lineMap.length ? lineMap[line + 1] : this.text.length; return createTextSpanFromBounds(start, end); } - const index = this.svc.getSnapshot().index; - const { lineText, absolutePosition } = index.lineNumberToInfo(line + 1); - const len = lineText !== undefined ? lineText.length : index.absolutePositionOfStartOfLine(line + 2) - absolutePosition; - return createTextSpan(absolutePosition, len); + return this.svc.lineToTextSpan(line); } /** @@ -90,7 +87,7 @@ namespace ts.server { } // TODO: assert this offset is actually on the line - return this.svc.getSnapshot().index.absolutePositionOfStartOfLine(line) + (offset - 1); + return this.svc.lineOffsetToPosition(line, offset); } positionToLineOffset(position: number): protocol.Location { @@ -98,7 +95,7 @@ namespace ts.server { const { line, character } = computeLineAndCharacterOfPosition(this.getLineMap(), position); return { line: line + 1, offset: character + 1 }; } - return this.svc.getSnapshot().index.positionToLineOffset(position); + return this.svc.positionToLineOffset(position); } private getFileText(tempFileName?: string) { diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index b6a189904db..451536a0caa 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -5,7 +5,7 @@ namespace ts.server { const lineCollectionCapacity = 4; - export interface LineCollection { + interface LineCollection { charCount(): number; lineCount(): number; isLeaf(): this is LineLeaf; @@ -17,7 +17,7 @@ namespace ts.server { lineText: string | undefined; } - export enum CharRangeSection { + const enum CharRangeSection { PreStart, Start, Entire, @@ -26,7 +26,7 @@ namespace ts.server { PostEnd } - export interface ILineIndexWalker { + interface ILineIndexWalker { goSubtree: boolean; done: boolean; leaf(relativeStart: number, relativeLength: number, lineCollection: LineLeaf): void; @@ -243,7 +243,7 @@ namespace ts.server { } // text change information - export class TextChange { + class TextChange { constructor(public pos: number, public deleteLen: number, public insertedText?: string) { } @@ -285,17 +285,6 @@ namespace ts.server { } } - latest() { - return this.versions[this.currentVersionToIndex()]; - } - - latestVersion() { - if (this.changes.length > 0) { - this.getSnapshot(); - } - return this.currentVersion; - } - // reload whole script, leaving no change history behind reload reload(script: string) { this.currentVersion++; @@ -314,7 +303,9 @@ namespace ts.server { this.minVersion = this.currentVersion; } - getSnapshot() { + getSnapshot(): IScriptSnapshot { return this._getSnapshot(); } + + private _getSnapshot(): LineIndexSnapshot { let snap = this.versions[this.currentVersionToIndex()]; if (this.changes.length > 0) { let snapIndex = snap.index; @@ -334,6 +325,29 @@ namespace ts.server { return snap; } + getSnapshotVersion(): number { + return this._getSnapshot().version; + } + + getLineInfo(line: number): AbsolutePositionAndLineText { + return this._getSnapshot().index.lineNumberToInfo(line); + } + + lineOffsetToPosition(line: number, column: number): number { + return this._getSnapshot().index.absolutePositionOfStartOfLine(line) + (column - 1); + } + + positionToLineOffset(position: number): protocol.Location { + return this._getSnapshot().index.positionToLineOffset(position); + } + + lineToTextSpan(line: number): TextSpan { + const index = this._getSnapshot().index; + const { lineText, absolutePosition } = index.lineNumberToInfo(line + 1); + const len = lineText !== undefined ? lineText.length : index.absolutePositionOfStartOfLine(line + 2) - absolutePosition; + return createTextSpan(absolutePosition, len); + } + getTextChangesBetweenVersions(oldVersion: number, newVersion: number) { if (oldVersion < newVersion) { if (oldVersion >= this.minVersion) { @@ -365,7 +379,7 @@ namespace ts.server { } } - export class LineIndexSnapshot implements IScriptSnapshot { + class LineIndexSnapshot implements IScriptSnapshot { constructor(readonly version: number, readonly cache: ScriptVersionCache, readonly index: LineIndex, readonly changesSincePreviousVersion: ReadonlyArray = emptyArray) { } @@ -389,6 +403,7 @@ namespace ts.server { } } + /* @internal */ export class LineIndex { root: LineNode; // set this to true to check each edit for accuracy @@ -561,7 +576,7 @@ namespace ts.server { } } - export class LineNode implements LineCollection { + class LineNode implements LineCollection { totalChars = 0; totalLines = 0; @@ -660,45 +675,29 @@ namespace ts.server { // Input position is relative to the start of this node. // Output line number is absolute. charOffsetToLineInfo(lineNumberAccumulator: number, relativePosition: number): { oneBasedLine: number, zeroBasedColumn: number, lineText: string | undefined } { - const childInfo = this.childFromCharOffset(lineNumberAccumulator, relativePosition); - if (!childInfo.child) { - return { - oneBasedLine: lineNumberAccumulator, - zeroBasedColumn: relativePosition, - lineText: undefined, - }; + if (this.children.length === 0) { + // Root node might have no children if this is an empty document. + return { oneBasedLine: lineNumberAccumulator, zeroBasedColumn: relativePosition, lineText: undefined }; } - else if (childInfo.childIndex < this.children.length) { - if (childInfo.child.isLeaf()) { - return { - oneBasedLine: childInfo.lineNumberAccumulator, - zeroBasedColumn: childInfo.relativePosition, - lineText: childInfo.child.text, - }; + + for (const child of this.children) { + if (child.charCount() > relativePosition) { + if (child.isLeaf()) { + return { oneBasedLine: lineNumberAccumulator, zeroBasedColumn: relativePosition, lineText: child.text }; + } + else { + return (child).charOffsetToLineInfo(lineNumberAccumulator, relativePosition); + } } else { - const lineNode = (childInfo.child); - return lineNode.charOffsetToLineInfo(childInfo.lineNumberAccumulator, childInfo.relativePosition); + relativePosition -= child.charCount(); + lineNumberAccumulator += child.lineCount(); } } - else { - const lineInfo = this.lineNumberToInfo(this.lineCount(), 0); - return { oneBasedLine: this.lineCount(), zeroBasedColumn: lineInfo.leaf.charCount(), lineText: undefined }; - } - } - lineNumberToInfo(relativeOneBasedLine: number, positionAccumulator: number): { position: number, leaf: LineLeaf | undefined } { - const childInfo = this.childFromLineNumber(relativeOneBasedLine, positionAccumulator); - if (!childInfo.child) { - return { position: positionAccumulator, leaf: undefined }; - } - else if (childInfo.child.isLeaf()) { - return { position: childInfo.positionAccumulator, leaf: childInfo.child }; - } - else { - const lineNode = (childInfo.child); - return lineNode.lineNumberToInfo(childInfo.relativeOneBasedLine, childInfo.positionAccumulator); - } + // Skipped all children + const { leaf } = this.lineNumberToInfo(this.lineCount(), 0); + return { oneBasedLine: this.lineCount(), zeroBasedColumn: leaf.charCount(), lineText: undefined }; } /** @@ -706,39 +705,19 @@ namespace ts.server { * Output line number is relative to the child. * positionAccumulator will be an absolute position once relativeLineNumber reaches 0. */ - private childFromLineNumber(relativeOneBasedLine: number, positionAccumulator: number): { child: LineCollection, relativeOneBasedLine: number, positionAccumulator: number } { - let child: LineCollection; - let i: number; - for (i = 0; i < this.children.length; i++) { - child = this.children[i]; + lineNumberToInfo(relativeOneBasedLine: number, positionAccumulator: number): { position: number, leaf: LineLeaf | undefined } { + for (const child of this.children) { const childLineCount = child.lineCount(); if (childLineCount >= relativeOneBasedLine) { - break; + return child.isLeaf() ? { position: positionAccumulator, leaf: child } : (child).lineNumberToInfo(relativeOneBasedLine, positionAccumulator); } else { relativeOneBasedLine -= childLineCount; positionAccumulator += child.charCount(); } } - return { child, relativeOneBasedLine, positionAccumulator }; - } - private childFromCharOffset(lineNumberAccumulator: number, relativePosition: number - ): { child: LineCollection, childIndex: number, relativePosition: number, lineNumberAccumulator: number } { - let child: LineCollection; - let i: number; - let len: number; - for (i = 0, len = this.children.length; i < len; i++) { - child = this.children[i]; - if (child.charCount() > relativePosition) { - break; - } - else { - relativePosition -= child.charCount(); - lineNumberAccumulator += child.lineCount(); - } - } - return { child, childIndex: i, relativePosition, lineNumberAccumulator }; + return { position: positionAccumulator, leaf: undefined }; } private splitAfter(childIndex: number) { @@ -844,7 +823,7 @@ namespace ts.server { } } - export class LineLeaf implements LineCollection { + class LineLeaf implements LineCollection { constructor(public text: string) { } diff --git a/src/server/server.ts b/src/server/server.ts index b72cc2f5a81..66467fd46ae 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -9,14 +9,15 @@ namespace ts.server { canUseEvents: boolean; installerEventPort: number; useSingleInferredProject: boolean; + useInferredProjectPerProjectRoot: boolean; disableAutomaticTypingAcquisition: boolean; globalTypingsCacheLocation: string; logger: Logger; typingSafeListLocation: string; npmLocation: string | undefined; telemetryEnabled: boolean; - globalPlugins: string[]; - pluginProbeLocations: string[]; + globalPlugins: ReadonlyArray; + pluginProbeLocations: ReadonlyArray; allowLocalPluginLoads: boolean; } @@ -116,8 +117,6 @@ namespace ts.server { birthtime: Date; } - type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} }; - const readline: { createInterface(options: ReadLineOptions): NodeJS.EventEmitter; } = require("readline"); @@ -141,8 +140,6 @@ namespace ts.server { class Logger implements server.Logger { private fd = -1; private seq = 0; - private inGroup = false; - private firstInGroup = true; constructor(private readonly logFilename: string, private readonly traceToConsole: boolean, @@ -172,22 +169,24 @@ namespace ts.server { } perftrc(s: string) { - this.msg(s, Msg.Perf); + this.msg(s, "Perf"); } info(s: string) { - this.msg(s, Msg.Info); + this.msg(s, "Info"); } - startGroup() { - this.inGroup = true; - this.firstInGroup = true; + err(s: string) { + this.msg(s, "Err"); } - endGroup() { - this.inGroup = false; + group(logGroupEntries: (log: (msg: string) => void) => void) { + let firstInGroup = false; + logGroupEntries(s => { + this.msg(s, "Info", /*inGroup*/ true, firstInGroup); + firstInGroup = false; + }); this.seq++; - this.firstInGroup = true; } loggingEnabled() { @@ -198,26 +197,32 @@ namespace ts.server { return this.loggingEnabled() && this.level >= level; } - msg(s: string, type: Msg.Types = Msg.Err) { - if (this.fd >= 0 || this.traceToConsole) { - s = `[${nowString()}] ${s}\n`; + private msg(s: string, type: string, inGroup = false, firstInGroup = false) { + if (!this.canWrite) return; + + s = `[${nowString()}] ${s}\n`; + if (!inGroup || firstInGroup) { const prefix = Logger.padStringRight(type + " " + this.seq.toString(), " "); - if (this.firstInGroup) { - s = prefix + s; - this.firstInGroup = false; - } - if (!this.inGroup) { - this.seq++; - this.firstInGroup = true; - } - if (this.fd >= 0) { - const buf = new Buffer(s); - // tslint:disable-next-line no-null-keyword - fs.writeSync(this.fd, buf, 0, buf.length, /*position*/ null); - } - if (this.traceToConsole) { - console.warn(s); - } + s = prefix + s; + } + this.write(s); + if (!inGroup) { + this.seq++; + } + } + + private get canWrite() { + return this.fd >= 0 || this.traceToConsole; + } + + private write(s: string) { + if (this.fd >= 0) { + const buf = new Buffer(s); + // tslint:disable-next-line no-null-keyword + fs.writeSync(this.fd, buf, 0, buf.length, /*position*/ null); + } + if (this.traceToConsole) { + console.warn(s); } } } @@ -410,6 +415,7 @@ namespace ts.server { host, cancellationToken, useSingleInferredProject, + useInferredProjectPerProjectRoot, typingsInstaller: typingsInstaller || nullTypingsInstaller, byteLength: Buffer.byteLength, hrtime: process.hrtime, @@ -762,11 +768,20 @@ namespace ts.server { const typingSafeListLocation = findArgument(Arguments.TypingSafeListLocation); const npmLocation = findArgument(Arguments.NpmLocation); - const globalPlugins = (findArgument("--globalPlugins") || "").split(","); - const pluginProbeLocations = (findArgument("--pluginProbeLocations") || "").split(","); + function parseStringArray(argName: string): ReadonlyArray { + const arg = findArgument(argName); + if (arg === undefined) { + return emptyArray; + } + return arg.split(",").filter(name => name !== ""); + } + + const globalPlugins = parseStringArray("--globalPlugins"); + const pluginProbeLocations = parseStringArray("--pluginProbeLocations"); const allowLocalPluginLoads = hasArgument("--allowLocalPluginLoads"); const useSingleInferredProject = hasArgument("--useSingleInferredProject"); + const useInferredProjectPerProjectRoot = hasArgument("--useInferredProjectPerProjectRoot"); const disableAutomaticTypingAcquisition = hasArgument("--disableAutomaticTypingAcquisition"); const telemetryEnabled = hasArgument(Arguments.EnableTelemetry); @@ -776,6 +791,7 @@ namespace ts.server { installerEventPort: eventPort, canUseEvents: eventPort === undefined, useSingleInferredProject, + useInferredProjectPerProjectRoot, disableAutomaticTypingAcquisition, globalTypingsCacheLocation: getGlobalTypingsCacheLocation(), typingSafeListLocation, diff --git a/src/server/session.ts b/src/server/session.ts index 31cb8c1e15d..9ef0def5085 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -163,26 +163,22 @@ namespace ts.server { * Scheduling is done via instance of NextStep. If on current step subsequent step was not scheduled - operation is assumed to be completed. */ class MultistepOperation implements NextStep { - private requestId: number; + private requestId: number | undefined; private timerHandle: any; - private immediateId: any; - private completed = true; + private immediateId: number | undefined; constructor(private readonly operationHost: MultistepOperationHost) {} public startNew(action: (next: NextStep) => void) { this.complete(); this.requestId = this.operationHost.getCurrentRequestId(); - this.completed = false; this.executeAction(action); } private complete() { - if (!this.completed) { - if (this.requestId) { - this.operationHost.sendRequestCompletedEvent(this.requestId); - } - this.completed = true; + if (this.requestId !== undefined) { + this.operationHost.sendRequestCompletedEvent(this.requestId); + this.requestId = undefined; } this.setTimerHandle(undefined); this.setImmediateId(undefined); @@ -251,6 +247,7 @@ namespace ts.server { host: ServerHost; cancellationToken: ServerCancellationToken; useSingleInferredProject: boolean; + useInferredProjectPerProjectRoot: boolean; typingsInstaller: ITypingsInstaller; byteLength: (buf: string, encoding?: string) => number; hrtime: (start?: number[]) => number[]; @@ -259,8 +256,8 @@ namespace ts.server { eventHandler?: ProjectServiceEventHandler; throttleWaitMilliseconds?: number; - globalPlugins?: string[]; - pluginProbeLocations?: string[]; + globalPlugins?: ReadonlyArray; + pluginProbeLocations?: ReadonlyArray; allowLocalPluginLoads?: boolean; } @@ -311,6 +308,7 @@ namespace ts.server { logger: this.logger, cancellationToken: this.cancellationToken, useSingleInferredProject: opts.useSingleInferredProject, + useInferredProjectPerProjectRoot: opts.useInferredProjectPerProjectRoot, typingsInstaller: this.typingsInstaller, throttleWaitMilliseconds, eventHandler: this.eventHandler, @@ -337,7 +335,7 @@ namespace ts.server { case ContextEvent: const { project, fileName } = event.data; this.projectService.logger.info(`got context event, updating diagnostics for ${fileName}`); - this.errorCheck.startNew(next => this.updateErrorCheck(next, [{ fileName, project }], this.changeSeq, (n) => n === this.changeSeq, 100)); + this.errorCheck.startNew(next => this.updateErrorCheck(next, [{ fileName, project }], 100)); break; case ConfigFileDiagEvent: const { triggerFile, configFileName, diagnostics } = event.data; @@ -370,7 +368,7 @@ namespace ts.server { msg += "\n" + (err).stack; } } - this.logger.msg(msg, Msg.Err); + this.logger.err(msg); } public send(msg: protocol.Message) { @@ -383,7 +381,7 @@ namespace ts.server { this.host.write(formatMessage(msg, this.logger, this.byteLength, this.host.newLine)); } - public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: Diagnostic[]) { + public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: ReadonlyArray) { const bakedDiags = map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true)); const ev: protocol.ConfigFileDiagnosticEvent = { seq: 0, @@ -453,14 +451,13 @@ namespace ts.server { } } - private updateErrorCheck(next: NextStep, checkList: PendingErrorCheck[], seq: number, matchSeq: (seq: number) => boolean, ms = 1500, followMs = 200, requireOpen = true) { - if (followMs > ms) { - followMs = ms; - } + private updateErrorCheck(next: NextStep, checkList: PendingErrorCheck[], ms: number, requireOpen = true) { + const seq = this.changeSeq; + const followMs = Math.min(ms, 200); let index = 0; const checkOne = () => { - if (matchSeq(seq)) { + if (this.changeSeq === seq) { const checkSpec = checkList[index]; index++; if (checkSpec.project.containsFile(checkSpec.fileName, requireOpen)) { @@ -475,7 +472,7 @@ namespace ts.server { } }; - if ((checkList.length > index) && (matchSeq(seq))) { + if (checkList.length > index && this.changeSeq === seq) { next.delay(ms, checkOne); } } @@ -534,8 +531,8 @@ namespace ts.server { ); } - private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: Diagnostic[]) { - return diagnostics.map(d => { + private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: ReadonlyArray): protocol.DiagnosticWithLinePosition[] { + return diagnostics.map(d => ({ message: flattenDiagnosticMessageText(d.messageText, this.host.newLine), start: d.start, length: d.length, @@ -543,7 +540,7 @@ namespace ts.server { code: d.code, startLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start)), endLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start + d.length)) - }); + })); } private getCompilerOptionsDiagnostics(args: protocol.CompilerOptionsDiagnosticsRequestArgs) { @@ -560,7 +557,7 @@ namespace ts.server { ); } - private convertToDiagnosticsWithLinePosition(diagnostics: Diagnostic[], scriptInfo: ScriptInfo) { + private convertToDiagnosticsWithLinePosition(diagnostics: ReadonlyArray, scriptInfo: ScriptInfo): protocol.DiagnosticWithLinePosition[] { return diagnostics.map(d => { message: flattenDiagnosticMessageText(d.messageText, this.host.newLine), start: d.start, @@ -573,10 +570,12 @@ namespace ts.server { }); } - private getDiagnosticsWorker(args: protocol.FileRequestArgs, isSemantic: boolean, selector: (project: Project, file: string) => Diagnostic[], includeLinePosition: boolean) { + private getDiagnosticsWorker( + args: protocol.FileRequestArgs, isSemantic: boolean, selector: (project: Project, file: string) => ReadonlyArray, includeLinePosition: boolean + ): ReadonlyArray | ReadonlyArray { const { project, file } = this.getFileAndProject(args); if (isSemantic && isDeclarationFileInJSOnlyNonConfiguredProject(project, file)) { - return []; + return emptyArray; } const scriptInfo = project.getScriptInfoForNormalizedPath(file); const diagnostics = selector(project, file); @@ -585,14 +584,14 @@ namespace ts.server { : diagnostics.map(d => formatDiag(file, project, d)); } - private getDefinition(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.FileSpan[] | DefinitionInfo[] { + private getDefinition(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); const definitions = project.getLanguageService().getDefinitionAtPosition(file, position); if (!definitions) { - return undefined; + return emptyArray; } if (simplifiedResult) { @@ -610,7 +609,7 @@ namespace ts.server { } } - private getTypeDefinition(args: protocol.FileLocationRequestArgs): protocol.FileSpan[] { + private getTypeDefinition(args: protocol.FileLocationRequestArgs): ReadonlyArray { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); @@ -630,12 +629,12 @@ namespace ts.server { }); } - private getImplementation(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.FileSpan[] | ImplementationLocation[] { + private getImplementation(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { const { file, project } = this.getFileAndProject(args); const position = this.getPosition(args, project.getScriptInfoForNormalizedPath(file)); const implementations = project.getLanguageService().getImplementationAtPosition(file, position); if (!implementations) { - return []; + return emptyArray; } if (simplifiedResult) { return implementations.map(({ fileName, textSpan }) => { @@ -652,7 +651,7 @@ namespace ts.server { } } - private getOccurrences(args: protocol.FileLocationRequestArgs): protocol.OccurrencesResponseItem[] { + private getOccurrences(args: protocol.FileLocationRequestArgs): ReadonlyArray { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); @@ -660,7 +659,7 @@ namespace ts.server { const occurrences = project.getLanguageService().getOccurrencesAtPosition(file, position); if (!occurrences) { - return undefined; + return emptyArray; } return occurrences.map(occurrence => { @@ -682,17 +681,17 @@ namespace ts.server { }); } - private getSyntacticDiagnosticsSync(args: protocol.SyntacticDiagnosticsSyncRequestArgs): protocol.Diagnostic[] | protocol.DiagnosticWithLinePosition[] { + private getSyntacticDiagnosticsSync(args: protocol.SyntacticDiagnosticsSyncRequestArgs): ReadonlyArray | ReadonlyArray { const { configFile } = this.getConfigFileAndProject(args); if (configFile) { // all the config file errors are reported as part of semantic check so nothing to report here - return []; + return emptyArray; } return this.getDiagnosticsWorker(args, /*isSemantic*/ false, (project, file) => project.getLanguageService().getSyntacticDiagnostics(file), args.includeLinePosition); } - private getSemanticDiagnosticsSync(args: protocol.SemanticDiagnosticsSyncRequestArgs): protocol.Diagnostic[] | protocol.DiagnosticWithLinePosition[] { + private getSemanticDiagnosticsSync(args: protocol.SemanticDiagnosticsSyncRequestArgs): ReadonlyArray | ReadonlyArray { const { configFile, project } = this.getConfigFileAndProject(args); if (configFile) { return this.getConfigFileDiagnostics(configFile, project, args.includeLinePosition); @@ -700,7 +699,7 @@ namespace ts.server { return this.getDiagnosticsWorker(args, /*isSemantic*/ true, (project, file) => project.getLanguageService().getSemanticDiagnostics(file), args.includeLinePosition); } - private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): protocol.DocumentHighlightsItem[] | DocumentHighlights[] { + private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); @@ -736,7 +735,7 @@ namespace ts.server { } private setCompilerOptionsForInferredProjects(args: protocol.SetCompilerOptionsForInferredProjectsArgs): void { - this.projectService.setCompilerOptionsForInferredProjects(args.options); + this.projectService.setCompilerOptionsForInferredProjects(args.options, args.projectRootPath); } private getProjectInfo(args: protocol.ProjectInfoRequestArgs): protocol.ProjectInfo { @@ -791,7 +790,7 @@ namespace ts.server { return info.getDefaultProject(); } - private getRenameLocations(args: protocol.RenameRequestArgs, simplifiedResult: boolean): protocol.RenameResponseBody | RenameLocation[] { + private getRenameLocations(args: protocol.RenameRequestArgs, simplifiedResult: boolean): protocol.RenameResponseBody | ReadonlyArray { const file = toNormalizedPath(args.file); const info = this.projectService.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, info); @@ -808,7 +807,7 @@ namespace ts.server { if (!renameInfo.canRename) { return { info: renameInfo, - locs: [] + locs: emptyArray }; } @@ -817,12 +816,12 @@ namespace ts.server { (project: Project) => { const renameLocations = project.getLanguageService().findRenameLocations(file, position, args.findInStrings, args.findInComments); if (!renameLocations) { - return []; + return emptyArray; } return renameLocations.map(location => { const locationScriptInfo = project.getScriptInfo(location.fileName); - return { + return { file: location.fileName, start: locationScriptInfo.positionToLineOffset(location.textSpan.start), end: locationScriptInfo.positionToLineOffset(textSpanEnd(location.textSpan)), @@ -894,7 +893,7 @@ namespace ts.server { } } - private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | ReferencedSymbol[] { + private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | ReadonlyArray { const file = toNormalizedPath(args.file); const projects = this.getProjects(args); @@ -904,7 +903,7 @@ namespace ts.server { if (simplifiedResult) { const nameInfo = defaultProject.getLanguageService().getQuickInfoAtPosition(file, position); if (!nameInfo) { - return undefined; + return emptyArray; } const displayString = displayPartsToString(nameInfo.displayParts); @@ -916,7 +915,7 @@ namespace ts.server { (project: Project) => { const references = project.getLanguageService().getReferencesAtPosition(file, position); if (!references) { - return []; + return emptyArray; } return references.map(ref => { @@ -973,7 +972,7 @@ namespace ts.server { if (this.eventHandler) { this.eventHandler({ eventName: "configFileDiag", - data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || [] } + data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || emptyArray } }); } } @@ -1158,7 +1157,7 @@ namespace ts.server { }); } - private getCompletions(args: protocol.CompletionsRequestArgs, simplifiedResult: boolean): protocol.CompletionEntry[] | CompletionInfo { + private getCompletions(args: protocol.CompletionsRequestArgs, simplifiedResult: boolean): ReadonlyArray | CompletionInfo { const prefix = args.prefix || ""; const { file, project } = this.getFileAndProject(args); @@ -1167,7 +1166,7 @@ namespace ts.server { const completions = project.getLanguageService().getCompletionsAtPosition(file, position); if (!completions) { - return undefined; + return emptyArray; } if (simplifiedResult) { return mapDefined(completions.entries, entry => { @@ -1183,7 +1182,7 @@ namespace ts.server { } } - private getCompletionEntryDetails(args: protocol.CompletionDetailsRequestArgs): protocol.CompletionEntryDetails[] { + private getCompletionEntryDetails(args: protocol.CompletionDetailsRequestArgs): ReadonlyArray { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); @@ -1192,14 +1191,14 @@ namespace ts.server { project.getLanguageService().getCompletionEntryDetails(file, position, entryName)); } - private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): protocol.CompileOnSaveAffectedFileListSingleProject[] { + private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): ReadonlyArray { const info = this.projectService.getScriptInfoEnsuringProjectsUptoDate(args.file); - const result: protocol.CompileOnSaveAffectedFileListSingleProject[] = []; - if (!info) { - return result; + return emptyArray; } + const result: protocol.CompileOnSaveAffectedFileListSingleProject[] = []; + // if specified a project, we only return affected file list in this project const projectsToSearch = args.projectFileName ? [this.projectService.findProject(args.projectFileName)] : info.containingProjects; for (const project of projectsToSearch) { @@ -1254,14 +1253,14 @@ namespace ts.server { } private getDiagnostics(next: NextStep, delay: number, fileNames: string[]): void { - const checkList = mapDefined(fileNames, uncheckedFileName => { + const checkList = mapDefined(fileNames, uncheckedFileName => { const fileName = toNormalizedPath(uncheckedFileName); const project = this.projectService.getDefaultProjectForFile(fileName, /*refreshInferredProjects*/ true); return project && { fileName, project }; }); if (checkList.length > 0) { - this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay); + this.updateErrorCheck(next, checkList, delay); } } @@ -1355,7 +1354,7 @@ namespace ts.server { : tree; } - private getNavigateToItems(args: protocol.NavtoRequestArgs, simplifiedResult: boolean): protocol.NavtoItem[] | NavigateToItem[] { + private getNavigateToItems(args: protocol.NavtoRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { const projects = this.getProjects(args); const fileName = args.currentFileOnly ? args.file && normalizeSlashes(args.file) : undefined; @@ -1365,7 +1364,7 @@ namespace ts.server { project => { const navItems = project.getLanguageService().getNavigateToItems(args.searchValue, args.maxResultCount, fileName, /*excludeDts*/ project.isNonTsProject()); if (!navItems) { - return []; + return emptyArray; } return navItems.map((navItem) => { @@ -1630,7 +1629,7 @@ namespace ts.server { const checkList = fileNamesInProject.map(fileName => ({ fileName, project })); // Project level error analysis runs on background files too, therefore // doesn't require the file to be opened - this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay, 200, /*requireOpen*/ false); + this.updateErrorCheck(next, checkList, delay, /*requireOpen*/ false); } } @@ -1941,7 +1940,7 @@ namespace ts.server { return this.executeWithRequestId(request.seq, () => handler(request)); } else { - this.logger.msg(`Unrecognized JSON command: ${JSON.stringify(request)}`, Msg.Err); + this.logger.err(`Unrecognized JSON command: ${JSON.stringify(request)}`); this.output(undefined, CommandNames.Unknown, request.seq, `Unrecognized JSON command: ${request.command}`); return { responseRequired: false }; } diff --git a/src/server/types.ts b/src/server/types.ts index 07b94fe827e..4fc4356a4a9 100644 --- a/src/server/types.ts +++ b/src/server/types.ts @@ -9,7 +9,7 @@ declare namespace ts.server { data: any; } - type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} }; + type RequireResult = { module: {}, error: undefined } | { module: undefined, error: { stack?: string, message?: string } }; export interface ServerHost extends System { setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; clearTimeout(timeoutId: any): void; diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 8a3840fd984..df2b6916e4b 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -356,14 +356,15 @@ namespace ts.server.typingsInstaller { this.sendResponse(this.createSetTypings(req, currentlyCachedTypings.concat(installedTypingFiles))); } finally { - this.sendResponse({ + const response: EndInstallTypes = { kind: EventEndInstallTypes, eventId: requestId, projectName: req.projectName, packagesToInstall: scopedTypings, installSuccess: ok, typingsInstallerVersion: ts.version // qualified explicitly to prevent occasional shadowing - }); + }; + this.sendResponse(response); } }); } diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 85bba441e51..179609a7db8 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -17,22 +17,11 @@ namespace ts.server { loggingEnabled(): boolean; perftrc(s: string): void; info(s: string): void; - startGroup(): void; - endGroup(): void; - msg(s: string, type?: Msg.Types): void; + err(s: string): void; + group(logGroupEntries: (log: (msg: string) => void) => void): void; getLogFileName(): string; } - export namespace Msg { - export type Err = "Err"; - export const Err: Err = "Err"; - export type Info = "Info"; - export const Info: Info = "Info"; - export type Perf = "Perf"; - export const Perf: Perf = "Perf"; - export type Types = Err | Info | Perf; - } - function getProjectRootPath(project: Project): Path { switch (project.projectKind) { case ProjectKind.Configured: @@ -227,6 +216,11 @@ namespace ts.server { /* @internal */ namespace ts.server { + export function getBaseConfigFileName(configFilePath: NormalizedPath): "tsconfig.json" | "jsconfig.json" | undefined { + const base = getBaseFileName(configFilePath); + return base === "tsconfig.json" || base === "jsconfig.json" ? base : undefined; + } + export function insertSorted(array: SortedArray, insert: T, compare: Comparer): void { if (array.length === 0) { array.push(insert); @@ -262,6 +256,15 @@ namespace ts.server { return arr as SortedArray; } + export function toDeduplicatedSortedArray(arr: string[]): SortedArray { + arr.sort(); + filterMutate(arr, isNonDuplicateInSortedArray); + return arr as SortedArray; + } + function isNonDuplicateInSortedArray(value: T, index: number, array: T[]) { + return index === 0 || value !== array[index - 1]; + } + export function enumerateInsertsAndDeletes(newItems: SortedReadonlyArray, oldItems: SortedReadonlyArray, inserted: (newItem: T) => void, deleted: (oldItem: T) => void, compare?: Comparer) { compare = compare || compareValues; let newIndex = 0; diff --git a/src/services/classifier.ts b/src/services/classifier.ts index dc5d99bc490..4552d8bf985 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -260,11 +260,11 @@ namespace ts { templateStack.pop(); } else { - Debug.assert(token === SyntaxKind.TemplateMiddle, "Should have been a template middle. Was " + token); + Debug.assertEqual(token, SyntaxKind.TemplateMiddle, "Should have been a template middle."); } } else { - Debug.assert(lastTemplateStackToken === SyntaxKind.OpenBraceToken, "Should have been an open brace. Was: " + token); + Debug.assertEqual(lastTemplateStackToken, SyntaxKind.OpenBraceToken, "Should have been an open brace"); templateStack.pop(); } } diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index d6fb8de9259..d0b52184031 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -147,7 +147,7 @@ namespace ts.codefix { } else if (isJsxOpeningLikeElement(token.parent) && token.parent.tagName === token) { // The error wasn't for the symbolAtLocation, it was for the JSX tag itself, which needs access to e.g. `React`. - symbol = checker.getAliasedSymbol(checker.resolveNameAtLocation(token, checker.getJsxNamespace(), SymbolFlags.Value)); + symbol = checker.getAliasedSymbol(checker.resolveName(checker.getJsxNamespace(), token.parent.tagName, SymbolFlags.Value)); symbolName = symbol.name; } else { @@ -394,9 +394,11 @@ namespace ts.codefix { : isNamespaceImport ? createImportClause(/*name*/ undefined, createNamespaceImport(createIdentifier(symbolName))) : createImportClause(/*name*/ undefined, createNamedImports([createImportSpecifier(/*propertyName*/ undefined, createIdentifier(symbolName))])); - const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, createLiteral(moduleSpecifierWithoutQuotes)); + const moduleSpecifierLiteral = createLiteral(moduleSpecifierWithoutQuotes); + moduleSpecifierLiteral.singleQuote = getSingleQuoteStyleFromExistingImports(); + const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, moduleSpecifierLiteral); if (!lastImportDeclaration) { - changeTracker.insertNodeAt(sourceFile, sourceFile.getStart(), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` }); + changeTracker.insertNodeAt(sourceFile, getSourceFileImportLocation(sourceFile), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` }); } else { changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: context.newLineCharacter }); @@ -413,6 +415,46 @@ namespace ts.codefix { moduleSpecifierWithoutQuotes ); + function getSourceFileImportLocation(node: SourceFile) { + // For a source file, it is possible there are detached comments we should not skip + const text = node.text; + let ranges = getLeadingCommentRanges(text, 0); + if (!ranges) return 0; + let position = 0; + // However we should still skip a pinned comment at the top + if (ranges.length && ranges[0].kind === SyntaxKind.MultiLineCommentTrivia && isPinnedComment(text, ranges[0])) { + position = ranges[0].end + 1; + ranges = ranges.slice(1); + } + // As well as any triple slash references + for (const range of ranges) { + if (range.kind === SyntaxKind.SingleLineCommentTrivia && isRecognizedTripleSlashComment(node.text, range.pos, range.end)) { + position = range.end + 1; + continue; + } + break; + } + return position; + } + + function getSingleQuoteStyleFromExistingImports() { + const firstModuleSpecifier = forEach(sourceFile.statements, node => { + if (isImportDeclaration(node) || isExportDeclaration(node)) { + if (node.moduleSpecifier && isStringLiteral(node.moduleSpecifier)) { + return node.moduleSpecifier; + } + } + else if (isImportEqualsDeclaration(node)) { + if (isExternalModuleReference(node.moduleReference) && isStringLiteral(node.moduleReference.expression)) { + return node.moduleReference.expression; + } + } + }); + if (firstModuleSpecifier) { + return sourceFile.text.charCodeAt(firstModuleSpecifier.getStart()) === CharacterCodes.singleQuote; + } + } + function getModuleSpecifierForNewImport() { const fileName = sourceFile.fileName; const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName; diff --git a/src/services/completions.ts b/src/services/completions.ts index b49567ef217..fde07aa78f6 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1000,7 +1000,7 @@ namespace ts.Completions { const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer); if (!typeForObject) return false; // In a binding pattern, get only known properties. Everywhere else we will get all possible properties. - typeMembers = typeChecker.getPropertiesOfType(typeForObject); + typeMembers = typeChecker.getPropertiesOfType(typeForObject).filter((symbol) => !(getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.NonPublicAccessibilityModifier)); existingMembers = (objectLikeContainer).elements; } } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 204de12a10c..ad26dae2115 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -652,10 +652,15 @@ namespace ts.FindAllReferences.Core { return undefined; } - // If the symbol has a parent, it's globally visible. - // Unless that parent is an external module, then we should only search in the module (and recurse on the export later). - // But if the parent is a module that has `export as namespace`, then the symbol *is* globally visible. - if (parent && !((parent.flags & SymbolFlags.Module) && isExternalModuleSymbol(parent) && !parent.globalExports)) { + /* + If the symbol has a parent, it's globally visible unless: + - It's a private property (handled above). + - It's a type parameter. + - The parent is an external module: then we should only search in the module (and recurse on the export later). + - But if the parent has `export as namespace`, the symbol is globally visible through that namespace. + */ + const exposedByParent = parent && !(symbol.flags & SymbolFlags.TypeParameter); + if (exposedByParent && !((parent.flags & SymbolFlags.Module) && isExternalModuleSymbol(parent) && !parent.globalExports)) { return undefined; } @@ -682,7 +687,7 @@ namespace ts.FindAllReferences.Core { // declare module "a" { export type T = number; } // declare module "b" { import { T } from "a"; export const x: T; } // So we must search the whole source file. (Because we will mark the source file as seen, we we won't return to it when searching for imports.) - return parent ? scope.getSourceFile() : scope; + return exposedByParent ? scope.getSourceFile() : scope; } function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): number[] { diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 15bbf5041d3..2daf8d9d284 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -195,6 +195,7 @@ namespace ts.formatting { // Insert space after opening and before closing nonempty parenthesis public SpaceAfterOpenParen: Rule; public SpaceBeforeCloseParen: Rule; + public SpaceBetweenOpenParens: Rule; public NoSpaceBetweenParens: Rule; public NoSpaceAfterOpenParen: Rule; public NoSpaceBeforeCloseParen: Rule; @@ -457,6 +458,7 @@ namespace ts.formatting { // Insert space after opening and before closing nonempty parenthesis this.SpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space)); this.SpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space)); + this.SpaceBetweenOpenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space)); this.NoSpaceBetweenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); this.NoSpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); this.NoSpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); @@ -544,7 +546,7 @@ namespace ts.formatting { this.SpaceAfterComma, this.NoSpaceAfterComma, this.SpaceAfterAnonymousFunctionKeyword, this.NoSpaceAfterAnonymousFunctionKeyword, this.SpaceAfterKeywordInControl, this.NoSpaceAfterKeywordInControl, - this.SpaceAfterOpenParen, this.SpaceBeforeCloseParen, this.NoSpaceBetweenParens, this.NoSpaceAfterOpenParen, this.NoSpaceBeforeCloseParen, + this.SpaceAfterOpenParen, this.SpaceBeforeCloseParen, this.SpaceBetweenOpenParens, this.NoSpaceBetweenParens, this.NoSpaceAfterOpenParen, this.NoSpaceBeforeCloseParen, this.SpaceAfterOpenBracket, this.SpaceBeforeCloseBracket, this.NoSpaceBetweenBrackets, this.NoSpaceAfterOpenBracket, this.NoSpaceBeforeCloseBracket, this.SpaceAfterOpenBrace, this.SpaceBeforeCloseBrace, this.NoSpaceBetweenEmptyBraceBrackets, this.NoSpaceAfterOpenBrace, this.NoSpaceBeforeCloseBrace, this.SpaceAfterTemplateHeadAndMiddle, this.SpaceBeforeTemplateMiddleAndTail, this.NoSpaceAfterTemplateHeadAndMiddle, this.NoSpaceBeforeTemplateMiddleAndTail, diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 3c4adcb68df..f60349e4ea5 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -76,6 +76,28 @@ namespace ts.GoToDefinition { declaration => createDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName)); } + // If the node is the name of a BindingElement within an ObjectBindingPattern instead of just returning the + // declaration the symbol (which is itself), we should try to get to the original type of the ObjectBindingPattern + // and return the property declaration for the referenced property. + // For example: + // import('./foo').then(({ b/*goto*/ar }) => undefined); => should get use to the declaration in file "./foo" + // + // function bar(onfulfilled: (value: T) => void) { //....} + // interface Test { + // pr/*destination*/op1: number + // } + // bar(({pr/*goto*/op1})=>{}); + if (isPropertyName(node) && isBindingElement(node.parent) && isObjectBindingPattern(node.parent.parent) && + (node === (node.parent.propertyName || node.parent.name))) { + const type = typeChecker.getTypeAtLocation(node.parent.parent); + if (type) { + const propSymbols = getPropertySymbolsFromType(type, node); + if (propSymbols) { + return flatMap(propSymbols, propSymbol => getDefinitionFromSymbol(typeChecker, propSymbol, node)); + } + } + } + // If the current location we want to find its definition is in an object literal, try to get the contextual type for the // object literal, lookup the property symbol in the contextual type, and use this for goto-definition. // For example diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 4de7bb3191d..5e7b7d424f8 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -35,7 +35,7 @@ namespace ts.JsTyping { "crypto", "stream", "util", "assert", "tty", "domain", "constants", "process", "v8", "timers", "console"]; - const nodeCoreModules = arrayToMap(nodeCoreModuleList, x => x); + const nodeCoreModules = arrayToSet(nodeCoreModuleList); /** * A map of loose file names to library names that we are confident require typings diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 432df8c53d0..04b12f16563 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -35,7 +35,7 @@ namespace ts { export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] { return flatMapIter(refactors.values(), refactor => - context.cancellationToken && context.cancellationToken.isCancellationRequested() ? [] : refactor.getAvailableActions(context)); + context.cancellationToken && context.cancellationToken.isCancellationRequested() ? undefined : refactor.getAvailableActions(context)); } export function getEditsForRefactor(context: RefactorContext, refactorName: string, actionName: string): RefactorEditInfo | undefined { diff --git a/src/services/refactors/convertFunctionToEs6Class.ts b/src/services/refactors/convertFunctionToEs6Class.ts index 45adfb4b039..bf0e4e22658 100644 --- a/src/services/refactors/convertFunctionToEs6Class.ts +++ b/src/services/refactors/convertFunctionToEs6Class.ts @@ -1,6 +1,6 @@ /* @internal */ -namespace ts.refactor { +namespace ts.refactor.convertFunctionToES6Class { const actionName = "convert"; const convertFunctionToES6Class: Refactor = { diff --git a/src/services/refactors/extractMethod.ts b/src/services/refactors/extractMethod.ts new file mode 100644 index 00000000000..f0f85ee6e81 --- /dev/null +++ b/src/services/refactors/extractMethod.ts @@ -0,0 +1,1136 @@ +/// +/// + +/* @internal */ +namespace ts.refactor.extractMethod { + const extractMethod: Refactor = { + name: "Extract Method", + description: Diagnostics.Extract_function.message, + getAvailableActions, + getEditsForAction, + }; + + registerRefactor(extractMethod); + + /** Compute the associated code actions */ + function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { + const rangeToExtract = getRangeToExtract(context.file, { start: context.startPosition, length: context.endPosition - context.startPosition }); + + const targetRange: TargetRange = rangeToExtract.targetRange; + if (targetRange === undefined) { + return undefined; + } + + const extractions = getPossibleExtractions(targetRange, context); + if (extractions === undefined) { + // No extractions possible + return undefined; + } + + const actions: RefactorActionInfo[] = []; + const usedNames: Map = createMap(); + + let i = 0; + for (const extr of extractions) { + // Skip these since we don't have a way to report errors yet + if (extr.errors && extr.errors.length) { + continue; + } + + // Don't issue refactorings with duplicated names. + // Scopes come back in "innermost first" order, so extractions will + // preferentially go into nearer scopes + const description = formatStringFromArgs(Diagnostics.Extract_function_into_0.message, [extr.scopeDescription]); + if (!usedNames.has(description)) { + usedNames.set(description, true); + actions.push({ + description, + name: `scope_${i}` + }); + } + // *do* increment i anyway because we'll look for the i-th scope + // later when actually doing the refactoring if the user requests it + i++; + } + + if (actions.length === 0) { + return undefined; + } + + return [{ + name: extractMethod.name, + description: extractMethod.description, + inlineable: true, + actions + }]; + } + + function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { + const length = context.endPosition === undefined ? 0 : context.endPosition - context.startPosition; + const rangeToExtract = getRangeToExtract(context.file, { start: context.startPosition, length }); + const targetRange: TargetRange = rangeToExtract.targetRange; + + const parsedIndexMatch = /^scope_(\d+)$/.exec(actionName); + Debug.assert(!!parsedIndexMatch, "Scope name should have matched the regexp"); + const index = +parsedIndexMatch[1]; + Debug.assert(isFinite(index), "Expected to parse a finite number from the scope index"); + + const extractions = getPossibleExtractions(targetRange, context, index); + // Scope is no longer valid from when the user issued the refactor (??) + Debug.assert(extractions !== undefined, "The extraction went missing? How?"); + return ({ edits: extractions[0].changes }); + } + + // Move these into diagnostic messages if they become user-facing + namespace Messages { + function createMessage(message: string): DiagnosticMessage { + return { message, code: 0, category: DiagnosticCategory.Message, key: message }; + } + + export const CannotExtractFunction: DiagnosticMessage = createMessage("Cannot extract function."); + export const StatementOrExpressionExpected: DiagnosticMessage = createMessage("Statement or expression expected."); + export const CannotExtractRangeContainingConditionalBreakOrContinueStatements: DiagnosticMessage = createMessage("Cannot extract range containing conditional break or continue statements."); + export const CannotExtractRangeContainingConditionalReturnStatement: DiagnosticMessage = createMessage("Cannot extract range containing conditional return statement."); + export const CannotExtractRangeContainingLabeledBreakOrContinueStatementWithTargetOutsideOfTheRange: DiagnosticMessage = createMessage("Cannot extract range containing labeled break or continue with target outside of the range."); + export const CannotExtractRangeThatContainsWritesToReferencesLocatedOutsideOfTheTargetRangeInGenerators: DiagnosticMessage = createMessage("Cannot extract range containing writes to references located outside of the target range in generators."); + export const TypeWillNotBeVisibleInTheNewScope = createMessage("Type will not visible in the new scope."); + export const FunctionWillNotBeVisibleInTheNewScope = createMessage("Function will not visible in the new scope."); + export const InsufficientSelection = createMessage("Select more than a single identifier."); + export const CannotExtractExportedEntity = createMessage("Cannot extract exported declaration"); + export const CannotCombineWritesAndReturns = createMessage("Cannot combine writes and returns"); + export const CannotExtractReadonlyPropertyInitializerOutsideConstructor = createMessage("Cannot move initialization of read-only class property outside of the constructor"); + export const CannotExtractAmbientBlock = createMessage("Cannot extract code from ambient contexts"); + } + + export enum RangeFacts { + None = 0, + HasReturn = 1 << 0, + IsGenerator = 1 << 1, + IsAsyncFunction = 1 << 2, + UsesThis = 1 << 3, + /** + * The range is in a function which needs the 'static' modifier in a class + */ + InStaticRegion = 1 << 4 + } + + /** + * Represents an expression or a list of statements that should be extracted with some extra information + */ + export interface TargetRange { + readonly range: Expression | Statement[]; + readonly facts: RangeFacts; + /** + * A list of symbols that are declared in the selected range which are visible in the containing lexical scope + * Used to ensure we don't turn something used outside the range free (or worse, resolve to a different entity). + */ + readonly declarations: Symbol[]; + } + + /** + * Result of 'getRangeToExtract' operation: contains either a range or a list of errors + */ + export type RangeToExtract = { + readonly targetRange?: never; + readonly errors: ReadonlyArray; + } | { + readonly targetRange: TargetRange; + readonly errors?: never; + }; + + /* + * Scopes that can store newly extracted method + */ + export type Scope = FunctionLikeDeclaration | SourceFile | ModuleBlock | ClassLikeDeclaration; + + /** + * Result of 'extractRange' operation for a specific scope. + * Stores either a list of changes that should be applied to extract a range or a list of errors + */ + export interface ExtractResultForScope { + readonly scope: Scope; + readonly scopeDescription: string; + readonly changes?: FileTextChanges[]; + readonly errors?: Diagnostic[]; + } + + /** + * getRangeToExtract takes a span inside a text file and returns either an expression or an array + * of statements representing the minimum set of nodes needed to extract the entire span. This + * process may fail, in which case a set of errors is returned instead (these are currently + * not shown to the user, but can be used by us diagnostically) + */ + export function getRangeToExtract(sourceFile: SourceFile, span: TextSpan): RangeToExtract { + const length = span.length || 0; + // Walk up starting from the the start position until we find a non-SourceFile node that subsumes the selected span. + // This may fail (e.g. you select two statements in the root of a source file) + let start = getParentNodeInSpan(getTokenAtPosition(sourceFile, span.start, /*includeJsDocComment*/ false), sourceFile, span); + // Do the same for the ending position + let end = getParentNodeInSpan(findTokenOnLeftOfPosition(sourceFile, textSpanEnd(span)), sourceFile, span); + + const declarations: Symbol[] = []; + + // We'll modify these flags as we walk the tree to collect data + // about what things need to be done as part of the extraction. + let rangeFacts = RangeFacts.None; + + if (!start || !end) { + // cannot find either start or end node + return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.CannotExtractFunction)] }; + } + + if (start.parent !== end.parent) { + // handle cases like 1 + [2 + 3] + 4 + // user selection is marked with []. + // in this case 2 + 3 does not belong to the same tree node + // instead the shape of the tree looks like this: + // + + // / \ + // + 4 + // / \ + // + 3 + // / \ + // 1 2 + // in this case there is no such one node that covers ends of selection and is located inside the selection + // to handle this we check if both start and end of the selection belong to some binary operation + // and start node is parented by the parent of the end node + // if this is the case - expand the selection to the entire parent of end node (in this case it will be [1 + 2 + 3] + 4) + const startParent = skipParentheses(start.parent); + const endParent = skipParentheses(end.parent); + if (isBinaryExpression(startParent) && isBinaryExpression(endParent) && isNodeDescendantOf(startParent, endParent)) { + start = end = endParent; + } + else { + // start and end nodes belong to different subtrees + return createErrorResult(sourceFile, span.start, length, Messages.CannotExtractFunction); + } + } + if (start !== end) { + // start and end should be statements and parent should be either block or a source file + if (!isBlockLike(start.parent)) { + return createErrorResult(sourceFile, span.start, length, Messages.CannotExtractFunction); + } + const statements: Statement[] = []; + for (const statement of (start.parent).statements) { + if (statement === start || statements.length) { + const errors = checkNode(statement); + if (errors) { + return { errors }; + } + statements.push(statement); + } + if (statement === end) { + break; + } + } + return { targetRange: { range: statements, facts: rangeFacts, declarations } }; + } + else { + // We have a single node (start) + const errors = checkRootNode(start) || checkNode(start); + if (errors) { + return { errors }; + } + + // If our selection is the expression in an ExpressionStatement, expand + // the selection to include the enclosing Statement (this stops us + // from trying to care about the return value of the extracted function + // and eliminates double semicolon insertion in certain scenarios) + const range = isStatement(start) + ? [start] + : start.parent && start.parent.kind === SyntaxKind.ExpressionStatement + ? [start.parent as Statement] + : start as Expression; + + return { targetRange: { range, facts: rangeFacts, declarations } }; + } + + function createErrorResult(sourceFile: SourceFile, start: number, length: number, message: DiagnosticMessage): RangeToExtract { + return { errors: [createFileDiagnostic(sourceFile, start, length, message)] }; + } + + function checkRootNode(node: Node): Diagnostic[] | undefined { + if (isIdentifier(node)) { + return [createDiagnosticForNode(node, Messages.InsufficientSelection)]; + } + return undefined; + } + + function checkForStaticContext(nodeToCheck: Node, containingClass: Node) { + let current: Node = nodeToCheck; + while (current !== containingClass) { + if (current.kind === SyntaxKind.PropertyDeclaration) { + if (hasModifier(current, ModifierFlags.Static)) { + rangeFacts |= RangeFacts.InStaticRegion; + } + break; + } + else if (current.kind === SyntaxKind.Parameter) { + const ctorOrMethod = getContainingFunction(current); + if (ctorOrMethod.kind === SyntaxKind.Constructor) { + rangeFacts |= RangeFacts.InStaticRegion; + } + break; + } + else if (current.kind === SyntaxKind.MethodDeclaration) { + if (hasModifier(current, ModifierFlags.Static)) { + rangeFacts |= RangeFacts.InStaticRegion; + } + } + current = current.parent; + } + } + + // Verifies whether we can actually extract this node or not. + function checkNode(nodeToCheck: Node): Diagnostic[] | undefined { + const enum PermittedJumps { + None = 0, + Break = 1 << 0, + Continue = 1 << 1, + Return = 1 << 2 + } + if (!isStatement(nodeToCheck) && !(isExpression(nodeToCheck) && isExtractableExpression(nodeToCheck))) { + return [createDiagnosticForNode(nodeToCheck, Messages.StatementOrExpressionExpected)]; + } + + if (isInAmbientContext(nodeToCheck)) { + return [createDiagnosticForNode(nodeToCheck, Messages.CannotExtractAmbientBlock)]; + } + + // If we're in a class, see whether we're in a static region (static property initializer, static method, class constructor parameter default) + const containingClass: Node = getContainingClass(nodeToCheck); + if (containingClass) { + checkForStaticContext(nodeToCheck, containingClass); + } + + let errors: Diagnostic[]; + let permittedJumps = PermittedJumps.Return; + let seenLabels: Array<__String>; + + visit(nodeToCheck); + + return errors; + + function visit(node: Node) { + if (errors) { + // already found an error - can stop now + return true; + } + + if (isDeclaration(node)) { + const declaringNode = (node.kind === SyntaxKind.VariableDeclaration) ? node.parent.parent : node; + if (hasModifier(declaringNode, ModifierFlags.Export)) { + (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.CannotExtractExportedEntity)); + return true; + } + declarations.push(node.symbol); + } + + // Some things can't be extracted in certain situations + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.CannotExtractFunction)); + return true; + case SyntaxKind.SuperKeyword: + // For a super *constructor call*, we have to be extracting the entire class, + // but a super *method call* simply implies a 'this' reference + if (node.parent.kind === SyntaxKind.CallExpression) { + // Super constructor call + const containingClass = getContainingClass(node); + if (containingClass.pos < span.start || containingClass.end >= (span.start + span.length)) { + (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.CannotExtractFunction)); + return true; + } + } + else { + rangeFacts |= RangeFacts.UsesThis; + } + break; + } + + if (!node || isFunctionLike(node) || isClassLike(node)) { + switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ClassDeclaration: + if (node.parent.kind === SyntaxKind.SourceFile && (node.parent as ts.SourceFile).externalModuleIndicator === undefined) { + // You cannot extract global declarations + (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.FunctionWillNotBeVisibleInTheNewScope)); + } + break; + } + + // do not dive into functions or classes + return false; + } + const savedPermittedJumps = permittedJumps; + if (node.parent) { + switch (node.parent.kind) { + case SyntaxKind.IfStatement: + if ((node.parent).thenStatement === node || (node.parent).elseStatement === node) { + // forbid all jumps inside thenStatement or elseStatement + permittedJumps = PermittedJumps.None; + } + break; + case SyntaxKind.TryStatement: + if ((node.parent).tryBlock === node) { + // forbid all jumps inside try blocks + permittedJumps = PermittedJumps.None; + } + else if ((node.parent).finallyBlock === node) { + // allow unconditional returns from finally blocks + permittedJumps = PermittedJumps.Return; + } + break; + case SyntaxKind.CatchClause: + if ((node.parent).block === node) { + // forbid all jumps inside the block of catch clause + permittedJumps = PermittedJumps.None; + } + break; + case SyntaxKind.CaseClause: + if ((node).expression !== node) { + // allow unlabeled break inside case clauses + permittedJumps |= PermittedJumps.Break; + } + break; + default: + if (isIterationStatement(node.parent, /*lookInLabeledStatements*/ false)) { + if ((node.parent).statement === node) { + // allow unlabeled break/continue inside loops + permittedJumps |= PermittedJumps.Break | PermittedJumps.Continue; + } + } + break; + } + } + + switch (node.kind) { + case SyntaxKind.ThisType: + case SyntaxKind.ThisKeyword: + rangeFacts |= RangeFacts.UsesThis; + break; + case SyntaxKind.LabeledStatement: + { + const label = (node).label; + (seenLabels || (seenLabels = [])).push(label.escapedText); + forEachChild(node, visit); + seenLabels.pop(); + break; + } + case SyntaxKind.BreakStatement: + case SyntaxKind.ContinueStatement: + { + const label = (node).label; + if (label) { + if (!contains(seenLabels, label.escapedText)) { + // attempts to jump to label that is not in range to be extracted + (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.CannotExtractRangeContainingLabeledBreakOrContinueStatementWithTargetOutsideOfTheRange)); + } + } + else { + if (!(permittedJumps & (SyntaxKind.BreakStatement ? PermittedJumps.Break : PermittedJumps.Continue))) { + // attempt to break or continue in a forbidden context + (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements)); + } + } + break; + } + case SyntaxKind.AwaitExpression: + rangeFacts |= RangeFacts.IsAsyncFunction; + break; + case SyntaxKind.YieldExpression: + rangeFacts |= RangeFacts.IsGenerator; + break; + case SyntaxKind.ReturnStatement: + if (permittedJumps & PermittedJumps.Return) { + rangeFacts |= RangeFacts.HasReturn; + } + else { + (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.CannotExtractRangeContainingConditionalReturnStatement)); + } + break; + default: + forEachChild(node, visit); + break; + } + + permittedJumps = savedPermittedJumps; + } + } + } + + function isValidExtractionTarget(node: Node): node is Scope { + // Note that we don't use isFunctionLike because we don't want to put the extracted closure *inside* a method + return (node.kind === SyntaxKind.FunctionDeclaration) || isSourceFile(node) || isModuleBlock(node) || isClassLike(node); + } + + /** + * Computes possible places we could extract the function into. For example, + * you may be able to extract into a class method *or* local closure *or* namespace function, + * depending on what's in the extracted body. + */ + export function collectEnclosingScopes(range: TargetRange): Scope[] | undefined { + let current: Node = isReadonlyArray(range.range) ? firstOrUndefined(range.range) : range.range; + if (range.facts & RangeFacts.UsesThis) { + // if range uses this as keyword or as type inside the class then it can only be extracted to a method of the containing class + const containingClass = getContainingClass(current); + if (containingClass) { + return [containingClass]; + } + } + + const start = current; + + let scopes: Scope[] | undefined = undefined; + while (current) { + // We want to find the nearest parent where we can place an "equivalent" sibling to the node we're extracting out of. + // Walk up to the closest parent of a place where we can logically put a sibling: + // * Function declaration + // * Class declaration or expression + // * Module/namespace or source file + if (current !== start && isValidExtractionTarget(current)) { + (scopes = scopes || []).push(current); + } + + // A function parameter's initializer is actually in the outer scope, not the function declaration + if (current && current.parent && current.parent.kind === SyntaxKind.Parameter) { + // Skip all the way to the outer scope of the function that declared this parameter + current = findAncestor(current, parent => isFunctionLike(parent)).parent; + } + else { + current = current.parent; + } + + } + return scopes; + } + + /** + * Given a piece of text to extract ('targetRange'), computes a list of possible extractions. + * Each returned ExtractResultForScope corresponds to a possible target scope and is either a set of changes + * or an error explaining why we can't extract into that scope. + */ + export function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number = undefined): ReadonlyArray | undefined { + const { file: sourceFile } = context; + + if (targetRange === undefined) { + return undefined; + } + + const scopes = collectEnclosingScopes(targetRange); + if (scopes === undefined) { + return undefined; + } + + const enclosingTextRange = getEnclosingTextRange(targetRange, sourceFile); + const { target, usagesPerScope, errorsPerScope } = collectReadsAndWrites( + targetRange, + scopes, + enclosingTextRange, + sourceFile, + context.program.getTypeChecker()); + + context.cancellationToken.throwIfCancellationRequested(); + + if (requestedChangesIndex !== undefined) { + if (errorsPerScope[requestedChangesIndex].length) { + return undefined; + } + return [extractFunctionInScope(target, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange, context)]; + } + else { + return scopes.map((scope, i) => { + const errors = errorsPerScope[i]; + if (errors.length) { + return { + scope, + scopeDescription: getDescriptionForScope(scope), + errors + }; + } + return { scope, scopeDescription: getDescriptionForScope(scope) }; + }); + } + } + + function getDescriptionForScope(scope: Scope) { + if (isFunctionLike(scope)) { + switch (scope.kind) { + case SyntaxKind.Constructor: + return "constructor"; + case SyntaxKind.FunctionExpression: + return scope.name + ? `function expression ${scope.name.getText()}` + : "anonymous function expression"; + case SyntaxKind.FunctionDeclaration: + return `function ${scope.name.getText()}`; + case SyntaxKind.ArrowFunction: + return "arrow function"; + case SyntaxKind.MethodDeclaration: + return `method ${scope.name.getText()}`; + case SyntaxKind.GetAccessor: + return `get ${scope.name.getText()}`; + case SyntaxKind.SetAccessor: + return `set ${scope.name.getText()}`; + } + } + else if (isModuleBlock(scope)) { + return `namespace ${scope.parent.name.getText()}`; + } + else if (isClassLike(scope)) { + return scope.kind === SyntaxKind.ClassDeclaration + ? `class ${scope.name.text}` + : scope.name.text + ? `class expression ${scope.name.text}` + : "anonymous class expression"; + } + else if (isSourceFile(scope)) { + return `file '${scope.fileName}'`; + } + else { + return "unknown"; + } + } + + function getUniqueName(isNameOkay: (name: string) => boolean) { + let functionNameText = "newFunction"; + if (isNameOkay(functionNameText)) { + return functionNameText; + } + let i = 1; + while (!isNameOkay(functionNameText = `newFunction_${i}`)) { + i++; + } + return functionNameText; + } + + export function extractFunctionInScope( + node: Statement | Expression | Block, + scope: Scope, + { usages: usagesInScope, substitutions }: ScopeUsages, + range: TargetRange, + context: RefactorContext): ExtractResultForScope { + + const checker = context.program.getTypeChecker(); + + // Make a unique name for the extracted function + const file = scope.getSourceFile(); + const functionNameText: string = getUniqueName(n => !file.identifiers.has(n)); + const isJS = isInJavaScriptFile(scope); + + const functionName = createIdentifier(functionNameText as string); + const functionReference = createIdentifier(functionNameText as string); + + let returnType: TypeNode = undefined; + const parameters: ParameterDeclaration[] = []; + const callArguments: Identifier[] = []; + let writes: UsageEntry[]; + usagesInScope.forEach((usage, name) => { + let typeNode: TypeNode = undefined; + if (!isJS) { + let type = checker.getTypeOfSymbolAtLocation(usage.symbol, usage.node); + // Widen the type so we don't emit nonsense annotations like "function fn(x: 3) {" + type = checker.getBaseTypeOfLiteralType(type); + typeNode = checker.typeToTypeNode(type, node, NodeBuilderFlags.NoTruncation); + } + + const paramDecl = createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + /*name*/ name, + /*questionToken*/ undefined, + typeNode + ); + parameters.push(paramDecl); + if (usage.usage === Usage.Write) { + (writes || (writes = [])).push(usage); + } + callArguments.push(createIdentifier(name)); + }); + + // Provide explicit return types for contexutally-typed functions + // to avoid problems when there are literal types present + if (isExpression(node) && !isJS) { + const contextualType = checker.getContextualType(node); + returnType = checker.typeToTypeNode(contextualType); + } + + const { body, returnValueProperty } = transformFunctionBody(node); + let newFunction: MethodDeclaration | FunctionDeclaration; + + if (isClassLike(scope)) { + // always create private method in TypeScript files + const modifiers: Modifier[] = isJS ? [] : [createToken(SyntaxKind.PrivateKeyword)]; + if (range.facts & RangeFacts.InStaticRegion) { + modifiers.push(createToken(SyntaxKind.StaticKeyword)); + } + if (range.facts & RangeFacts.IsAsyncFunction) { + modifiers.push(createToken(SyntaxKind.AsyncKeyword)); + } + newFunction = createMethod( + /*decorators*/ undefined, + modifiers, + range.facts & RangeFacts.IsGenerator ? createToken(SyntaxKind.AsteriskToken) : undefined, + functionName, + /*questionToken*/ undefined, + /*typeParameters*/[], + parameters, + returnType, + body + ); + } + else { + newFunction = createFunctionDeclaration( + /*decorators*/ undefined, + range.facts & RangeFacts.IsAsyncFunction ? [createToken(SyntaxKind.AsyncKeyword)] : undefined, + range.facts & RangeFacts.IsGenerator ? createToken(SyntaxKind.AsteriskToken) : undefined, + functionName, + /*typeParameters*/[], + parameters, + returnType, + body + ); + } + + const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); + // insert function at the end of the scope + changeTracker.insertNodeBefore(context.file, scope.getLastToken(), newFunction, { prefix: context.newLineCharacter, suffix: context.newLineCharacter }); + + const newNodes: Node[] = []; + // replace range with function call + let call: Expression = createCall( + isClassLike(scope) ? createPropertyAccess(range.facts & RangeFacts.InStaticRegion ? createIdentifier(scope.name.getText()) : createThis(), functionReference) : functionReference, + /*typeArguments*/ undefined, + callArguments); + if (range.facts & RangeFacts.IsGenerator) { + call = createYield(createToken(SyntaxKind.AsteriskToken), call); + } + if (range.facts & RangeFacts.IsAsyncFunction) { + call = createAwait(call); + } + + if (writes) { + if (returnValueProperty) { + // has both writes and return, need to create variable declaration to hold return value; + newNodes.push(createVariableStatement( + /*modifiers*/ undefined, + [createVariableDeclaration(returnValueProperty, createKeywordTypeNode(SyntaxKind.AnyKeyword))] + )); + } + + const assignments = getPropertyAssignmentsForWrites(writes); + if (returnValueProperty) { + assignments.unshift(createShorthandPropertyAssignment(returnValueProperty)); + } + + // propagate writes back + if (assignments.length === 1) { + if (returnValueProperty) { + newNodes.push(createReturn(createIdentifier(returnValueProperty))); + } + else { + newNodes.push(createStatement(createBinary(assignments[0].name, SyntaxKind.EqualsToken, call))); + } + } + else { + // emit e.g. + // { a, b, __return } = newFunction(a, b); + // return __return; + newNodes.push(createStatement(createBinary(createObjectLiteral(assignments), SyntaxKind.EqualsToken, call))); + if (returnValueProperty) { + newNodes.push(createReturn(createIdentifier(returnValueProperty))); + } + } + } + else { + if (range.facts & RangeFacts.HasReturn) { + newNodes.push(createReturn(call)); + } + else if (isReadonlyArray(range.range)) { + newNodes.push(createStatement(call)); + } + else { + newNodes.push(call); + } + } + + if (isReadonlyArray(range.range)) { + changeTracker.replaceNodesWithNodes(context.file, range.range, newNodes, { + nodeSeparator: context.newLineCharacter, + suffix: context.newLineCharacter // insert newline only when replacing statements + }); + } + else { + changeTracker.replaceNodeWithNodes(context.file, range.range, newNodes, { nodeSeparator: context.newLineCharacter }); + } + + return { + scope, + scopeDescription: getDescriptionForScope(scope), + changes: changeTracker.getChanges() + }; + + function getPropertyAssignmentsForWrites(writes: UsageEntry[]) { + return writes.map(w => createShorthandPropertyAssignment(w.symbol.name)); + } + + function generateReturnValueProperty() { + return "__return"; + } + + function transformFunctionBody(body: Node) { + if (isBlock(body) && !writes && substitutions.size === 0) { + // already block, no writes to propagate back, no substitutions - can use node as is + return { body: createBlock(body.statements, /*multLine*/ true), returnValueProperty: undefined }; + } + let returnValueProperty: string; + const statements = createNodeArray(isBlock(body) ? body.statements.slice(0) : [isStatement(body) ? body : createReturn(body)]); + // rewrite body if either there are writes that should be propagated back via return statements or there are substitutions + if (writes || substitutions.size) { + const rewrittenStatements = visitNodes(statements, visitor).slice(); + if (writes && !(range.facts & RangeFacts.HasReturn) && isStatement(body)) { + // add return at the end to propagate writes back in case if control flow falls out of the function body + // it is ok to know that range has at least one return since it we only allow unconditional returns + const assignments = getPropertyAssignmentsForWrites(writes); + if (assignments.length === 1) { + rewrittenStatements.push(createReturn(assignments[0].name)); + } + else { + rewrittenStatements.push(createReturn(createObjectLiteral(assignments))); + } + } + return { body: createBlock(rewrittenStatements, /*multiLine*/ true), returnValueProperty }; + } + else { + return { body: createBlock(statements, /*multiLine*/ true), returnValueProperty: undefined }; + } + + function visitor(node: Node): VisitResult { + if (node.kind === SyntaxKind.ReturnStatement && writes) { + const assignments: ObjectLiteralElementLike[] = getPropertyAssignmentsForWrites(writes); + if ((node).expression) { + if (!returnValueProperty) { + returnValueProperty = generateReturnValueProperty(); + } + assignments.unshift(createPropertyAssignment(returnValueProperty, visitNode((node).expression, visitor))); + } + if (assignments.length === 1) { + return createReturn(assignments[0].name as Expression); + } + else { + return createReturn(createObjectLiteral(assignments)); + } + } + else { + const substitution = substitutions.get(getNodeId(node).toString()); + return substitution || visitEachChild(node, visitor, nullTransformationContext); + } + } + } + } + + function isModuleBlock(n: Node): n is ModuleBlock { + return n.kind === SyntaxKind.ModuleBlock; + } + + function isReadonlyArray(v: any): v is ReadonlyArray { + return isArray(v); + } + + /** + * Produces a range that spans the entirety of nodes, given a selection + * that might start/end in the middle of nodes. + * + * For example, when the user makes a selection like this + * v---v + * var someThing = foo + bar; + * this returns ^-------^ + */ + function getEnclosingTextRange(targetRange: TargetRange, sourceFile: SourceFile): TextRange { + return isReadonlyArray(targetRange.range) + ? { pos: targetRange.range[0].getStart(sourceFile), end: targetRange.range[targetRange.range.length - 1].getEnd() } + : targetRange.range; + } + + const enum Usage { + // value should be passed to extracted method + Read = 1, + // value should be passed to extracted method and propagated back + Write = 2 + } + + interface UsageEntry { + readonly usage: Usage; + readonly symbol: Symbol; + readonly node: Node; + } + + interface ScopeUsages { + usages: Map; + substitutions: Map; + } + + function collectReadsAndWrites( + targetRange: TargetRange, + scopes: Scope[], + enclosingTextRange: TextRange, + sourceFile: SourceFile, + checker: TypeChecker) { + + const usagesPerScope: ScopeUsages[] = []; + const substitutionsPerScope: Map[] = []; + const errorsPerScope: Diagnostic[][] = []; + const visibleDeclarationsInExtractedRange: Symbol[] = []; + + // initialize results + for (const _ of scopes) { + usagesPerScope.push({ usages: createMap(), substitutions: createMap() }); + substitutionsPerScope.push(createMap()); + errorsPerScope.push([]); + } + const seenUsages = createMap(); + const target = isReadonlyArray(targetRange.range) ? createBlock(targetRange.range) : targetRange.range; + const containingLexicalScopeOfExtraction = isBlockScope(scopes[0], scopes[0].parent) ? scopes[0] : getEnclosingBlockScopeContainer(scopes[0]); + + collectUsages(target); + + for (let i = 0; i < scopes.length; i++) { + let hasWrite = false; + let readonlyClassPropertyWrite: Declaration | undefined = undefined; + usagesPerScope[i].usages.forEach(value => { + if (value.usage === Usage.Write) { + hasWrite = true; + if (value.symbol.flags & SymbolFlags.ClassMember && + value.symbol.valueDeclaration && + hasModifier(value.symbol.valueDeclaration, ModifierFlags.Readonly)) { + readonlyClassPropertyWrite = value.symbol.valueDeclaration; + } + } + }); + + if (hasWrite && !isReadonlyArray(targetRange.range) && isExpression(targetRange.range)) { + errorsPerScope[i].push(createDiagnosticForNode(targetRange.range, Messages.CannotCombineWritesAndReturns)); + } + else if (readonlyClassPropertyWrite && i > 0) { + errorsPerScope[i].push(createDiagnosticForNode(readonlyClassPropertyWrite, Messages.CannotCombineWritesAndReturns)); + } + } + + // If there are any declarations in the extracted block that are used in the same enclosing + // lexical scope, we can't move the extraction "up" as those declarations will become unreachable + if (visibleDeclarationsInExtractedRange.length) { + forEachChild(containingLexicalScopeOfExtraction, checkForUsedDeclarations); + } + + return { target, usagesPerScope, errorsPerScope }; + + function collectUsages(node: Node, valueUsage = Usage.Read) { + if (isDeclaration(node) && node.symbol) { + visibleDeclarationsInExtractedRange.push(node.symbol); + } + + if (isAssignmentExpression(node)) { + // use 'write' as default usage for values + collectUsages(node.left, Usage.Write); + collectUsages(node.right); + } + else if (isUnaryExpressionWithWrite(node)) { + collectUsages(node.operand, Usage.Write); + } + else if (isPropertyAccessExpression(node) || isElementAccessExpression(node)) { + // use 'write' as default usage for values + forEachChild(node, collectUsages); + } + else if (isIdentifier(node)) { + if (!node.parent) { + return; + } + if (isQualifiedName(node.parent) && node !== node.parent.left) { + return; + } + if (isPropertyAccessExpression(node.parent) && node !== node.parent.expression) { + return; + } + recordUsage(node, valueUsage, /*isTypeNode*/ isPartOfTypeNode(node)); + } + else { + forEachChild(node, collectUsages); + } + } + + function recordUsage(n: Identifier, usage: Usage, isTypeNode: boolean) { + const symbolId = recordUsagebySymbol(n, usage, isTypeNode); + if (symbolId) { + for (let i = 0; i < scopes.length; i++) { + // push substitution from map to map to simplify rewriting + const substitition = substitutionsPerScope[i].get(symbolId); + if (substitition) { + usagesPerScope[i].substitutions.set(getNodeId(n).toString(), substitition); + } + } + } + } + + function recordUsagebySymbol(identifier: Identifier, usage: Usage, isTypeName: boolean) { + const symbol = checker.getSymbolAtLocation(identifier); + if (!symbol) { + // cannot find symbol - do nothing + return undefined; + } + const symbolId = getSymbolId(symbol).toString(); + const lastUsage = seenUsages.get(symbolId); + // there are two kinds of value usages + // - reads - if range contains a read from the value located outside of the range then value should be passed as a parameter + // - writes - if range contains a write to a value located outside the range the value should be passed as a parameter and + // returned as a return value + // 'write' case is a superset of 'read' so if we already have processed 'write' of some symbol there is not need to handle 'read' + // since all information is already recorded + if (lastUsage && lastUsage >= usage) { + return symbolId; + } + + seenUsages.set(symbolId, usage); + if (lastUsage) { + // if we get here this means that we are trying to handle 'write' and 'read' was already processed + // walk scopes and update existing records. + for (const perScope of usagesPerScope) { + const prevEntry = perScope.usages.get(identifier.text as string); + if (prevEntry) { + perScope.usages.set(identifier.text as string, { usage, symbol, node: identifier }); + } + } + return symbolId; + } + // find first declaration in this file + const declInFile = find(symbol.getDeclarations(), d => d.getSourceFile() === sourceFile); + if (!declInFile) { + return undefined; + } + if (rangeContainsRange(enclosingTextRange, declInFile)) { + // declaration is located in range to be extracted - do nothing + return undefined; + } + if (targetRange.facts & RangeFacts.IsGenerator && usage === Usage.Write) { + // this is write to a reference located outside of the target scope and range is extracted into generator + // currently this is unsupported scenario + for (const errors of errorsPerScope) { + errors.push(createDiagnosticForNode(identifier, Messages.CannotExtractRangeThatContainsWritesToReferencesLocatedOutsideOfTheTargetRangeInGenerators)); + } + } + for (let i = 0; i < scopes.length; i++) { + const scope = scopes[i]; + const resolvedSymbol = checker.resolveName(symbol.name, scope, symbol.flags); + if (resolvedSymbol === symbol) { + continue; + } + if (!substitutionsPerScope[i].has(symbolId)) { + const substitution = tryReplaceWithQualifiedNameOrPropertyAccess(symbol.exportSymbol || symbol, scope, isTypeName); + if (substitution) { + substitutionsPerScope[i].set(symbolId, substitution); + } + else if (isTypeName) { + errorsPerScope[i].push(createDiagnosticForNode(identifier, Messages.TypeWillNotBeVisibleInTheNewScope)); + } + else { + usagesPerScope[i].usages.set(identifier.text as string, { usage, symbol, node: identifier }); + } + } + } + return symbolId; + } + + function checkForUsedDeclarations(node: Node) { + // If this node is entirely within the original extraction range, we don't need to do anything. + if (node === targetRange.range || (isReadonlyArray(targetRange.range) && targetRange.range.indexOf(node as Statement) >= 0)) { + return; + } + + // Otherwise check and recurse. + const sym = checker.getSymbolAtLocation(node); + if (sym && visibleDeclarationsInExtractedRange.some(d => d === sym)) { + for (const scope of errorsPerScope) { + scope.push(createDiagnosticForNode(node, Messages.CannotExtractExportedEntity)); + } + return true; + } + else { + forEachChild(node, checkForUsedDeclarations); + } + } + + function tryReplaceWithQualifiedNameOrPropertyAccess(symbol: Symbol, scopeDecl: Node, isTypeNode: boolean): PropertyAccessExpression | EntityName { + if (!symbol) { + return undefined; + } + if (symbol.getDeclarations().some(d => d.parent === scopeDecl)) { + return createIdentifier(symbol.name); + } + const prefix = tryReplaceWithQualifiedNameOrPropertyAccess(symbol.parent, scopeDecl, isTypeNode); + if (prefix === undefined) { + return undefined; + } + return isTypeNode ? createQualifiedName(prefix, createIdentifier(symbol.name)) : createPropertyAccess(prefix, symbol.name); + } + } + + function getParentNodeInSpan(node: Node, file: SourceFile, span: TextSpan): Node { + if (!node) return undefined; + + while (node.parent) { + if (isSourceFile(node.parent) || !spanContainsNode(span, node.parent, file)) { + return node; + } + + node = node.parent; + } + } + + function spanContainsNode(span: TextSpan, node: Node, file: SourceFile): boolean { + return textSpanContainsPosition(span, node.getStart(file)) && + node.getEnd() <= textSpanEnd(span); + } + + /** + * Computes whether or not a node represents an expression in a position where it could + * be extracted. + * The isExpression() in utilities.ts returns some false positives we need to handle, + * such as `import x from 'y'` -- the 'y' is a StringLiteral but is *not* an expression + * in the sense of something that you could extract on + */ + function isExtractableExpression(node: Node): boolean { + switch (node.parent.kind) { + case SyntaxKind.EnumMember: + return false; + } + + switch (node.kind) { + case SyntaxKind.StringLiteral: + return node.parent.kind !== SyntaxKind.ImportDeclaration && + node.parent.kind !== SyntaxKind.ImportSpecifier; + + case SyntaxKind.SpreadElement: + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.BindingElement: + return false; + + case SyntaxKind.Identifier: + return node.parent.kind !== SyntaxKind.BindingElement && + node.parent.kind !== SyntaxKind.ImportSpecifier && + node.parent.kind !== SyntaxKind.ExportSpecifier; + } + return true; + } + + function isBlockLike(node: Node): node is BlockLike { + switch (node.kind) { + case SyntaxKind.Block: + case SyntaxKind.SourceFile: + case SyntaxKind.ModuleBlock: + case SyntaxKind.CaseClause: + return true; + default: + return false; + } + } +} diff --git a/src/services/refactors/refactors.ts b/src/services/refactors/refactors.ts index 4c4ecb33416..3a33ccc83c2 100644 --- a/src/services/refactors/refactors.ts +++ b/src/services/refactors/refactors.ts @@ -1 +1,2 @@ /// +/// diff --git a/src/services/services.ts b/src/services/services.ts index 14b5ddd85c4..ba1c48b9037 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1247,7 +1247,7 @@ namespace ts { // We do not support the scenario where a host can modify a registered // file's script kind, i.e. in one project some file is treated as ".ts" // and in another as ".js" - Debug.assert(hostFileInformation.scriptKind === oldSourceFile.scriptKind, "Registered script kind (" + oldSourceFile.scriptKind + ") should match new script kind (" + hostFileInformation.scriptKind + ") for file: " + path); + Debug.assertEqual(hostFileInformation.scriptKind, oldSourceFile.scriptKind, "Registered script kind should match new script kind.", path); return documentRegistry.updateDocumentWithKey(fileName, path, newSettings, documentRegistryBucketKey, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } @@ -2073,12 +2073,17 @@ namespace ts { export function getPropertySymbolsFromContextualType(typeChecker: TypeChecker, node: ObjectLiteralElement): Symbol[] { const objectLiteral = node.parent; const contextualType = typeChecker.getContextualType(objectLiteral); - const name = unescapeLeadingUnderscores(getTextOfPropertyName(node.name)); - if (name && contextualType) { + return getPropertySymbolsFromType(contextualType, node.name); + } + + /* @internal */ + export function getPropertySymbolsFromType(type: Type, propName: PropertyName) { + const name = unescapeLeadingUnderscores(getTextOfPropertyName(propName)); + if (name && type) { const result: Symbol[] = []; - const symbol = contextualType.getProperty(name); - if (contextualType.flags & TypeFlags.Union) { - forEach((contextualType).types, t => { + const symbol = type.getProperty(name); + if (type.flags & TypeFlags.Union) { + forEach((type).types, t => { const symbol = t.getProperty(name); if (symbol) { result.push(symbol); diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index f008c829116..2976b0d28ee 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -136,7 +136,9 @@ namespace ts.SignatureHelp { const kind = invocation.typeArguments && invocation.typeArguments.pos === list.pos ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments; const argumentCount = getArgumentCount(list); - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + if (argumentIndex !== 0) { + Debug.assertLessThan(argumentIndex, argumentCount); + } const argumentsSpan = getApplicableSpanForArguments(list, sourceFile); return { kind, invocation, argumentsSpan, argumentIndex, argumentCount }; } @@ -270,7 +272,9 @@ namespace ts.SignatureHelp { ? 1 : (tagExpression.template).templateSpans.length + 1; - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + if (argumentIndex !== 0) { + Debug.assertLessThan(argumentIndex, argumentCount); + } return { kind: ArgumentListKind.TaggedTemplateArguments, invocation: tagExpression, @@ -402,7 +406,9 @@ namespace ts.SignatureHelp { }; }); - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + if (argumentIndex !== 0) { + Debug.assertLessThan(argumentIndex, argumentCount); + } const selectedItemIndex = candidates.indexOf(resolvedSignature); Debug.assert(selectedItemIndex !== -1); // If candidates is non-empty it should always include bestSignature. We check for an empty candidates before calling this function. diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 0cb3916c9c5..303bb8395ee 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -203,7 +203,7 @@ namespace ts.SymbolDisplay { // get the signature from the declaration and write it const functionDeclaration = location.parent; // Use function declaration to write the signatures only if the symbol corresponding to this declaration - const locationIsSymbolDeclaration = findDeclaration(symbol, declaration => + const locationIsSymbolDeclaration = find(symbol.declarations, declaration => declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration)); if (locationIsSymbolDeclaration) { diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index d468263227e..bddfc205db4 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -64,7 +64,7 @@ namespace ts.textChanges { */ export type ConfigurableStartEnd = ConfigurableStart & ConfigurableEnd; - export interface InsertNodeOptions { + interface InsertNodeOptions { /** * Text to be inserted before the new node */ @@ -83,16 +83,43 @@ namespace ts.textChanges { delta?: number; } - export type ChangeNodeOptions = ConfigurableStartEnd & InsertNodeOptions; + enum ChangeKind { + Remove, + ReplaceWithSingleNode, + ReplaceWithMultipleNodes + } - interface Change { + type Change = ReplaceWithSingleNode | ReplaceWithMultipleNodes | RemoveNode; + + interface BaseChange { readonly sourceFile: SourceFile; readonly range: TextRange; + } + + interface ChangeNodeOptions extends ConfigurableStartEnd, InsertNodeOptions { readonly useIndentationFromFile?: boolean; - readonly node?: Node; + } + interface ReplaceWithSingleNode extends BaseChange { + readonly kind: ChangeKind.ReplaceWithSingleNode; + readonly node: Node; readonly options?: ChangeNodeOptions; } + interface RemoveNode extends BaseChange { + readonly kind: ChangeKind.Remove; + readonly node?: never; + readonly options?: never; + } + + interface ChangeMultipleNodesOptions extends ChangeNodeOptions { + nodeSeparator: string; + } + interface ReplaceWithMultipleNodes extends BaseChange { + readonly kind: ChangeKind.ReplaceWithMultipleNodes; + readonly nodes: ReadonlyArray; + readonly options?: ChangeMultipleNodesOptions; + } + export function getSeparatorCharacter(separator: Token) { return tokenToString(separator.kind); } @@ -126,13 +153,11 @@ namespace ts.textChanges { } export function getAdjustedEndPosition(sourceFile: SourceFile, node: Node, options: ConfigurableEnd) { - if (options.useNonAdjustedEndPosition) { + if (options.useNonAdjustedEndPosition || isExpression(node)) { return node.getEnd(); } const end = node.getEnd(); const newEnd = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true); - // check if last character before newPos is linebreak - // if yes - considered all skipped trivia to be trailing trivia of the node return newEnd !== end && isLineBreak(sourceFile.text.charCodeAt(newEnd - 1)) ? newEnd : end; @@ -153,12 +178,16 @@ namespace ts.textChanges { return s; } + function getNewlineKind(context: { newLineCharacter: string }) { + return context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed; + } + export class ChangeTracker { private changes: Change[] = []; private readonly newLineCharacter: string; - public static fromCodeFixContext(context: { newLineCharacter: string, rulesProvider: formatting.RulesProvider }) { - return new ChangeTracker(context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.rulesProvider); + public static fromCodeFixContext(context: { newLineCharacter: string, rulesProvider?: formatting.RulesProvider }) { + return new ChangeTracker(getNewlineKind(context), context.rulesProvider); } constructor( @@ -168,22 +197,22 @@ namespace ts.textChanges { this.newLineCharacter = getNewLineCharacter({ newLine }); } - public deleteNode(sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd = {}) { - const startPosition = getAdjustedStartPosition(sourceFile, node, options, Position.FullStart); - const endPosition = getAdjustedEndPosition(sourceFile, node, options); - this.changes.push({ sourceFile, options, range: { pos: startPosition, end: endPosition } }); + public deleteRange(sourceFile: SourceFile, range: TextRange) { + this.changes.push({ kind: ChangeKind.Remove, sourceFile, range }); return this; } - public deleteRange(sourceFile: SourceFile, range: TextRange) { - this.changes.push({ sourceFile, range }); + public deleteNode(sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd = {}) { + const startPosition = getAdjustedStartPosition(sourceFile, node, options, Position.FullStart); + const endPosition = getAdjustedEndPosition(sourceFile, node, options); + this.changes.push({ kind: ChangeKind.Remove, sourceFile, range: { pos: startPosition, end: endPosition } }); return this; } public deleteNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd = {}) { const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.FullStart); const endPosition = getAdjustedEndPosition(sourceFile, endNode, options); - this.changes.push({ sourceFile, options, range: { pos: startPosition, end: endPosition } }); + this.changes.push({ kind: ChangeKind.Remove, sourceFile, range: { pos: startPosition, end: endPosition } }); return this; } @@ -223,33 +252,74 @@ namespace ts.textChanges { } public replaceRange(sourceFile: SourceFile, range: TextRange, newNode: Node, options: InsertNodeOptions = {}) { - this.changes.push({ sourceFile, range, options, node: newNode }); + this.changes.push({ kind: ChangeKind.ReplaceWithSingleNode, sourceFile, range, options, node: newNode }); return this; } public replaceNode(sourceFile: SourceFile, oldNode: Node, newNode: Node, options: ChangeNodeOptions = {}) { const startPosition = getAdjustedStartPosition(sourceFile, oldNode, options, Position.Start); const endPosition = getAdjustedEndPosition(sourceFile, oldNode, options); - this.changes.push({ sourceFile, options, useIndentationFromFile: true, node: newNode, range: { pos: startPosition, end: endPosition } }); - return this; + return this.replaceWithSingle(sourceFile, startPosition, endPosition, newNode, options); } public replaceNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, newNode: Node, options: ChangeNodeOptions = {}) { const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.Start); const endPosition = getAdjustedEndPosition(sourceFile, endNode, options); - this.changes.push({ sourceFile, options, useIndentationFromFile: true, node: newNode, range: { pos: startPosition, end: endPosition } }); + return this.replaceWithSingle(sourceFile, startPosition, endPosition, newNode, options); + } + + private replaceWithSingle(sourceFile: SourceFile, startPosition: number, endPosition: number, newNode: Node, options: ChangeNodeOptions): this { + this.changes.push({ + kind: ChangeKind.ReplaceWithSingleNode, + sourceFile, + options, + node: newNode, + range: { pos: startPosition, end: endPosition } + }); return this; } + private replaceWithMultiple(sourceFile: SourceFile, startPosition: number, endPosition: number, newNodes: ReadonlyArray, options: ChangeMultipleNodesOptions): this { + this.changes.push({ + kind: ChangeKind.ReplaceWithMultipleNodes, + sourceFile, + options, + nodes: newNodes, + range: { pos: startPosition, end: endPosition } + }); + return this; + } + + public replaceNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: ReadonlyArray, options: ChangeMultipleNodesOptions) { + const startPosition = getAdjustedStartPosition(sourceFile, oldNode, options, Position.Start); + const endPosition = getAdjustedEndPosition(sourceFile, oldNode, options); + return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options); + } + + public replaceNodesWithNodes(sourceFile: SourceFile, oldNodes: ReadonlyArray, newNodes: ReadonlyArray, options: ChangeMultipleNodesOptions) { + const startPosition = getAdjustedStartPosition(sourceFile, oldNodes[0], options, Position.Start); + const endPosition = getAdjustedEndPosition(sourceFile, lastOrUndefined(oldNodes), options); + return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options); + } + + public replaceRangeWithNodes(sourceFile: SourceFile, range: TextRange, newNodes: ReadonlyArray, options: ChangeMultipleNodesOptions) { + return this.replaceWithMultiple(sourceFile, range.pos, range.end, newNodes, options); + } + + public replaceNodeRangeWithNodes(sourceFile: SourceFile, startNode: Node, endNode: Node, newNodes: ReadonlyArray, options: ChangeMultipleNodesOptions) { + const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.Start); + const endPosition = getAdjustedEndPosition(sourceFile, endNode, options); + return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options); + } + public insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}) { - this.changes.push({ sourceFile, options, node: newNode, range: { pos, end: pos } }); + this.changes.push({ kind: ChangeKind.ReplaceWithSingleNode, sourceFile, options, node: newNode, range: { pos, end: pos } }); return this; } public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, options: InsertNodeOptions & ConfigurableStart = {}) { const startPosition = getAdjustedStartPosition(sourceFile, before, options, Position.Start); - this.changes.push({ sourceFile, options, useIndentationFromFile: true, node: newNode, range: { pos: startPosition, end: startPosition } }); - return this; + return this.replaceWithSingle(sourceFile, startPosition, startPosition, newNode, options); } public insertNodeAfter(sourceFile: SourceFile, after: Node, newNode: Node, options: InsertNodeOptions & ConfigurableEnd = {}) { @@ -261,6 +331,7 @@ namespace ts.textChanges { // if not - insert semicolon to preserve the code from changing the meaning due to ASI if (sourceFile.text.charCodeAt(after.end - 1) !== CharacterCodes.semicolon) { this.changes.push({ + kind: ChangeKind.ReplaceWithSingleNode, sourceFile, options: {}, range: { pos: after.end, end: after.end }, @@ -269,8 +340,7 @@ namespace ts.textChanges { } } const endPosition = getAdjustedEndPosition(sourceFile, after, options); - this.changes.push({ sourceFile, options, useIndentationFromFile: true, node: newNode, range: { pos: endPosition, end: endPosition } }); - return this; + return this.replaceWithSingle(sourceFile, endPosition, endPosition, newNode, options); } /** @@ -339,10 +409,10 @@ namespace ts.textChanges { } this.changes.push({ + kind: ChangeKind.ReplaceWithSingleNode, sourceFile, range: { pos: startPos, end: containingList[index + 1].getStart(sourceFile) }, node: newNode, - useIndentationFromFile: true, options: { prefix, // write separator and leading trivia of the next element as suffix @@ -383,6 +453,7 @@ namespace ts.textChanges { if (multilineList) { // insert separator immediately following the 'after' node to preserve comments in trailing trivia this.changes.push({ + kind: ChangeKind.ReplaceWithSingleNode, sourceFile, range: { pos: end, end }, node: createToken(separator), @@ -396,6 +467,7 @@ namespace ts.textChanges { insertPos--; } this.changes.push({ + kind: ChangeKind.ReplaceWithSingleNode, sourceFile, range: { pos: insertPos, end: insertPos }, node: newNode, @@ -404,6 +476,7 @@ namespace ts.textChanges { } else { this.changes.push({ + kind: ChangeKind.ReplaceWithSingleNode, sourceFile, range: { pos: end, end }, node: newNode, @@ -446,38 +519,51 @@ namespace ts.textChanges { } private computeNewText(change: Change, sourceFile: SourceFile): string { - if (!change.node) { + if (change.kind === ChangeKind.Remove) { // deletion case return ""; } + const options = change.options || {}; - const nonFormattedText = getNonformattedText(change.node, sourceFile, this.newLine); + let text: string; + const pos = change.range.pos; + const posStartsLine = getLineStartPositionForPosition(pos, sourceFile) === pos; + if (change.kind === ChangeKind.ReplaceWithMultipleNodes) { + const parts = change.nodes.map(n => this.getFormattedTextOfNode(n, sourceFile, pos, options)); + text = parts.join(change.options.nodeSeparator); + } + else { + Debug.assert(change.kind === ChangeKind.ReplaceWithSingleNode, "change.kind === ReplaceWithSingleNode"); + text = this.getFormattedTextOfNode(change.node, sourceFile, pos, options); + } + // strip initial indentation (spaces or tabs) if text will be inserted in the middle of the line + text = (posStartsLine || options.indentation !== undefined) ? text : text.replace(/^\s+/, ""); + return (options.prefix || "") + text + (options.suffix || ""); + } + + private getFormattedTextOfNode(node: Node, sourceFile: SourceFile, pos: number, options: ChangeNodeOptions): string { + const nonformattedText = getNonformattedText(node, sourceFile, this.newLine); if (this.validator) { - this.validator(nonFormattedText); + this.validator(nonformattedText); } const formatOptions = this.rulesProvider.getFormatOptions(); - const pos = change.range.pos; const posStartsLine = getLineStartPositionForPosition(pos, sourceFile) === pos; const initialIndentation = - change.options.indentation !== undefined - ? change.options.indentation - : change.useIndentationFromFile - ? formatting.SmartIndenter.getIndentation(change.range.pos, sourceFile, formatOptions, posStartsLine || (change.options.prefix === this.newLineCharacter)) + options.indentation !== undefined + ? options.indentation + : (options.useIndentationFromFile !== false) + ? formatting.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, posStartsLine || (options.prefix === this.newLineCharacter)) : 0; const delta = - change.options.delta !== undefined - ? change.options.delta - : formatting.SmartIndenter.shouldIndentChildNode(change.node) - ? formatOptions.indentSize + options.delta !== undefined + ? options.delta + : formatting.SmartIndenter.shouldIndentChildNode(node) + ? (formatOptions.indentSize || 0) : 0; - let text = applyFormatting(nonFormattedText, sourceFile, initialIndentation, delta, this.rulesProvider); - // strip initial indentation (spaces or tabs) if text will be inserted in the middle of the line - // however keep indentation if it is was forced - text = posStartsLine || change.options.indentation !== undefined ? text : text.replace(/^\s+/, ""); - return (options.prefix || "") + text + (options.suffix || ""); + return applyFormatting(nonformattedText, sourceFile, initialIndentation, delta, this.rulesProvider); } private static normalize(changes: Change[]): Change[] { @@ -654,4 +740,4 @@ namespace ts.textChanges { this.lastNonTriviaPosition = 0; } } -} \ No newline at end of file +} diff --git a/src/services/transpile.ts b/src/services/transpile.ts index fc381ba8e50..000bd124fa7 100644 --- a/src/services/transpile.ts +++ b/src/services/transpile.ts @@ -78,11 +78,11 @@ namespace ts { getSourceFile: (fileName) => fileName === normalizePath(inputFileName) ? sourceFile : undefined, writeFile: (name, text) => { if (fileExtensionIs(name, ".map")) { - Debug.assert(sourceMapText === undefined, `Unexpected multiple source map outputs for the file '${name}'`); + Debug.assertEqual(sourceMapText, undefined, "Unexpected multiple source map outputs, file:", name); sourceMapText = text; } else { - Debug.assert(outputText === undefined, `Unexpected multiple outputs for the file: '${name}'`); + Debug.assertEqual(outputText, undefined, "Unexpected multiple outputs, file:", name); outputText = text; } }, diff --git a/src/services/types.ts b/src/services/types.ts index 018e8941765..bade6214a6e 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -272,7 +272,6 @@ namespace ts { isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: FormatCodeSettings): CodeAction[]; - getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[]; getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined; diff --git a/tests/baselines/reference/1.0lib-noErrors.js b/tests/baselines/reference/1.0lib-noErrors.js index 1dcfa9743b2..ade0f4bf903 100644 --- a/tests/baselines/reference/1.0lib-noErrors.js +++ b/tests/baselines/reference/1.0lib-noErrors.js @@ -1158,3 +1158,4 @@ MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ +/// diff --git a/tests/baselines/reference/additionOperatorWithNullValueAndInvalidOperator.errors.txt b/tests/baselines/reference/additionOperatorWithNullValueAndInvalidOperator.errors.txt index 5f0d78c59d5..891fe86dcd1 100644 --- a/tests/baselines/reference/additionOperatorWithNullValueAndInvalidOperator.errors.txt +++ b/tests/baselines/reference/additionOperatorWithNullValueAndInvalidOperator.errors.txt @@ -1,14 +1,14 @@ -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(11,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(12,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(13,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(14,14): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(15,14): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(16,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(19,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(20,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(21,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(22,11): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(23,11): error TS2531: Object is possibly 'null'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(11,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'boolean'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(12,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'Object'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(13,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'void'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(14,10): error TS2365: Operator '+' cannot be applied to types 'boolean' and 'null'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(15,10): error TS2365: Operator '+' cannot be applied to types 'Object' and 'null'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(16,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'void'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(19,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'Number'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(20,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'true'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(21,10): error TS2365: Operator '+' cannot be applied to types 'null' and '{ a: string; }'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(22,11): error TS2365: Operator '+' cannot be applied to types 'null' and 'void'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts(23,11): error TS2365: Operator '+' cannot be applied to types 'null' and '() => void'. ==== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndInvalidOperator.ts (11 errors) ==== @@ -23,37 +23,37 @@ tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOpe // null + boolean/Object var r1 = null + a; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'boolean'. var r2 = null + b; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'Object'. var r3 = null + c; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'void'. var r4 = a + null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'boolean' and 'null'. var r5 = b + null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'Object' and 'null'. var r6 = null + c; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'void'. // other cases var r7 = null + d; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'Number'. var r8 = null + true; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'true'. var r9 = null + { a: '' }; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and '{ a: string; }'. var r10 = null + foo(); - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'void'. var r11 = null + (() => { }); - ~~~~ -!!! error TS2531: Object is possibly 'null'. \ No newline at end of file + ~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and '() => void'. \ No newline at end of file diff --git a/tests/baselines/reference/additionOperatorWithNullValueAndValidOperator.errors.txt b/tests/baselines/reference/additionOperatorWithNullValueAndValidOperator.errors.txt index db01a42c8bd..b722fd1be46 100644 --- a/tests/baselines/reference/additionOperatorWithNullValueAndValidOperator.errors.txt +++ b/tests/baselines/reference/additionOperatorWithNullValueAndValidOperator.errors.txt @@ -1,13 +1,13 @@ -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(15,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(16,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(17,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(18,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(19,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(20,14): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(21,14): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(22,15): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(23,17): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(24,20): error TS2531: Object is possibly 'null'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(15,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'number'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(16,10): error TS2365: Operator '+' cannot be applied to types 'null' and '1'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(17,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'E'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(18,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'E.a'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(19,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'E.a'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(20,10): error TS2365: Operator '+' cannot be applied to types 'number' and 'null'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(21,10): error TS2365: Operator '+' cannot be applied to types '1' and 'null'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(22,11): error TS2365: Operator '+' cannot be applied to types 'E' and 'null'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(23,11): error TS2365: Operator '+' cannot be applied to types 'E.a' and 'null'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts(24,11): error TS2365: Operator '+' cannot be applied to types 'E.a' and 'null'. ==== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithNullValueAndValidOperator.ts (10 errors) ==== @@ -26,35 +26,35 @@ tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOpe // null + number/enum var r3 = null + b; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'number'. var r4 = null + 1; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and '1'. var r5 = null + c; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'E'. var r6 = null + E.a; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'E.a'. var r7 = null + E['a']; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'E.a'. var r8 = b + null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'number' and 'null'. var r9 = 1 + null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types '1' and 'null'. var r10 = c + null - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'E' and 'null'. var r11 = E.a + null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'E.a' and 'null'. var r12 = E['a'] + null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'E.a' and 'null'. // null + string var r13 = null + d; diff --git a/tests/baselines/reference/additionOperatorWithOnlyNullValueOrUndefinedValue.errors.txt b/tests/baselines/reference/additionOperatorWithOnlyNullValueOrUndefinedValue.errors.txt index aef66891be0..b4746ff1e68 100644 --- a/tests/baselines/reference/additionOperatorWithOnlyNullValueOrUndefinedValue.errors.txt +++ b/tests/baselines/reference/additionOperatorWithOnlyNullValueOrUndefinedValue.errors.txt @@ -1,32 +1,20 @@ -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(2,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(2,17): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(3,10): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(3,17): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(4,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(4,22): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(5,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(5,22): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(2,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(3,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(4,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'null'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts(5,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. -==== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts (8 errors) ==== +==== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithOnlyNullValueOrUndefinedValue.ts (4 errors) ==== // bug 819721 var r1 = null + null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. var r2 = null + undefined; - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. var r3 = undefined + null; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'null'. var r4 = undefined + undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. \ No newline at end of file + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. \ No newline at end of file diff --git a/tests/baselines/reference/additionOperatorWithTypeParameter.errors.txt b/tests/baselines/reference/additionOperatorWithTypeParameter.errors.txt index 478c4fdee3f..bdb81190b50 100644 --- a/tests/baselines/reference/additionOperatorWithTypeParameter.errors.txt +++ b/tests/baselines/reference/additionOperatorWithTypeParameter.errors.txt @@ -8,8 +8,8 @@ tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOpe tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithTypeParameter.ts(27,15): error TS2365: Operator '+' cannot be applied to types 'Object' and 'T'. tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithTypeParameter.ts(28,15): error TS2365: Operator '+' cannot be applied to types 'E' and 'T'. tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithTypeParameter.ts(29,15): error TS2365: Operator '+' cannot be applied to types 'void' and 'T'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithTypeParameter.ts(32,19): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithTypeParameter.ts(33,19): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithTypeParameter.ts(32,15): error TS2365: Operator '+' cannot be applied to types 'T' and 'null'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithTypeParameter.ts(33,15): error TS2365: Operator '+' cannot be applied to types 'T' and 'undefined'. tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithTypeParameter.ts(34,15): error TS2365: Operator '+' cannot be applied to types 'T' and 'T'. tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithTypeParameter.ts(35,15): error TS2365: Operator '+' cannot be applied to types 'T' and 'U'. tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithTypeParameter.ts(36,15): error TS2365: Operator '+' cannot be applied to types 'T' and '() => void'. @@ -69,11 +69,11 @@ tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOpe // other cases var r15 = t + null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'T' and 'null'. var r16 = t + undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'T' and 'undefined'. var r17 = t + t; ~~~~~ !!! error TS2365: Operator '+' cannot be applied to types 'T' and 'T'. diff --git a/tests/baselines/reference/additionOperatorWithUndefinedValueAndInvalidOperands.errors.txt b/tests/baselines/reference/additionOperatorWithUndefinedValueAndInvalidOperands.errors.txt index 06e8c67b105..6be41e81fd5 100644 --- a/tests/baselines/reference/additionOperatorWithUndefinedValueAndInvalidOperands.errors.txt +++ b/tests/baselines/reference/additionOperatorWithUndefinedValueAndInvalidOperands.errors.txt @@ -1,14 +1,14 @@ -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(11,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(12,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(13,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(14,14): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(15,14): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(16,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(19,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(20,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(21,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(22,11): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(23,11): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(11,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'boolean'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(12,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'Object'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(13,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'void'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(14,10): error TS2365: Operator '+' cannot be applied to types 'boolean' and 'undefined'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(15,10): error TS2365: Operator '+' cannot be applied to types 'Object' and 'undefined'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(16,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'void'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(19,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'Number'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(20,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'true'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(21,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and '{ a: string; }'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(22,11): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'void'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts(23,11): error TS2365: Operator '+' cannot be applied to types 'undefined' and '() => void'. ==== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndInvalidOperands.ts (11 errors) ==== @@ -23,37 +23,37 @@ tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOpe // undefined + boolean/Object var r1 = undefined + a; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'boolean'. var r2 = undefined + b; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'Object'. var r3 = undefined + c; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'void'. var r4 = a + undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'boolean' and 'undefined'. var r5 = b + undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'Object' and 'undefined'. var r6 = undefined + c; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'void'. // other cases var r7 = undefined + d; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'Number'. var r8 = undefined + true; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'true'. var r9 = undefined + { a: '' }; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and '{ a: string; }'. var r10 = undefined + foo(); - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'void'. var r11 = undefined + (() => { }); - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. \ No newline at end of file + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and '() => void'. \ No newline at end of file diff --git a/tests/baselines/reference/additionOperatorWithUndefinedValueAndValidOperator.errors.txt b/tests/baselines/reference/additionOperatorWithUndefinedValueAndValidOperator.errors.txt index 04c0e2f3266..db2446aa3f9 100644 --- a/tests/baselines/reference/additionOperatorWithUndefinedValueAndValidOperator.errors.txt +++ b/tests/baselines/reference/additionOperatorWithUndefinedValueAndValidOperator.errors.txt @@ -1,13 +1,13 @@ -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(15,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(16,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(17,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(18,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(19,10): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(20,14): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(21,14): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(22,15): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(23,17): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(24,20): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(15,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'number'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(16,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and '1'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(17,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'E'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(18,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'E.a'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(19,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'E.a'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(20,10): error TS2365: Operator '+' cannot be applied to types 'number' and 'undefined'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(21,10): error TS2365: Operator '+' cannot be applied to types '1' and 'undefined'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(22,11): error TS2365: Operator '+' cannot be applied to types 'E' and 'undefined'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(23,11): error TS2365: Operator '+' cannot be applied to types 'E.a' and 'undefined'. +tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts(24,11): error TS2365: Operator '+' cannot be applied to types 'E.a' and 'undefined'. ==== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithUndefinedValueAndValidOperator.ts (10 errors) ==== @@ -26,35 +26,35 @@ tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOpe // undefined + number/enum var r3 = undefined + b; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'number'. var r4 = undefined + 1; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and '1'. var r5 = undefined + c; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'E'. var r6 = undefined + E.a; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'E.a'. var r7 = undefined + E['a']; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'E.a'. var r8 = b + undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'number' and 'undefined'. var r9 = 1 + undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types '1' and 'undefined'. var r10 = c + undefined - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'E' and 'undefined'. var r11 = E.a + undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'E.a' and 'undefined'. var r12 = E['a'] + undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'E.a' and 'undefined'. // undefined + string var r13 = undefined + d; diff --git a/tests/baselines/reference/asOperator3.types b/tests/baselines/reference/asOperator3.types index a888d70892b..e5e319d3caf 100644 --- a/tests/baselines/reference/asOperator3.types +++ b/tests/baselines/reference/asOperator3.types @@ -36,7 +36,7 @@ var d = `Hello ${123} World` as string; var e = `Hello` as string; >e : string >`Hello` as string : string ->`Hello` : string +>`Hello` : "Hello" var f = 1 + `${1} end of string` as string; >f : string @@ -59,5 +59,5 @@ var h = tag `Hello` as string; >tag `Hello` as string : string >tag `Hello` : any >tag : (...x: any[]) => any ->`Hello` : string +>`Hello` : "Hello" diff --git a/tests/baselines/reference/asOperatorASI.types b/tests/baselines/reference/asOperatorASI.types index e5e1de88c29..985806bf040 100644 --- a/tests/baselines/reference/asOperatorASI.types +++ b/tests/baselines/reference/asOperatorASI.types @@ -14,7 +14,7 @@ var x = 10 as `Hello world`; // should not error >as `Hello world` : any >as : (...args: any[]) => any ->`Hello world` : string +>`Hello world` : "Hello world" // Example 2 var y = 20 diff --git a/tests/baselines/reference/bindingPatternOmittedExpressionNesting.js b/tests/baselines/reference/bindingPatternOmittedExpressionNesting.js new file mode 100644 index 00000000000..e2dff308879 --- /dev/null +++ b/tests/baselines/reference/bindingPatternOmittedExpressionNesting.js @@ -0,0 +1,11 @@ +//// [bindingPatternOmittedExpressionNesting.ts] +export let [,,[,[],,[],]] = undefined as any; + +//// [bindingPatternOmittedExpressionNesting.js] +"use strict"; +exports.__esModule = true; +exports._a = (_b = undefined, _c = _b[2], _d = _c[1], _e = _c[3]); +var _b, _c, _d, _e; + + +//// [bindingPatternOmittedExpressionNesting.d.ts] diff --git a/tests/baselines/reference/bindingPatternOmittedExpressionNesting.symbols b/tests/baselines/reference/bindingPatternOmittedExpressionNesting.symbols new file mode 100644 index 00000000000..25db0dd6271 --- /dev/null +++ b/tests/baselines/reference/bindingPatternOmittedExpressionNesting.symbols @@ -0,0 +1,4 @@ +=== tests/cases/compiler/bindingPatternOmittedExpressionNesting.ts === +export let [,,[,[],,[],]] = undefined as any; +>undefined : Symbol(undefined) + diff --git a/tests/baselines/reference/bindingPatternOmittedExpressionNesting.types b/tests/baselines/reference/bindingPatternOmittedExpressionNesting.types new file mode 100644 index 00000000000..2c5211c9d9d --- /dev/null +++ b/tests/baselines/reference/bindingPatternOmittedExpressionNesting.types @@ -0,0 +1,9 @@ +=== tests/cases/compiler/bindingPatternOmittedExpressionNesting.ts === +export let [,,[,[],,[],]] = undefined as any; +> : undefined +> : undefined +> : undefined +> : undefined +>undefined as any : any +>undefined : undefined + diff --git a/tests/baselines/reference/bitwiseNotOperatorWithAnyOtherType.errors.txt b/tests/baselines/reference/bitwiseNotOperatorWithAnyOtherType.errors.txt index 411c7099f11..795a9f0863d 100644 --- a/tests/baselines/reference/bitwiseNotOperatorWithAnyOtherType.errors.txt +++ b/tests/baselines/reference/bitwiseNotOperatorWithAnyOtherType.errors.txt @@ -1,14 +1,11 @@ tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts(34,24): error TS2532: Object is possibly 'undefined'. tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts(35,24): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts(46,26): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts(46,33): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts(47,26): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts(47,33): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts(48,26): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts(48,38): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts(46,26): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. +tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts(47,26): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. +tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts(48,26): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. -==== tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts (8 errors) ==== +==== tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNotOperatorWithAnyOtherType.ts (5 errors) ==== // ~ operator on any type var ANY: any; @@ -59,20 +56,14 @@ tests/cases/conformance/expressions/unaryOperators/bitwiseNotOperator/bitwiseNot var ResultIsNumber14 = ~A.foo(); var ResultIsNumber15 = ~(ANY + ANY1); var ResultIsNumber16 = ~(null + undefined); - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. var ResultIsNumber17 = ~(null + null); - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. var ResultIsNumber18 = ~(undefined + undefined); - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. // multiple ~ operators var ResultIsNumber19 = ~~ANY; diff --git a/tests/baselines/reference/checkTypePredicateForRedundantProperties.errors.txt b/tests/baselines/reference/checkTypePredicateForRedundantProperties.errors.txt new file mode 100644 index 00000000000..a5cb9b0a098 --- /dev/null +++ b/tests/baselines/reference/checkTypePredicateForRedundantProperties.errors.txt @@ -0,0 +1,13 @@ +tests/cases/compiler/checkTypePredicateForRedundantProperties.ts(1,35): error TS2300: Duplicate identifier 'a'. +tests/cases/compiler/checkTypePredicateForRedundantProperties.ts(1,46): error TS2300: Duplicate identifier 'a'. + + +==== tests/cases/compiler/checkTypePredicateForRedundantProperties.ts (2 errors) ==== + function addProp2(x: any): x is { a: string; a: string; } { + ~ +!!! error TS2300: Duplicate identifier 'a'. + ~ +!!! error TS2300: Duplicate identifier 'a'. + return true; + } + \ No newline at end of file diff --git a/tests/baselines/reference/checkTypePredicateForRedundantProperties.js b/tests/baselines/reference/checkTypePredicateForRedundantProperties.js new file mode 100644 index 00000000000..8f7be2bfbbc --- /dev/null +++ b/tests/baselines/reference/checkTypePredicateForRedundantProperties.js @@ -0,0 +1,10 @@ +//// [checkTypePredicateForRedundantProperties.ts] +function addProp2(x: any): x is { a: string; a: string; } { + return true; +} + + +//// [checkTypePredicateForRedundantProperties.js] +function addProp2(x) { + return true; +} diff --git a/tests/baselines/reference/commentOnBinaryOperator1.js b/tests/baselines/reference/commentOnBinaryOperator1.js index daad6f5bdde..73db1caeab5 100644 --- a/tests/baselines/reference/commentOnBinaryOperator1.js +++ b/tests/baselines/reference/commentOnBinaryOperator1.js @@ -21,5 +21,5 @@ var b = 'some' + 'text'; var c = 'some' /* comment */ - +/*comment1*/ + + /*comment1*/ 'text'; diff --git a/tests/baselines/reference/commentsArgumentsOfCallExpression2.js b/tests/baselines/reference/commentsArgumentsOfCallExpression2.js index e05256b86b6..f89e67ef3d8 100644 --- a/tests/baselines/reference/commentsArgumentsOfCallExpression2.js +++ b/tests/baselines/reference/commentsArgumentsOfCallExpression2.js @@ -14,7 +14,7 @@ foo( function foo(/*c1*/ x, /*d1*/ y, /*e1*/ w) { } var a, b; foo(/*c2*/ 1, /*d2*/ 1 + 2, /*e1*/ a + b); -foo(/*c3*/ function () { }, /*d2*/ function () { }, /*e2*/ a +/*e3*/ b); +foo(/*c3*/ function () { }, /*d2*/ function () { }, /*e2*/ a + /*e3*/ b); foo(/*c3*/ function () { }, /*d3*/ function () { }, /*e3*/ (a + b)); foo( /*c4*/ function () { }, diff --git a/tests/baselines/reference/compoundAdditionAssignmentLHSCanBeAssigned.errors.txt b/tests/baselines/reference/compoundAdditionAssignmentLHSCanBeAssigned.errors.txt index 08d8fd0c6d3..f7df532b20e 100644 --- a/tests/baselines/reference/compoundAdditionAssignmentLHSCanBeAssigned.errors.txt +++ b/tests/baselines/reference/compoundAdditionAssignmentLHSCanBeAssigned.errors.txt @@ -1,7 +1,7 @@ -tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentLHSCanBeAssigned.ts(32,7): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentLHSCanBeAssigned.ts(33,7): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentLHSCanBeAssigned.ts(39,7): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentLHSCanBeAssigned.ts(40,7): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentLHSCanBeAssigned.ts(32,1): error TS2365: Operator '+=' cannot be applied to types 'number' and 'null'. +tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentLHSCanBeAssigned.ts(33,1): error TS2365: Operator '+=' cannot be applied to types 'number' and 'undefined'. +tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentLHSCanBeAssigned.ts(39,1): error TS2365: Operator '+=' cannot be applied to types 'E' and 'null'. +tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentLHSCanBeAssigned.ts(40,1): error TS2365: Operator '+=' cannot be applied to types 'E' and 'undefined'. ==== tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentLHSCanBeAssigned.ts (4 errors) ==== @@ -37,22 +37,22 @@ tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmen x3 += 0; x3 += E.a; x3 += null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~ +!!! error TS2365: Operator '+=' cannot be applied to types 'number' and 'null'. x3 += undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+=' cannot be applied to types 'number' and 'undefined'. var x4: E; x4 += a; x4 += 0; x4 += E.a; x4 += null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~ +!!! error TS2365: Operator '+=' cannot be applied to types 'E' and 'null'. x4 += undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+=' cannot be applied to types 'E' and 'undefined'. var x5: boolean; x5 += a; diff --git a/tests/baselines/reference/compoundAdditionAssignmentWithInvalidOperands.errors.txt b/tests/baselines/reference/compoundAdditionAssignmentWithInvalidOperands.errors.txt index 1812065f149..e570f2f6cf0 100644 --- a/tests/baselines/reference/compoundAdditionAssignmentWithInvalidOperands.errors.txt +++ b/tests/baselines/reference/compoundAdditionAssignmentWithInvalidOperands.errors.txt @@ -3,22 +3,22 @@ tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmen tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(8,1): error TS2365: Operator '+=' cannot be applied to types 'boolean' and '0'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(9,1): error TS2365: Operator '+=' cannot be applied to types 'boolean' and 'E.a'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(10,1): error TS2365: Operator '+=' cannot be applied to types 'boolean' and '{}'. -tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(11,7): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(12,7): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(11,1): error TS2365: Operator '+=' cannot be applied to types 'boolean' and 'null'. +tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(12,1): error TS2365: Operator '+=' cannot be applied to types 'boolean' and 'undefined'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(15,1): error TS2365: Operator '+=' cannot be applied to types '{}' and 'void'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(16,1): error TS2365: Operator '+=' cannot be applied to types '{}' and 'true'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(17,1): error TS2365: Operator '+=' cannot be applied to types '{}' and '0'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(18,1): error TS2365: Operator '+=' cannot be applied to types '{}' and 'E.a'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(19,1): error TS2365: Operator '+=' cannot be applied to types '{}' and '{}'. -tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(20,7): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(21,7): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(20,1): error TS2365: Operator '+=' cannot be applied to types '{}' and 'null'. +tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(21,1): error TS2365: Operator '+=' cannot be applied to types '{}' and 'undefined'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(24,1): error TS2365: Operator '+=' cannot be applied to types 'void' and 'void'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(25,1): error TS2365: Operator '+=' cannot be applied to types 'void' and 'true'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(26,1): error TS2365: Operator '+=' cannot be applied to types 'void' and '0'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(27,1): error TS2365: Operator '+=' cannot be applied to types 'void' and 'E.a'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(28,1): error TS2365: Operator '+=' cannot be applied to types 'void' and '{}'. -tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(29,7): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(30,7): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(29,1): error TS2365: Operator '+=' cannot be applied to types 'void' and 'null'. +tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(30,1): error TS2365: Operator '+=' cannot be applied to types 'void' and 'undefined'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(33,1): error TS2365: Operator '+=' cannot be applied to types 'number' and 'void'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(34,1): error TS2365: Operator '+=' cannot be applied to types 'number' and 'true'. tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmentWithInvalidOperands.ts(35,1): error TS2365: Operator '+=' cannot be applied to types 'number' and '{}'. @@ -49,11 +49,11 @@ tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmen ~~~~~~~~ !!! error TS2365: Operator '+=' cannot be applied to types 'boolean' and '{}'. x1 += null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~ +!!! error TS2365: Operator '+=' cannot be applied to types 'boolean' and 'null'. x1 += undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+=' cannot be applied to types 'boolean' and 'undefined'. var x2: {}; x2 += a; @@ -72,11 +72,11 @@ tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmen ~~~~~~~~ !!! error TS2365: Operator '+=' cannot be applied to types '{}' and '{}'. x2 += null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~ +!!! error TS2365: Operator '+=' cannot be applied to types '{}' and 'null'. x2 += undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+=' cannot be applied to types '{}' and 'undefined'. var x3: void; x3 += a; @@ -95,11 +95,11 @@ tests/cases/conformance/expressions/assignmentOperator/compoundAdditionAssignmen ~~~~~~~~ !!! error TS2365: Operator '+=' cannot be applied to types 'void' and '{}'. x3 += null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~ +!!! error TS2365: Operator '+=' cannot be applied to types 'void' and 'null'. x3 += undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+=' cannot be applied to types 'void' and 'undefined'. var x4: number; x4 += a; diff --git a/tests/baselines/reference/computedPropertyNames10_ES5.types b/tests/baselines/reference/computedPropertyNames10_ES5.types index 96130979298..4750d44fde8 100644 --- a/tests/baselines/reference/computedPropertyNames10_ES5.types +++ b/tests/baselines/reference/computedPropertyNames10_ES5.types @@ -46,7 +46,7 @@ var v = { >true : true [`hello bye`]() { }, ->`hello bye` : string +>`hello bye` : "hello bye" [`hello ${a} bye`]() { } >`hello ${a} bye` : string diff --git a/tests/baselines/reference/computedPropertyNames10_ES6.types b/tests/baselines/reference/computedPropertyNames10_ES6.types index 5bbda2e8f19..3a12047f588 100644 --- a/tests/baselines/reference/computedPropertyNames10_ES6.types +++ b/tests/baselines/reference/computedPropertyNames10_ES6.types @@ -46,7 +46,7 @@ var v = { >true : true [`hello bye`]() { }, ->`hello bye` : string +>`hello bye` : "hello bye" [`hello ${a} bye`]() { } >`hello ${a} bye` : string diff --git a/tests/baselines/reference/computedPropertyNames11_ES5.types b/tests/baselines/reference/computedPropertyNames11_ES5.types index 88fbc690323..08aa14aa911 100644 --- a/tests/baselines/reference/computedPropertyNames11_ES5.types +++ b/tests/baselines/reference/computedPropertyNames11_ES5.types @@ -55,7 +55,7 @@ var v = { >0 : 0 set [`hello bye`](v) { }, ->`hello bye` : string +>`hello bye` : "hello bye" >v : any get [`hello ${a} bye`]() { return 0; } diff --git a/tests/baselines/reference/computedPropertyNames11_ES6.types b/tests/baselines/reference/computedPropertyNames11_ES6.types index 458d6d1de49..2f61eb59d61 100644 --- a/tests/baselines/reference/computedPropertyNames11_ES6.types +++ b/tests/baselines/reference/computedPropertyNames11_ES6.types @@ -55,7 +55,7 @@ var v = { >0 : 0 set [`hello bye`](v) { }, ->`hello bye` : string +>`hello bye` : "hello bye" >v : any get [`hello ${a} bye`]() { return 0; } diff --git a/tests/baselines/reference/computedPropertyNames13_ES5.types b/tests/baselines/reference/computedPropertyNames13_ES5.types index 2585fe15f94..a4381fdb891 100644 --- a/tests/baselines/reference/computedPropertyNames13_ES5.types +++ b/tests/baselines/reference/computedPropertyNames13_ES5.types @@ -45,7 +45,7 @@ class C { >true : true [`hello bye`]() { } ->`hello bye` : string +>`hello bye` : "hello bye" static [`hello ${a} bye`]() { } >`hello ${a} bye` : string diff --git a/tests/baselines/reference/computedPropertyNames13_ES6.types b/tests/baselines/reference/computedPropertyNames13_ES6.types index 61e8cae265c..48a989f9af1 100644 --- a/tests/baselines/reference/computedPropertyNames13_ES6.types +++ b/tests/baselines/reference/computedPropertyNames13_ES6.types @@ -45,7 +45,7 @@ class C { >true : true [`hello bye`]() { } ->`hello bye` : string +>`hello bye` : "hello bye" static [`hello ${a} bye`]() { } >`hello ${a} bye` : string diff --git a/tests/baselines/reference/computedPropertyNames16_ES5.types b/tests/baselines/reference/computedPropertyNames16_ES5.types index 5c0fb34bc25..5f14d4d4c5d 100644 --- a/tests/baselines/reference/computedPropertyNames16_ES5.types +++ b/tests/baselines/reference/computedPropertyNames16_ES5.types @@ -54,7 +54,7 @@ class C { >0 : 0 set [`hello bye`](v) { } ->`hello bye` : string +>`hello bye` : "hello bye" >v : any get [`hello ${a} bye`]() { return 0; } diff --git a/tests/baselines/reference/computedPropertyNames16_ES6.types b/tests/baselines/reference/computedPropertyNames16_ES6.types index 7ebb2cd0060..d7f00f77d32 100644 --- a/tests/baselines/reference/computedPropertyNames16_ES6.types +++ b/tests/baselines/reference/computedPropertyNames16_ES6.types @@ -54,7 +54,7 @@ class C { >0 : 0 set [`hello bye`](v) { } ->`hello bye` : string +>`hello bye` : "hello bye" >v : any get [`hello ${a} bye`]() { return 0; } diff --git a/tests/baselines/reference/computedPropertyNames4_ES5.types b/tests/baselines/reference/computedPropertyNames4_ES5.types index 6f875aaddac..fc883aa2833 100644 --- a/tests/baselines/reference/computedPropertyNames4_ES5.types +++ b/tests/baselines/reference/computedPropertyNames4_ES5.types @@ -55,7 +55,7 @@ var v = { >0 : 0 [`hello bye`]: 0, ->`hello bye` : string +>`hello bye` : "hello bye" >0 : 0 [`hello ${a} bye`]: 0 diff --git a/tests/baselines/reference/computedPropertyNames4_ES6.types b/tests/baselines/reference/computedPropertyNames4_ES6.types index e9feac0a81f..5704841b97f 100644 --- a/tests/baselines/reference/computedPropertyNames4_ES6.types +++ b/tests/baselines/reference/computedPropertyNames4_ES6.types @@ -55,7 +55,7 @@ var v = { >0 : 0 [`hello bye`]: 0, ->`hello bye` : string +>`hello bye` : "hello bye" >0 : 0 [`hello ${a} bye`]: 0 diff --git a/tests/baselines/reference/computedPropertyNames51_ES5.errors.txt b/tests/baselines/reference/computedPropertyNames51_ES5.errors.txt new file mode 100644 index 00000000000..3ee876feaed --- /dev/null +++ b/tests/baselines/reference/computedPropertyNames51_ES5.errors.txt @@ -0,0 +1,15 @@ +tests/cases/conformance/es6/computedProperties/computedPropertyNames51_ES5.ts(5,9): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. + + +==== tests/cases/conformance/es6/computedProperties/computedPropertyNames51_ES5.ts (1 errors) ==== + function f() { + var t: T; + var k: K; + var v = { + [t]: 0, + ~~~ +!!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. + [k]: 1 + }; + } + \ No newline at end of file diff --git a/tests/baselines/reference/computedPropertyNames51_ES5.js b/tests/baselines/reference/computedPropertyNames51_ES5.js new file mode 100644 index 00000000000..9f138b37f3d --- /dev/null +++ b/tests/baselines/reference/computedPropertyNames51_ES5.js @@ -0,0 +1,21 @@ +//// [computedPropertyNames51_ES5.ts] +function f() { + var t: T; + var k: K; + var v = { + [t]: 0, + [k]: 1 + }; +} + + +//// [computedPropertyNames51_ES5.js] +function f() { + var t; + var k; + var v = (_a = {}, + _a[t] = 0, + _a[k] = 1, + _a); + var _a; +} diff --git a/tests/baselines/reference/computedPropertyNames51_ES6.errors.txt b/tests/baselines/reference/computedPropertyNames51_ES6.errors.txt new file mode 100644 index 00000000000..b85ae8f48d0 --- /dev/null +++ b/tests/baselines/reference/computedPropertyNames51_ES6.errors.txt @@ -0,0 +1,15 @@ +tests/cases/conformance/es6/computedProperties/computedPropertyNames51_ES6.ts(5,9): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. + + +==== tests/cases/conformance/es6/computedProperties/computedPropertyNames51_ES6.ts (1 errors) ==== + function f() { + var t: T; + var k: K; + var v = { + [t]: 0, + ~~~ +!!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. + [k]: 1 + }; + } + \ No newline at end of file diff --git a/tests/baselines/reference/computedPropertyNames51_ES6.js b/tests/baselines/reference/computedPropertyNames51_ES6.js new file mode 100644 index 00000000000..361af9cc15a --- /dev/null +++ b/tests/baselines/reference/computedPropertyNames51_ES6.js @@ -0,0 +1,20 @@ +//// [computedPropertyNames51_ES6.ts] +function f() { + var t: T; + var k: K; + var v = { + [t]: 0, + [k]: 1 + }; +} + + +//// [computedPropertyNames51_ES6.js] +function f() { + var t; + var k; + var v = { + [t]: 0, + [k]: 1 + }; +} diff --git a/tests/baselines/reference/computedPropertyNames8_ES5.errors.txt b/tests/baselines/reference/computedPropertyNames8_ES5.errors.txt index 9fbd14a92fb..56dbe16a523 100644 --- a/tests/baselines/reference/computedPropertyNames8_ES5.errors.txt +++ b/tests/baselines/reference/computedPropertyNames8_ES5.errors.txt @@ -1,8 +1,7 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames8_ES5.ts(5,9): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. -tests/cases/conformance/es6/computedProperties/computedPropertyNames8_ES5.ts(6,9): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. -==== tests/cases/conformance/es6/computedProperties/computedPropertyNames8_ES5.ts (2 errors) ==== +==== tests/cases/conformance/es6/computedProperties/computedPropertyNames8_ES5.ts (1 errors) ==== function f() { var t: T; var u: U; @@ -11,7 +10,5 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames8_ES5.ts(6,9 ~~~ !!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. [u]: 1 - ~~~ -!!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. }; } \ No newline at end of file diff --git a/tests/baselines/reference/computedPropertyNames8_ES6.errors.txt b/tests/baselines/reference/computedPropertyNames8_ES6.errors.txt index 22674a3992c..9996f35f063 100644 --- a/tests/baselines/reference/computedPropertyNames8_ES6.errors.txt +++ b/tests/baselines/reference/computedPropertyNames8_ES6.errors.txt @@ -1,8 +1,7 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames8_ES6.ts(5,9): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. -tests/cases/conformance/es6/computedProperties/computedPropertyNames8_ES6.ts(6,9): error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. -==== tests/cases/conformance/es6/computedProperties/computedPropertyNames8_ES6.ts (2 errors) ==== +==== tests/cases/conformance/es6/computedProperties/computedPropertyNames8_ES6.ts (1 errors) ==== function f() { var t: T; var u: U; @@ -11,7 +10,5 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames8_ES6.ts(6,9 ~~~ !!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. [u]: 1 - ~~~ -!!! error TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'. }; } \ No newline at end of file diff --git a/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.errors.txt b/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.errors.txt index 1d37b20a3b3..708f175c002 100644 --- a/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.errors.txt +++ b/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.errors.txt @@ -34,7 +34,7 @@ tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts(138,13): error T tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts(141,32): error TS1005: '{' expected. tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts(143,13): error TS1005: 'try' expected. tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts(159,24): error TS1109: Expression expected. -tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts(159,30): error TS1005: '(' expected. +tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts(159,30): error TS1005: '{' expected. tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts(159,31): error TS2304: Cannot find name 'Property'. tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts(166,13): error TS2365: Operator '+=' cannot be applied to types 'number' and 'void'. tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts(180,40): error TS2447: The '^' operator is not allowed for boolean types. Consider using '!==' instead. @@ -323,7 +323,7 @@ tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts(261,1): error TS ~~~~~ !!! error TS1109: Expression expected. ~ -!!! error TS1005: '(' expected. +!!! error TS1005: '{' expected. ~~~~~~~~ !!! error TS2304: Cannot find name 'Property'. retVal += c.Member(); diff --git a/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.js b/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.js index 8e9dc6da688..f33c4b6d316 100644 --- a/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.js +++ b/tests/baselines/reference/constructorWithIncompleteTypeAnnotation.js @@ -441,7 +441,7 @@ var BasicFeatures = (function () { var xx = c; retVal += ; try { } - catch () { } + catch (_a) { } Property; retVal += c.Member(); retVal += xx.Foo() ? 0 : 1; diff --git a/tests/baselines/reference/contextualTypingFunctionReturningFunction.js b/tests/baselines/reference/contextualTypingFunctionReturningFunction.js new file mode 100644 index 00000000000..31f89bdefd8 --- /dev/null +++ b/tests/baselines/reference/contextualTypingFunctionReturningFunction.js @@ -0,0 +1,19 @@ +//// [contextualTypingFunctionReturningFunction.ts] +interface I { + a(s: string): void; + b(): (n: number) => void; +} + +declare function f(i: I): void; + +f({ + a: s => {}, + b: () => n => {}, +}); + + +//// [contextualTypingFunctionReturningFunction.js] +f({ + a: function (s) { }, + b: function () { return function (n) { }; } +}); diff --git a/tests/baselines/reference/contextualTypingFunctionReturningFunction.symbols b/tests/baselines/reference/contextualTypingFunctionReturningFunction.symbols new file mode 100644 index 00000000000..509c7129745 --- /dev/null +++ b/tests/baselines/reference/contextualTypingFunctionReturningFunction.symbols @@ -0,0 +1,31 @@ +=== tests/cases/compiler/contextualTypingFunctionReturningFunction.ts === +interface I { +>I : Symbol(I, Decl(contextualTypingFunctionReturningFunction.ts, 0, 0)) + + a(s: string): void; +>a : Symbol(I.a, Decl(contextualTypingFunctionReturningFunction.ts, 0, 13)) +>s : Symbol(s, Decl(contextualTypingFunctionReturningFunction.ts, 1, 3)) + + b(): (n: number) => void; +>b : Symbol(I.b, Decl(contextualTypingFunctionReturningFunction.ts, 1, 20)) +>n : Symbol(n, Decl(contextualTypingFunctionReturningFunction.ts, 2, 7)) +} + +declare function f(i: I): void; +>f : Symbol(f, Decl(contextualTypingFunctionReturningFunction.ts, 3, 1)) +>i : Symbol(i, Decl(contextualTypingFunctionReturningFunction.ts, 5, 19)) +>I : Symbol(I, Decl(contextualTypingFunctionReturningFunction.ts, 0, 0)) + +f({ +>f : Symbol(f, Decl(contextualTypingFunctionReturningFunction.ts, 3, 1)) + + a: s => {}, +>a : Symbol(a, Decl(contextualTypingFunctionReturningFunction.ts, 7, 3)) +>s : Symbol(s, Decl(contextualTypingFunctionReturningFunction.ts, 8, 3)) + + b: () => n => {}, +>b : Symbol(b, Decl(contextualTypingFunctionReturningFunction.ts, 8, 12)) +>n : Symbol(n, Decl(contextualTypingFunctionReturningFunction.ts, 9, 9)) + +}); + diff --git a/tests/baselines/reference/contextualTypingFunctionReturningFunction.types b/tests/baselines/reference/contextualTypingFunctionReturningFunction.types new file mode 100644 index 00000000000..8185819acf6 --- /dev/null +++ b/tests/baselines/reference/contextualTypingFunctionReturningFunction.types @@ -0,0 +1,36 @@ +=== tests/cases/compiler/contextualTypingFunctionReturningFunction.ts === +interface I { +>I : I + + a(s: string): void; +>a : (s: string) => void +>s : string + + b(): (n: number) => void; +>b : () => (n: number) => void +>n : number +} + +declare function f(i: I): void; +>f : (i: I) => void +>i : I +>I : I + +f({ +>f({ a: s => {}, b: () => n => {},}) : void +>f : (i: I) => void +>{ a: s => {}, b: () => n => {},} : { a: (s: string) => void; b: () => (n: number) => void; } + + a: s => {}, +>a : (s: string) => void +>s => {} : (s: string) => void +>s : string + + b: () => n => {}, +>b : () => (n: number) => void +>() => n => {} : () => (n: number) => void +>n => {} : (n: number) => void +>n : number + +}); + diff --git a/tests/baselines/reference/contextualTypingFunctionReturningFunction2.js b/tests/baselines/reference/contextualTypingFunctionReturningFunction2.js new file mode 100644 index 00000000000..88308dbe2e6 --- /dev/null +++ b/tests/baselines/reference/contextualTypingFunctionReturningFunction2.js @@ -0,0 +1,9 @@ +//// [contextualTypingFunctionReturningFunction2.ts] +declare function f(n: number): void; +declare function f(cb: () => (n: number) => number): void; + +f(() => n => n); + + +//// [contextualTypingFunctionReturningFunction2.js] +f(function () { return function (n) { return n; }; }); diff --git a/tests/baselines/reference/contextualTypingFunctionReturningFunction2.symbols b/tests/baselines/reference/contextualTypingFunctionReturningFunction2.symbols new file mode 100644 index 00000000000..9071bfc23d8 --- /dev/null +++ b/tests/baselines/reference/contextualTypingFunctionReturningFunction2.symbols @@ -0,0 +1,15 @@ +=== tests/cases/compiler/contextualTypingFunctionReturningFunction2.ts === +declare function f(n: number): void; +>f : Symbol(f, Decl(contextualTypingFunctionReturningFunction2.ts, 0, 0), Decl(contextualTypingFunctionReturningFunction2.ts, 0, 36)) +>n : Symbol(n, Decl(contextualTypingFunctionReturningFunction2.ts, 0, 19)) + +declare function f(cb: () => (n: number) => number): void; +>f : Symbol(f, Decl(contextualTypingFunctionReturningFunction2.ts, 0, 0), Decl(contextualTypingFunctionReturningFunction2.ts, 0, 36)) +>cb : Symbol(cb, Decl(contextualTypingFunctionReturningFunction2.ts, 1, 19)) +>n : Symbol(n, Decl(contextualTypingFunctionReturningFunction2.ts, 1, 30)) + +f(() => n => n); +>f : Symbol(f, Decl(contextualTypingFunctionReturningFunction2.ts, 0, 0), Decl(contextualTypingFunctionReturningFunction2.ts, 0, 36)) +>n : Symbol(n, Decl(contextualTypingFunctionReturningFunction2.ts, 3, 7)) +>n : Symbol(n, Decl(contextualTypingFunctionReturningFunction2.ts, 3, 7)) + diff --git a/tests/baselines/reference/contextualTypingFunctionReturningFunction2.types b/tests/baselines/reference/contextualTypingFunctionReturningFunction2.types new file mode 100644 index 00000000000..14126a0e0cd --- /dev/null +++ b/tests/baselines/reference/contextualTypingFunctionReturningFunction2.types @@ -0,0 +1,18 @@ +=== tests/cases/compiler/contextualTypingFunctionReturningFunction2.ts === +declare function f(n: number): void; +>f : { (n: number): void; (cb: () => (n: number) => number): void; } +>n : number + +declare function f(cb: () => (n: number) => number): void; +>f : { (n: number): void; (cb: () => (n: number) => number): void; } +>cb : () => (n: number) => number +>n : number + +f(() => n => n); +>f(() => n => n) : void +>f : { (n: number): void; (cb: () => (n: number) => number): void; } +>() => n => n : () => (n: number) => number +>n => n : (n: number) => number +>n : number +>n : number + diff --git a/tests/baselines/reference/contextualTypingWithFixedTypeParameters1.errors.txt b/tests/baselines/reference/contextualTypingWithFixedTypeParameters1.errors.txt index 95b3c66c264..0fe1e69d079 100644 --- a/tests/baselines/reference/contextualTypingWithFixedTypeParameters1.errors.txt +++ b/tests/baselines/reference/contextualTypingWithFixedTypeParameters1.errors.txt @@ -1,15 +1,15 @@ tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts(2,22): error TS2339: Property 'foo' does not exist on type 'string'. -tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts(3,32): error TS2339: Property 'foo' does not exist on type 'string'. -tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts(3,38): error TS2345: Argument of type '1' is not assignable to parameter of type 'string'. +tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts(3,32): error TS2339: Property 'foo' does not exist on type '""'. +tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts(3,38): error TS2345: Argument of type '1' is not assignable to parameter of type '""'. ==== tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts (3 errors) ==== var f10: (x: T, b: () => (a: T) => void, y: T) => T; - f10('', () => a => a.foo, ''); // a is string + f10('', () => a => a.foo, ''); // a is "" ~~~ !!! error TS2339: Property 'foo' does not exist on type 'string'. var r9 = f10('', () => (a => a.foo), 1); // error ~~~ -!!! error TS2339: Property 'foo' does not exist on type 'string'. +!!! error TS2339: Property 'foo' does not exist on type '""'. ~ -!!! error TS2345: Argument of type '1' is not assignable to parameter of type 'string'. \ No newline at end of file +!!! error TS2345: Argument of type '1' is not assignable to parameter of type '""'. \ No newline at end of file diff --git a/tests/baselines/reference/contextualTypingWithFixedTypeParameters1.js b/tests/baselines/reference/contextualTypingWithFixedTypeParameters1.js index 55f7580b3f8..b60a7c986ab 100644 --- a/tests/baselines/reference/contextualTypingWithFixedTypeParameters1.js +++ b/tests/baselines/reference/contextualTypingWithFixedTypeParameters1.js @@ -1,9 +1,9 @@ //// [contextualTypingWithFixedTypeParameters1.ts] var f10: (x: T, b: () => (a: T) => void, y: T) => T; -f10('', () => a => a.foo, ''); // a is string +f10('', () => a => a.foo, ''); // a is "" var r9 = f10('', () => (a => a.foo), 1); // error //// [contextualTypingWithFixedTypeParameters1.js] var f10; -f10('', function () { return function (a) { return a.foo; }; }, ''); // a is string +f10('', function () { return function (a) { return a.foo; }; }, ''); // a is "" var r9 = f10('', function () { return (function (a) { return a.foo; }); }, 1); // error diff --git a/tests/baselines/reference/decrementOperatorWithAnyOtherTypeInvalidOperations.errors.txt b/tests/baselines/reference/decrementOperatorWithAnyOtherTypeInvalidOperations.errors.txt index 33e31bc9478..ff54b4d52c6 100644 --- a/tests/baselines/reference/decrementOperatorWithAnyOtherTypeInvalidOperations.errors.txt +++ b/tests/baselines/reference/decrementOperatorWithAnyOtherTypeInvalidOperations.errors.txt @@ -19,27 +19,21 @@ tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOp tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(46,26): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(47,26): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(48,26): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(48,27): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(48,34): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(48,27): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(49,26): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(49,27): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(49,34): error TS2531: Object is possibly 'null'. +tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(49,27): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(50,26): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(50,27): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(50,39): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(50,27): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(51,26): error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(52,26): error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(54,24): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(55,24): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(56,24): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(56,25): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(56,32): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(56,25): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(57,24): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(57,25): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(57,32): error TS2531: Object is possibly 'null'. +tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(57,25): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(58,24): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(58,25): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(58,37): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(58,25): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(59,24): error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(60,24): error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(63,3): error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. @@ -58,7 +52,7 @@ tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOp tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts(72,12): error TS1109: Expression expected. -==== tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts (58 errors) ==== +==== tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOperatorWithAnyOtherTypeInvalidOperations.ts (52 errors) ==== // -- operator on any type var ANY1: any; var ANY2: any[] = ["", ""]; @@ -149,24 +143,18 @@ tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOp var ResultIsNumber19 = --(null + undefined); ~~~~~~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. var ResultIsNumber20 = --(null + null); ~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. var ResultIsNumber21 = --(undefined + undefined); ~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. var ResultIsNumber22 = --obj1.x; ~~~~~~ !!! error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. @@ -183,24 +171,18 @@ tests/cases/conformance/expressions/unaryOperators/decrementOperator/decrementOp var ResultIsNumber26 = (null + undefined)--; ~~~~~~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. var ResultIsNumber27 = (null + null)--; ~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. var ResultIsNumber28 = (undefined + undefined)--; ~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. var ResultIsNumber29 = obj1.x--; ~~~~~~ !!! error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. diff --git a/tests/baselines/reference/deleteOperatorWithAnyOtherType.errors.txt b/tests/baselines/reference/deleteOperatorWithAnyOtherType.errors.txt index f2260b11036..5ea3f6135c1 100644 --- a/tests/baselines/reference/deleteOperatorWithAnyOtherType.errors.txt +++ b/tests/baselines/reference/deleteOperatorWithAnyOtherType.errors.txt @@ -9,15 +9,12 @@ tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperator tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(42,32): error TS2703: The operand of a delete operator must be a property reference. tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(43,32): error TS2703: The operand of a delete operator must be a property reference. tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(44,33): error TS2703: The operand of a delete operator must be a property reference. -tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(45,33): error TS2531: Object is possibly 'null'. +tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(45,33): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(45,33): error TS2703: The operand of a delete operator must be a property reference. -tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(45,40): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(46,33): error TS2531: Object is possibly 'null'. +tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(46,33): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(46,33): error TS2703: The operand of a delete operator must be a property reference. -tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(46,40): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(47,33): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(47,33): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(47,33): error TS2703: The operand of a delete operator must be a property reference. -tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(47,45): error TS2532: Object is possibly 'undefined'. tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(50,32): error TS2703: The operand of a delete operator must be a property reference. tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(50,39): error TS2703: The operand of a delete operator must be a property reference. tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(51,32): error TS2703: The operand of a delete operator must be a property reference. @@ -28,7 +25,7 @@ tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperator tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts(57,8): error TS2703: The operand of a delete operator must be a property reference. -==== tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts (28 errors) ==== +==== tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperatorWithAnyOtherType.ts (25 errors) ==== // delete operator on any type var ANY: any; @@ -96,26 +93,20 @@ tests/cases/conformance/expressions/unaryOperators/deleteOperator/deleteOperator ~~~~~~~~~~ !!! error TS2703: The operand of a delete operator must be a property reference. var ResultIsBoolean17 = delete (null + undefined); - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. ~~~~~~~~~~~~~~~~ !!! error TS2703: The operand of a delete operator must be a property reference. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. var ResultIsBoolean18 = delete (null + null); - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. ~~~~~~~~~~~ !!! error TS2703: The operand of a delete operator must be a property reference. - ~~~~ -!!! error TS2531: Object is possibly 'null'. var ResultIsBoolean19 = delete (undefined + undefined); - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. ~~~~~~~~~~~~~~~~~~~~~ !!! error TS2703: The operand of a delete operator must be a property reference. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. // multiple delete operators var ResultIsBoolean20 = delete delete ANY; diff --git a/tests/baselines/reference/duplicatePackage.errors.txt b/tests/baselines/reference/duplicatePackage.errors.txt new file mode 100644 index 00000000000..917ed711c64 --- /dev/null +++ b/tests/baselines/reference/duplicatePackage.errors.txt @@ -0,0 +1,48 @@ +/src/a.ts(5,3): error TS2345: Argument of type 'X' is not assignable to parameter of type 'X'. + Types have separate declarations of a private property 'x'. + + +==== /src/a.ts (1 errors) ==== + import { a } from "a"; + import { b } from "b"; + import { c } from "c"; + a(b); // Works + a(c); // Error, these are from different versions of the library. + ~ +!!! error TS2345: Argument of type 'X' is not assignable to parameter of type 'X'. +!!! error TS2345: Types have separate declarations of a private property 'x'. + +==== /node_modules/a/index.d.ts (0 errors) ==== + import X from "x"; + export function a(x: X): void; + +==== /node_modules/a/node_modules/x/index.d.ts (0 errors) ==== + export default class X { + private x: number; + } + +==== /node_modules/a/node_modules/x/package.json (0 errors) ==== + { "name": "x", "version": "1.2.3" } + +==== /node_modules/b/index.d.ts (0 errors) ==== + import X from "x"; + export const b: X; + +==== /node_modules/b/node_modules/x/index.d.ts (0 errors) ==== + content not parsed + +==== /node_modules/b/node_modules/x/package.json (0 errors) ==== + { "name": "x", "version": "1.2.3" } + +==== /node_modules/c/index.d.ts (0 errors) ==== + import X from "x"; + export const c: X; + +==== /node_modules/c/node_modules/x/index.d.ts (0 errors) ==== + export default class X { + private x: number; + } + +==== /node_modules/c/node_modules/x/package.json (0 errors) ==== + { "name": "x", "version": "1.2.4" } + \ No newline at end of file diff --git a/tests/baselines/reference/duplicatePackage.js b/tests/baselines/reference/duplicatePackage.js new file mode 100644 index 00000000000..ada5c900b93 --- /dev/null +++ b/tests/baselines/reference/duplicatePackage.js @@ -0,0 +1,52 @@ +//// [tests/cases/compiler/duplicatePackage.ts] //// + +//// [index.d.ts] +import X from "x"; +export function a(x: X): void; + +//// [index.d.ts] +export default class X { + private x: number; +} + +//// [package.json] +{ "name": "x", "version": "1.2.3" } + +//// [index.d.ts] +import X from "x"; +export const b: X; + +//// [index.d.ts] +content not parsed + +//// [package.json] +{ "name": "x", "version": "1.2.3" } + +//// [index.d.ts] +import X from "x"; +export const c: X; + +//// [index.d.ts] +export default class X { + private x: number; +} + +//// [package.json] +{ "name": "x", "version": "1.2.4" } + +//// [a.ts] +import { a } from "a"; +import { b } from "b"; +import { c } from "c"; +a(b); // Works +a(c); // Error, these are from different versions of the library. + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var a_1 = require("a"); +var b_1 = require("b"); +var c_1 = require("c"); +a_1.a(b_1.b); // Works +a_1.a(c_1.c); // Error, these are from different versions of the library. diff --git a/tests/baselines/reference/duplicatePackage_withErrors.errors.txt b/tests/baselines/reference/duplicatePackage_withErrors.errors.txt new file mode 100644 index 00000000000..ad24637e983 --- /dev/null +++ b/tests/baselines/reference/duplicatePackage_withErrors.errors.txt @@ -0,0 +1,27 @@ +/node_modules/a/node_modules/x/index.d.ts(1,18): error TS1254: A 'const' initializer in an ambient context must be a string or numeric literal. + + +==== /src/a.ts (0 errors) ==== + import { x as xa } from "a"; + import { x as xb } from "b"; + +==== /node_modules/a/index.d.ts (0 errors) ==== + export { x } from "x"; + +==== /node_modules/a/node_modules/x/index.d.ts (1 errors) ==== + export const x = 1 + 1; + ~~~~~ +!!! error TS1254: A 'const' initializer in an ambient context must be a string or numeric literal. + +==== /node_modules/a/node_modules/x/package.json (0 errors) ==== + { "name": "x", "version": "1.2.3" } + +==== /node_modules/b/index.d.ts (0 errors) ==== + export { x } from "x"; + +==== /node_modules/b/node_modules/x/index.d.ts (0 errors) ==== + content not parsed + +==== /node_modules/b/node_modules/x/package.json (0 errors) ==== + { "name": "x", "version": "1.2.3" } + \ No newline at end of file diff --git a/tests/baselines/reference/duplicatePackage_withErrors.js b/tests/baselines/reference/duplicatePackage_withErrors.js new file mode 100644 index 00000000000..a7fdafd522f --- /dev/null +++ b/tests/baselines/reference/duplicatePackage_withErrors.js @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/duplicatePackage_withErrors.ts] //// + +//// [index.d.ts] +export { x } from "x"; + +//// [index.d.ts] +export const x = 1 + 1; + +//// [package.json] +{ "name": "x", "version": "1.2.3" } + +//// [index.d.ts] +export { x } from "x"; + +//// [index.d.ts] +content not parsed + +//// [package.json] +{ "name": "x", "version": "1.2.3" } + +//// [a.ts] +import { x as xa } from "a"; +import { x as xb } from "b"; + + +//// [a.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/emitter.noCatchBinding.esnext.js b/tests/baselines/reference/emitter.noCatchBinding.esnext.js new file mode 100644 index 00000000000..f47a947ca1a --- /dev/null +++ b/tests/baselines/reference/emitter.noCatchBinding.esnext.js @@ -0,0 +1,22 @@ +//// [emitter.noCatchBinding.esnext.ts] +function f() { + try { } catch { } + try { } catch { + try { } catch { } + } + try { } catch { } finally { } +} + +//// [emitter.noCatchBinding.esnext.js] +function f() { + try { } + catch { } + try { } + catch { + try { } + catch { } + } + try { } + catch { } + finally { } +} diff --git a/tests/baselines/reference/emitter.noCatchBinding.esnext.symbols b/tests/baselines/reference/emitter.noCatchBinding.esnext.symbols new file mode 100644 index 00000000000..91242f5b01c --- /dev/null +++ b/tests/baselines/reference/emitter.noCatchBinding.esnext.symbols @@ -0,0 +1,10 @@ +=== tests/cases/conformance/emitter/esnext/noCatchBinding/emitter.noCatchBinding.esnext.ts === +function f() { +>f : Symbol(f, Decl(emitter.noCatchBinding.esnext.ts, 0, 0)) + + try { } catch { } + try { } catch { + try { } catch { } + } + try { } catch { } finally { } +} diff --git a/tests/baselines/reference/emitter.noCatchBinding.esnext.types b/tests/baselines/reference/emitter.noCatchBinding.esnext.types new file mode 100644 index 00000000000..70c2b728a5d --- /dev/null +++ b/tests/baselines/reference/emitter.noCatchBinding.esnext.types @@ -0,0 +1,10 @@ +=== tests/cases/conformance/emitter/esnext/noCatchBinding/emitter.noCatchBinding.esnext.ts === +function f() { +>f : () => void + + try { } catch { } + try { } catch { + try { } catch { } + } + try { } catch { } finally { } +} diff --git a/tests/baselines/reference/emptyAssignmentPatterns01_ES5.js b/tests/baselines/reference/emptyAssignmentPatterns01_ES5.js index bd7ca293446..82f23d24ea5 100644 --- a/tests/baselines/reference/emptyAssignmentPatterns01_ES5.js +++ b/tests/baselines/reference/emptyAssignmentPatterns01_ES5.js @@ -2,12 +2,15 @@ var a: any; ({} = a); -([] = a); +([] = a); + +var [,] = [1,2]; //// [emptyAssignmentPatterns01_ES5.js] var a; (a); (a); +var _a = [1, 2]; //// [emptyAssignmentPatterns01_ES5.d.ts] diff --git a/tests/baselines/reference/emptyAssignmentPatterns01_ES5.symbols b/tests/baselines/reference/emptyAssignmentPatterns01_ES5.symbols index e8c3f93c013..30676ec4579 100644 --- a/tests/baselines/reference/emptyAssignmentPatterns01_ES5.symbols +++ b/tests/baselines/reference/emptyAssignmentPatterns01_ES5.symbols @@ -8,3 +8,4 @@ var a: any; ([] = a); >a : Symbol(a, Decl(emptyAssignmentPatterns01_ES5.ts, 0, 3)) +var [,] = [1,2]; diff --git a/tests/baselines/reference/emptyAssignmentPatterns01_ES5.types b/tests/baselines/reference/emptyAssignmentPatterns01_ES5.types index b298c7f66ee..a744a190d03 100644 --- a/tests/baselines/reference/emptyAssignmentPatterns01_ES5.types +++ b/tests/baselines/reference/emptyAssignmentPatterns01_ES5.types @@ -14,3 +14,9 @@ var a: any; >[] : undefined[] >a : any +var [,] = [1,2]; +> : undefined +>[1,2] : [number, number] +>1 : 1 +>2 : 2 + diff --git a/tests/baselines/reference/extractMethod/extractMethod1.js b/tests/baselines/reference/extractMethod/extractMethod1.js new file mode 100644 index 00000000000..60c7e301616 --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod1.js @@ -0,0 +1,98 @@ +==ORIGINAL== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + + let y = 5; + let z = x; + a = y; + foo(); + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + + newFunction(); + + function newFunction() { + let y = 5; + let z = x; + a = y; + foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + + a = newFunction(a); + } + + function newFunction(a: number) { + let y = 5; + let z = x; + a = y; + foo(); + return a; + } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + + a = newFunction(a); + } + } + + function newFunction(a: number) { + let y = 5; + let z = x; + a = y; + foo(); + return a; + } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + + a = newFunction(x, a, foo); + } + } +} +function newFunction(x: number, a: number, foo: () => void) { + let y = 5; + let z = x; + a = y; + foo(); + return a; +} diff --git a/tests/baselines/reference/extractMethod/extractMethod10.js b/tests/baselines/reference/extractMethod/extractMethod10.js new file mode 100644 index 00000000000..02923b7ab50 --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod10.js @@ -0,0 +1,55 @@ +==ORIGINAL== +namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + let a1: I = { x: 1 }; + return a1.x + 10; + } + } +} +==SCOPE::class C== +namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + return this.newFunction(); + } + + private newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } + } +} +==SCOPE::namespace A== +namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + return newFunction(); + } + } + + function newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } +} +==SCOPE::file '/a.ts'== +namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + return newFunction(); + } + } +} +function newFunction() { + let a1: A.I = { x: 1 }; + return a1.x + 10; +} diff --git a/tests/baselines/reference/extractMethod/extractMethod11.js b/tests/baselines/reference/extractMethod/extractMethod11.js new file mode 100644 index 00000000000..77565e7b0a7 --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod11.js @@ -0,0 +1,69 @@ +==ORIGINAL== +namespace A { + let y = 1; + class C { + a() { + let z = 1; + let a1 = { x: 1 }; + y = 10; + z = 42; + return a1.x + 10; + } + } +} +==SCOPE::class C== +namespace A { + let y = 1; + class C { + a() { + let z = 1; + var __return: any; + ({ __return, z } = this.newFunction(z)); + return __return; + } + + private newFunction(z: number) { + let a1 = { x: 1 }; + y = 10; + z = 42; + return { __return: a1.x + 10, z }; + } + } +} +==SCOPE::namespace A== +namespace A { + let y = 1; + class C { + a() { + let z = 1; + var __return: any; + ({ __return, z } = newFunction(z)); + return __return; + } + } + + function newFunction(z: number) { + let a1 = { x: 1 }; + y = 10; + z = 42; + return { __return: a1.x + 10, z }; + } +} +==SCOPE::file '/a.ts'== +namespace A { + let y = 1; + class C { + a() { + let z = 1; + var __return: any; + ({ __return, y, z } = newFunction(y, z)); + return __return; + } + } +} +function newFunction(y: number, z: number) { + let a1 = { x: 1 }; + y = 10; + z = 42; + return { __return: a1.x + 10, y, z }; +} diff --git a/tests/baselines/reference/extractMethod/extractMethod12.js b/tests/baselines/reference/extractMethod/extractMethod12.js new file mode 100644 index 00000000000..8ff6e130dad --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod12.js @@ -0,0 +1,36 @@ +==ORIGINAL== +namespace A { + let y = 1; + class C { + b() {} + a() { + let z = 1; + let a1 = { x: 1 }; + y = 10; + z = 42; + this.b(); + return a1.x + 10; + } + } +} +==SCOPE::class C== +namespace A { + let y = 1; + class C { + b() {} + a() { + let z = 1; + var __return: any; + ({ __return, z } = this.newFunction(z)); + return __return; + } + + private newFunction(z: number) { + let a1 = { x: 1 }; + y = 10; + z = 42; + this.b(); + return { __return: a1.x + 10, z }; + } + } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractMethod/extractMethod2.js b/tests/baselines/reference/extractMethod/extractMethod2.js new file mode 100644 index 00000000000..28c27993d71 --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod2.js @@ -0,0 +1,85 @@ +==ORIGINAL== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + + let y = 5; + let z = x; + return foo(); + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + + return newFunction(); + + function newFunction() { + let y = 5; + let z = x; + return foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + + return newFunction(); + } + + function newFunction() { + let y = 5; + let z = x; + return foo(); + } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + + return newFunction(); + } + } + + function newFunction() { + let y = 5; + let z = x; + return foo(); + } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + + return newFunction(x, foo); + } + } +} +function newFunction(x: number, foo: () => void) { + let y = 5; + let z = x; + return foo(); +} diff --git a/tests/baselines/reference/extractMethod/extractMethod3.js b/tests/baselines/reference/extractMethod/extractMethod3.js new file mode 100644 index 00000000000..e5903ead181 --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod3.js @@ -0,0 +1,80 @@ +==ORIGINAL== +namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + + let y = 5; + yield z; + return foo(); + } + } +} +==SCOPE::function a== +namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + + return yield* newFunction(); + + function* newFunction() { + let y = 5; + yield z; + return foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + + return yield* newFunction(z); + } + + function* newFunction(z: number) { + let y = 5; + yield z; + return foo(); + } + } +} +==SCOPE::namespace A== +namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + + return yield* newFunction(z); + } + } + + function* newFunction(z: number) { + let y = 5; + yield z; + return foo(); + } +} +==SCOPE::file '/a.ts'== +namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + + return yield* newFunction(z, foo); + } + } +} +function* newFunction(z: number, foo: () => void) { + let y = 5; + yield z; + return foo(); +} diff --git a/tests/baselines/reference/extractMethod/extractMethod4.js b/tests/baselines/reference/extractMethod/extractMethod4.js new file mode 100644 index 00000000000..6b9e2eed099 --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod4.js @@ -0,0 +1,90 @@ +==ORIGINAL== +namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + + let y = 5; + if (z) { + await z1; + } + return foo(); + } + } +} +==SCOPE::function a== +namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + + return await newFunction(); + + async function newFunction() { + let y = 5; + if(z) { + await z1; + } + return foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + + return await newFunction(z, z1); + } + + async function newFunction(z: number, z1: any) { + let y = 5; + if(z) { + await z1; + } + return foo(); + } + } +} +==SCOPE::namespace A== +namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + + return await newFunction(z, z1); + } + } + + async function newFunction(z: number, z1: any) { + let y = 5; + if(z) { + await z1; + } + return foo(); + } +} +==SCOPE::file '/a.ts'== +namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + + return await newFunction(z, z1, foo); + } + } +} +async function newFunction(z: number, z1: any, foo: () => void) { + let y = 5; + if(z) { + await z1; +} + return foo(); +} diff --git a/tests/baselines/reference/extractMethod/extractMethod5.js b/tests/baselines/reference/extractMethod/extractMethod5.js new file mode 100644 index 00000000000..cf63cb2a932 --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod5.js @@ -0,0 +1,98 @@ +==ORIGINAL== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + let y = 5; + let z = x; + a = y; + foo(); + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + newFunction(); + + function newFunction() { + let y = 5; + let z = x; + a = y; + foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + a = newFunction(a); + } + + function newFunction(a: number) { + let y = 5; + let z = x; + a = y; + foo(); + return a; + } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + a = newFunction(a); + } + } + + function newFunction(a: number) { + let y = 5; + let z = x; + a = y; + foo(); + return a; + } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + a = newFunction(x, a); + } + } +} +function newFunction(x: number, a: number) { + let y = 5; + let z = x; + a = y; + A.foo(); + return a; +} diff --git a/tests/baselines/reference/extractMethod/extractMethod6.js b/tests/baselines/reference/extractMethod/extractMethod6.js new file mode 100644 index 00000000000..99f52e9febb --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod6.js @@ -0,0 +1,101 @@ +==ORIGINAL== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + let y = 5; + let z = x; + a = y; + return foo(); + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + return newFunction(); + + function newFunction() { + let y = 5; + let z = x; + a = y; + return foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ __return, a } = newFunction(a)); + return __return; + } + + function newFunction(a: number) { + let y = 5; + let z = x; + a = y; + return { __return: foo(), a }; + } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ __return, a } = newFunction(a)); + return __return; + } + } + + function newFunction(a: number) { + let y = 5; + let z = x; + a = y; + return { __return: foo(), a }; + } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ __return, a } = newFunction(x, a)); + return __return; + } + } +} +function newFunction(x: number, a: number) { + let y = 5; + let z = x; + a = y; + return { __return: A.foo(), a }; +} diff --git a/tests/baselines/reference/extractMethod/extractMethod7.js b/tests/baselines/reference/extractMethod/extractMethod7.js new file mode 100644 index 00000000000..09d5edaa2c0 --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod7.js @@ -0,0 +1,111 @@ +==ORIGINAL== +namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + + let y = 5; + let z = x; + a = y; + return C.foo(); + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + + return newFunction(); + + function newFunction() { + let y = 5; + let z = x; + a = y; + return C.foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ __return, a } = newFunction(a)); + return __return; + } + + function newFunction(a: number) { + let y = 5; + let z = x; + a = y; + return { __return: C.foo(), a }; + } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ __return, a } = newFunction(a)); + return __return; + } + } + + function newFunction(a: number) { + let y = 5; + let z = x; + a = y; + return { __return: C.foo(), a }; + } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ __return, a } = newFunction(x, a)); + return __return; + } + } +} +function newFunction(x: number, a: number) { + let y = 5; + let z = x; + a = y; + return { __return: A.C.foo(), a }; +} diff --git a/tests/baselines/reference/extractMethod/extractMethod8.js b/tests/baselines/reference/extractMethod/extractMethod8.js new file mode 100644 index 00000000000..fe9cf2a92f0 --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod8.js @@ -0,0 +1,65 @@ +==ORIGINAL== +namespace A { + let x = 1; + namespace B { + function a() { + let a1 = 1; + return 1 + a1 + x + 100; + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + namespace B { + function a() { + let a1 = 1; + return newFunction() + 100; + + function newFunction() { + return 1 + a1 + x; + } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + namespace B { + function a() { + let a1 = 1; + return newFunction(a1) + 100; + } + + function newFunction(a1: number) { + return 1 + a1 + x; + } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + namespace B { + function a() { + let a1 = 1; + return newFunction(a1) + 100; + } + } + + function newFunction(a1: number) { + return 1 + a1 + x; + } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + namespace B { + function a() { + let a1 = 1; + return newFunction(a1, x) + 100; + } + } +} +function newFunction(a1: number, x: number) { + return 1 + a1 + x; +} diff --git a/tests/baselines/reference/extractMethod/extractMethod9.js b/tests/baselines/reference/extractMethod/extractMethod9.js new file mode 100644 index 00000000000..fcc5dcd23de --- /dev/null +++ b/tests/baselines/reference/extractMethod/extractMethod9.js @@ -0,0 +1,65 @@ +==ORIGINAL== +namespace A { + export interface I { x: number }; + namespace B { + function a() { + let a1: I = { x: 1 }; + return a1.x + 10; + } + } +} +==SCOPE::function a== +namespace A { + export interface I { x: number }; + namespace B { + function a() { + return newFunction(); + + function newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } + } + } +} +==SCOPE::namespace B== +namespace A { + export interface I { x: number }; + namespace B { + function a() { + return newFunction(); + } + + function newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } + } +} +==SCOPE::namespace A== +namespace A { + export interface I { x: number }; + namespace B { + function a() { + return newFunction(); + } + } + + function newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } +} +==SCOPE::file '/a.ts'== +namespace A { + export interface I { x: number }; + namespace B { + function a() { + return newFunction(); + } + } +} +function newFunction() { + let a1: A.I = { x: 1 }; + return a1.x + 10; +} diff --git a/tests/baselines/reference/extractMethod1.js b/tests/baselines/reference/extractMethod1.js new file mode 100644 index 00000000000..699916b0dc2 --- /dev/null +++ b/tests/baselines/reference/extractMethod1.js @@ -0,0 +1,98 @@ +==ORIGINAL== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + + let y = 5; + let z = x; + a = y; + foo(); + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + + newFunction(); + + function newFunction() { + let y = 5; + let z = x; + a = y; + foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + + ({ a } = newFunction(a)); + } + + function newFunction(a: any) { + let y = 5; + let z = x; + a = y; + foo(); + return { a }; + } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + + ({ a } = newFunction(a)); + } + } + + function newFunction(a: any) { + let y = 5; + let z = x; + a = y; + foo(); + return { a }; + } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + + ({ a } = newFunction(x, a, foo)); + } + } +} +function newFunction(x: any, a: any, foo: any) { + let y = 5; + let z = x; + a = y; + foo(); + return { a }; +} diff --git a/tests/baselines/reference/extractMethod10.js b/tests/baselines/reference/extractMethod10.js new file mode 100644 index 00000000000..e416a66e262 --- /dev/null +++ b/tests/baselines/reference/extractMethod10.js @@ -0,0 +1,70 @@ +==ORIGINAL== +namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + let a1: I = { x: 1 }; + return a1.x + 10; + } + } +} +==SCOPE::method a== +namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + return newFunction(); + + function newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } + } + } +} +==SCOPE::class C== +namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + return this.newFunction(); + } + + private newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } + } +} +==SCOPE::namespace A== +namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + return newFunction(); + } + } + + function newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } +} +==SCOPE::file '/a.ts'== +namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + return newFunction(); + } + } +} +function newFunction() { + let a1: A.I = { x: 1 }; + return a1.x + 10; +} diff --git a/tests/baselines/reference/extractMethod11.js b/tests/baselines/reference/extractMethod11.js new file mode 100644 index 00000000000..4cbd31d4453 --- /dev/null +++ b/tests/baselines/reference/extractMethod11.js @@ -0,0 +1,86 @@ +==ORIGINAL== +namespace A { + let y = 1; + class C { + a() { + let z = 1; + let a1 = { x: 1 }; + y = 10; + z = 42; + return a1.x + 10; + } + } +} +==SCOPE::method a== +namespace A { + let y = 1; + class C { + a() { + let z = 1; + return newFunction(); + + function newFunction() { + let a1 = { x: 1 }; + y = 10; + z = 42; + return a1.x + 10; + } + } + } +} +==SCOPE::class C== +namespace A { + let y = 1; + class C { + a() { + let z = 1; + var __return: any; + ({ z, __return } = this.newFunction(z)); + return __return; + } + + private newFunction(z: any) { + let a1 = { x: 1 }; + y = 10; + z = 42; + return { z, __return: a1.x + 10 }; + } + } +} +==SCOPE::namespace A== +namespace A { + let y = 1; + class C { + a() { + let z = 1; + var __return: any; + ({ z, __return } = newFunction(z)); + return __return; + } + } + + function newFunction(z: any) { + let a1 = { x: 1 }; + y = 10; + z = 42; + return { z, __return: a1.x + 10 }; + } +} +==SCOPE::file '/a.ts'== +namespace A { + let y = 1; + class C { + a() { + let z = 1; + var __return: any; + ({ y, z, __return } = newFunction(y, z)); + return __return; + } + } +} +function newFunction(y: any, z: any) { + let a1 = { x: 1 }; + y = 10; + z = 42; + return { y, z, __return: a1.x + 10 }; +} diff --git a/tests/baselines/reference/extractMethod12.js b/tests/baselines/reference/extractMethod12.js new file mode 100644 index 00000000000..da0788a937f --- /dev/null +++ b/tests/baselines/reference/extractMethod12.js @@ -0,0 +1,36 @@ +==ORIGINAL== +namespace A { + let y = 1; + class C { + b() {} + a() { + let z = 1; + let a1 = { x: 1 }; + y = 10; + z = 42; + this.b(); + return a1.x + 10; + } + } +} +==SCOPE::class C== +namespace A { + let y = 1; + class C { + b() {} + a() { + let z = 1; + var __return: any; + ({ z, __return } = this.newFunction(z)); + return __return; + } + + private newFunction(z: any) { + let a1 = { x: 1 }; + y = 10; + z = 42; + this.b(); + return { z, __return: a1.x + 10 }; + } + } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractMethod2.js b/tests/baselines/reference/extractMethod2.js new file mode 100644 index 00000000000..a89fe52f2fb --- /dev/null +++ b/tests/baselines/reference/extractMethod2.js @@ -0,0 +1,85 @@ +==ORIGINAL== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + + let y = 5; + let z = x; + return foo(); + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + + return newFunction(); + + function newFunction() { + let y = 5; + let z = x; + return foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + + return newFunction(); + } + + function newFunction() { + let y = 5; + let z = x; + return foo(); + } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + + return newFunction(); + } + } + + function newFunction() { + let y = 5; + let z = x; + return foo(); + } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + + return newFunction(x, foo); + } + } +} +function newFunction(x: any, foo: any) { + let y = 5; + let z = x; + return foo(); +} diff --git a/tests/baselines/reference/extractMethod3.js b/tests/baselines/reference/extractMethod3.js new file mode 100644 index 00000000000..847e196130c --- /dev/null +++ b/tests/baselines/reference/extractMethod3.js @@ -0,0 +1,80 @@ +==ORIGINAL== +namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + + let y = 5; + yield z; + return foo(); + } + } +} +==SCOPE::function a== +namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + + return yield* newFunction(); + + function* newFunction() { + let y = 5; + yield z; + return foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + + return yield* newFunction(z); + } + + function* newFunction(z: any) { + let y = 5; + yield z; + return foo(); + } + } +} +==SCOPE::namespace A== +namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + + return yield* newFunction(z); + } + } + + function* newFunction(z: any) { + let y = 5; + yield z; + return foo(); + } +} +==SCOPE::file '/a.ts'== +namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + + return yield* newFunction(z, foo); + } + } +} +function* newFunction(z: any, foo: any) { + let y = 5; + yield z; + return foo(); +} diff --git a/tests/baselines/reference/extractMethod4.js b/tests/baselines/reference/extractMethod4.js new file mode 100644 index 00000000000..25f410eeecb --- /dev/null +++ b/tests/baselines/reference/extractMethod4.js @@ -0,0 +1,90 @@ +==ORIGINAL== +namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + + let y = 5; + if (z) { + await z1; + } + return foo(); + } + } +} +==SCOPE::function a== +namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + + return await newFunction(); + + async function newFunction() { + let y = 5; + if (z) { + await z1; + } + return foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + + return await newFunction(z, z1); + } + + async function newFunction(z: any, z1: any) { + let y = 5; + if (z) { + await z1; + } + return foo(); + } + } +} +==SCOPE::namespace A== +namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + + return await newFunction(z, z1); + } + } + + async function newFunction(z: any, z1: any) { + let y = 5; + if (z) { + await z1; + } + return foo(); + } +} +==SCOPE::file '/a.ts'== +namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + + return await newFunction(z, z1, foo); + } + } +} +async function newFunction(z: any, z1: any, foo: any) { + let y = 5; + if (z) { + await z1; + } + return foo(); +} diff --git a/tests/baselines/reference/extractMethod5.js b/tests/baselines/reference/extractMethod5.js new file mode 100644 index 00000000000..135c4ed5f62 --- /dev/null +++ b/tests/baselines/reference/extractMethod5.js @@ -0,0 +1,98 @@ +==ORIGINAL== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + let y = 5; + let z = x; + a = y; + foo(); + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + newFunction(); + + function newFunction() { + let y = 5; + let z = x; + a = y; + foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + ({ a } = newFunction(a)); + } + + function newFunction(a: any) { + let y = 5; + let z = x; + a = y; + foo(); + return { a }; + } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + ({ a } = newFunction(a)); + } + } + + function newFunction(a: any) { + let y = 5; + let z = x; + a = y; + foo(); + return { a }; + } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + ({ a } = newFunction(x, a)); + } + } +} +function newFunction(x: any, a: any) { + let y = 5; + let z = x; + a = y; + A.foo(); + return { a }; +} diff --git a/tests/baselines/reference/extractMethod6.js b/tests/baselines/reference/extractMethod6.js new file mode 100644 index 00000000000..94fcc8e6e31 --- /dev/null +++ b/tests/baselines/reference/extractMethod6.js @@ -0,0 +1,101 @@ +==ORIGINAL== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + let y = 5; + let z = x; + a = y; + return foo(); + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + return newFunction(); + + function newFunction() { + let y = 5; + let z = x; + a = y; + return foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ a, __return } = newFunction(a)); + return __return; + } + + function newFunction(a: any) { + let y = 5; + let z = x; + a = y; + return { a, __return: foo() }; + } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ a, __return } = newFunction(a)); + return __return; + } + } + + function newFunction(a: any) { + let y = 5; + let z = x; + a = y; + return { a, __return: foo() }; + } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ a, __return } = newFunction(x, a)); + return __return; + } + } +} +function newFunction(x: any, a: any) { + let y = 5; + let z = x; + a = y; + return { a, __return: A.foo() }; +} diff --git a/tests/baselines/reference/extractMethod7.js b/tests/baselines/reference/extractMethod7.js new file mode 100644 index 00000000000..c7c3cc8d77a --- /dev/null +++ b/tests/baselines/reference/extractMethod7.js @@ -0,0 +1,111 @@ +==ORIGINAL== +namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + + let y = 5; + let z = x; + a = y; + return C.foo(); + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + + return newFunction(); + + function newFunction() { + let y = 5; + let z = x; + a = y; + return C.foo(); + } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ a, __return } = newFunction(a)); + return __return; + } + + function newFunction(a: any) { + let y = 5; + let z = x; + a = y; + return { a, __return: C.foo() }; + } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ a, __return } = newFunction(a)); + return __return; + } + } + + function newFunction(a: any) { + let y = 5; + let z = x; + a = y; + return { a, __return: C.foo() }; + } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + + var __return: any; + ({ a, __return } = newFunction(x, a)); + return __return; + } + } +} +function newFunction(x: any, a: any) { + let y = 5; + let z = x; + a = y; + return { a, __return: A.C.foo() }; +} diff --git a/tests/baselines/reference/extractMethod8.js b/tests/baselines/reference/extractMethod8.js new file mode 100644 index 00000000000..f030b0ea4af --- /dev/null +++ b/tests/baselines/reference/extractMethod8.js @@ -0,0 +1,57 @@ +==ORIGINAL== +namespace A { + let x = 1; + namespace B { + function a() { + let a1 = 1; + return 1 + a1 + x + 100; + } + } +} +==SCOPE::function a== +namespace A { + let x = 1; + namespace B { + function a() { + let a1 = 1; + return newFunction() + 100; + + function newFunction() { 1 + a1 + x; } + } + } +} +==SCOPE::namespace B== +namespace A { + let x = 1; + namespace B { + function a() { + let a1 = 1; + return newFunction(a1) + 100; + } + + function newFunction(a1: any) { 1 + a1 + x; } + } +} +==SCOPE::namespace A== +namespace A { + let x = 1; + namespace B { + function a() { + let a1 = 1; + return newFunction(a1) + 100; + } + } + + function newFunction(a1: any) { 1 + a1 + x; } +} +==SCOPE::file '/a.ts'== +namespace A { + let x = 1; + namespace B { + function a() { + let a1 = 1; + return newFunction(a1, x) + 100; + } + } +} +function newFunction(a1: any, x: any) { 1 + a1 + x; } diff --git a/tests/baselines/reference/extractMethod9.js b/tests/baselines/reference/extractMethod9.js new file mode 100644 index 00000000000..fcc5dcd23de --- /dev/null +++ b/tests/baselines/reference/extractMethod9.js @@ -0,0 +1,65 @@ +==ORIGINAL== +namespace A { + export interface I { x: number }; + namespace B { + function a() { + let a1: I = { x: 1 }; + return a1.x + 10; + } + } +} +==SCOPE::function a== +namespace A { + export interface I { x: number }; + namespace B { + function a() { + return newFunction(); + + function newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } + } + } +} +==SCOPE::namespace B== +namespace A { + export interface I { x: number }; + namespace B { + function a() { + return newFunction(); + } + + function newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } + } +} +==SCOPE::namespace A== +namespace A { + export interface I { x: number }; + namespace B { + function a() { + return newFunction(); + } + } + + function newFunction() { + let a1: I = { x: 1 }; + return a1.x + 10; + } +} +==SCOPE::file '/a.ts'== +namespace A { + export interface I { x: number }; + namespace B { + function a() { + return newFunction(); + } + } +} +function newFunction() { + let a1: A.I = { x: 1 }; + return a1.x + 10; +} diff --git a/tests/baselines/reference/functionWithDefaultParameterWithNoStatements4.types b/tests/baselines/reference/functionWithDefaultParameterWithNoStatements4.types index bfc627fa60a..3e6c4f65f43 100644 --- a/tests/baselines/reference/functionWithDefaultParameterWithNoStatements4.types +++ b/tests/baselines/reference/functionWithDefaultParameterWithNoStatements4.types @@ -2,10 +2,10 @@ function foo(a = ``) { } >foo : (a?: string) => void >a : string ->`` : string +>`` : "" function bar(a = ``) { >bar : (a?: string) => void >a : string ->`` : string +>`` : "" } diff --git a/tests/baselines/reference/importCallExpressionAsyncES3System.types b/tests/baselines/reference/importCallExpressionAsyncES3System.types index 4f6a2bb31be..90c981c01ca 100644 --- a/tests/baselines/reference/importCallExpressionAsyncES3System.types +++ b/tests/baselines/reference/importCallExpressionAsyncES3System.types @@ -3,9 +3,9 @@ export async function fn() { >fn : () => Promise const req = await import('./test') // ONE ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } @@ -16,9 +16,9 @@ export class cl1 { >m : () => Promise const req = await import('./test') // TWO ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } } @@ -32,9 +32,9 @@ export const obj = { >async () => { const req = await import('./test') // THREE } : () => Promise const req = await import('./test') // THREE ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } } @@ -51,9 +51,9 @@ export class cl2 { >async () => { const req = await import('./test') // FOUR } : () => Promise const req = await import('./test') // FOUR ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } } @@ -64,9 +64,9 @@ export const l = async () => { >async () => { const req = await import('./test') // FIVE} : () => Promise const req = await import('./test') // FIVE ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } diff --git a/tests/baselines/reference/importCallExpressionAsyncES5System.types b/tests/baselines/reference/importCallExpressionAsyncES5System.types index 4f6a2bb31be..90c981c01ca 100644 --- a/tests/baselines/reference/importCallExpressionAsyncES5System.types +++ b/tests/baselines/reference/importCallExpressionAsyncES5System.types @@ -3,9 +3,9 @@ export async function fn() { >fn : () => Promise const req = await import('./test') // ONE ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } @@ -16,9 +16,9 @@ export class cl1 { >m : () => Promise const req = await import('./test') // TWO ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } } @@ -32,9 +32,9 @@ export const obj = { >async () => { const req = await import('./test') // THREE } : () => Promise const req = await import('./test') // THREE ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } } @@ -51,9 +51,9 @@ export class cl2 { >async () => { const req = await import('./test') // FOUR } : () => Promise const req = await import('./test') // FOUR ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } } @@ -64,9 +64,9 @@ export const l = async () => { >async () => { const req = await import('./test') // FIVE} : () => Promise const req = await import('./test') // FIVE ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } diff --git a/tests/baselines/reference/importCallExpressionAsyncES6System.types b/tests/baselines/reference/importCallExpressionAsyncES6System.types index 4f6a2bb31be..90c981c01ca 100644 --- a/tests/baselines/reference/importCallExpressionAsyncES6System.types +++ b/tests/baselines/reference/importCallExpressionAsyncES6System.types @@ -3,9 +3,9 @@ export async function fn() { >fn : () => Promise const req = await import('./test') // ONE ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } @@ -16,9 +16,9 @@ export class cl1 { >m : () => Promise const req = await import('./test') // TWO ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } } @@ -32,9 +32,9 @@ export const obj = { >async () => { const req = await import('./test') // THREE } : () => Promise const req = await import('./test') // THREE ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } } @@ -51,9 +51,9 @@ export class cl2 { >async () => { const req = await import('./test') // FOUR } : () => Promise const req = await import('./test') // FOUR ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } } @@ -64,9 +64,9 @@ export const l = async () => { >async () => { const req = await import('./test') // FIVE} : () => Promise const req = await import('./test') // FIVE ->req : typeof "tests/cases/conformance/dynamicImport/test" ->await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" ->import('./test') : Promise +>req : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>await import('./test') : typeof "tests/cases/conformance/dynamicImport/test" & { default: typeof "tests/cases/conformance/dynamicImport/test"; } +>import('./test') : Promise >'./test' : "./test" } diff --git a/tests/baselines/reference/importCallExpressionES5System.types b/tests/baselines/reference/importCallExpressionES5System.types index e97f722b14f..02bd64142d4 100644 --- a/tests/baselines/reference/importCallExpressionES5System.types +++ b/tests/baselines/reference/importCallExpressionES5System.types @@ -5,41 +5,41 @@ export function foo() { return "foo"; } === tests/cases/conformance/dynamicImport/1.ts === import("./0"); ->import("./0") : Promise +>import("./0") : Promise >"./0" : "./0" var p1 = import("./0"); ->p1 : Promise ->import("./0") : Promise +>p1 : Promise +>import("./0") : Promise >"./0" : "./0" p1.then(zero => { >p1.then(zero => { return zero.foo();}) : Promise ->p1.then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0") => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise ->p1 : Promise ->then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0") => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise ->zero => { return zero.foo();} : (zero: typeof "tests/cases/conformance/dynamicImport/0") => string ->zero : typeof "tests/cases/conformance/dynamicImport/0" +>p1.then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>p1 : Promise +>then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>zero => { return zero.foo();} : (zero: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => string +>zero : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } return zero.foo(); >zero.foo() : string >zero.foo : () => string ->zero : typeof "tests/cases/conformance/dynamicImport/0" +>zero : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } >foo : () => string }); export var p2 = import("./0"); ->p2 : Promise ->import("./0") : Promise +>p2 : Promise +>import("./0") : Promise >"./0" : "./0" function foo() { >foo : () => void const p2 = import("./0"); ->p2 : Promise ->import("./0") : Promise +>p2 : Promise +>import("./0") : Promise >"./0" : "./0" } @@ -50,8 +50,8 @@ class C { >method : () => void const loadAsync = import ("./0"); ->loadAsync : Promise ->import ("./0") : Promise +>loadAsync : Promise +>import ("./0") : Promise >"./0" : "./0" } } @@ -63,8 +63,8 @@ export class D { >method : () => void const loadAsync = import ("./0"); ->loadAsync : Promise ->import ("./0") : Promise +>loadAsync : Promise +>import ("./0") : Promise >"./0" : "./0" } } diff --git a/tests/baselines/reference/importCallExpressionES6System.types b/tests/baselines/reference/importCallExpressionES6System.types index e97f722b14f..02bd64142d4 100644 --- a/tests/baselines/reference/importCallExpressionES6System.types +++ b/tests/baselines/reference/importCallExpressionES6System.types @@ -5,41 +5,41 @@ export function foo() { return "foo"; } === tests/cases/conformance/dynamicImport/1.ts === import("./0"); ->import("./0") : Promise +>import("./0") : Promise >"./0" : "./0" var p1 = import("./0"); ->p1 : Promise ->import("./0") : Promise +>p1 : Promise +>import("./0") : Promise >"./0" : "./0" p1.then(zero => { >p1.then(zero => { return zero.foo();}) : Promise ->p1.then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0") => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise ->p1 : Promise ->then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0") => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise ->zero => { return zero.foo();} : (zero: typeof "tests/cases/conformance/dynamicImport/0") => string ->zero : typeof "tests/cases/conformance/dynamicImport/0" +>p1.then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>p1 : Promise +>then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>zero => { return zero.foo();} : (zero: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => string +>zero : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } return zero.foo(); >zero.foo() : string >zero.foo : () => string ->zero : typeof "tests/cases/conformance/dynamicImport/0" +>zero : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } >foo : () => string }); export var p2 = import("./0"); ->p2 : Promise ->import("./0") : Promise +>p2 : Promise +>import("./0") : Promise >"./0" : "./0" function foo() { >foo : () => void const p2 = import("./0"); ->p2 : Promise ->import("./0") : Promise +>p2 : Promise +>import("./0") : Promise >"./0" : "./0" } @@ -50,8 +50,8 @@ class C { >method : () => void const loadAsync = import ("./0"); ->loadAsync : Promise ->import ("./0") : Promise +>loadAsync : Promise +>import ("./0") : Promise >"./0" : "./0" } } @@ -63,8 +63,8 @@ export class D { >method : () => void const loadAsync = import ("./0"); ->loadAsync : Promise ->import ("./0") : Promise +>loadAsync : Promise +>import ("./0") : Promise >"./0" : "./0" } } diff --git a/tests/baselines/reference/importCallExpressionInSystem1.types b/tests/baselines/reference/importCallExpressionInSystem1.types index 661d27d1469..a82d8176753 100644 --- a/tests/baselines/reference/importCallExpressionInSystem1.types +++ b/tests/baselines/reference/importCallExpressionInSystem1.types @@ -5,40 +5,40 @@ export function foo() { return "foo"; } === tests/cases/conformance/dynamicImport/1.ts === import("./0"); ->import("./0") : Promise +>import("./0") : Promise >"./0" : "./0" var p1 = import("./0"); ->p1 : Promise ->import("./0") : Promise +>p1 : Promise +>import("./0") : Promise >"./0" : "./0" p1.then(zero => { >p1.then(zero => { return zero.foo();}) : Promise ->p1.then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0") => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise ->p1 : Promise ->then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0") => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise ->zero => { return zero.foo();} : (zero: typeof "tests/cases/conformance/dynamicImport/0") => string ->zero : typeof "tests/cases/conformance/dynamicImport/0" +>p1.then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>p1 : Promise +>then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>zero => { return zero.foo();} : (zero: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => string +>zero : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } return zero.foo(); >zero.foo() : string >zero.foo : () => string ->zero : typeof "tests/cases/conformance/dynamicImport/0" +>zero : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } >foo : () => string }); export var p2 = import("./0"); ->p2 : Promise ->import("./0") : Promise +>p2 : Promise +>import("./0") : Promise >"./0" : "./0" function foo() { >foo : () => void const p2 = import("./0"); ->p2 : Promise ->import("./0") : Promise +>p2 : Promise +>import("./0") : Promise >"./0" : "./0" } diff --git a/tests/baselines/reference/importCallExpressionInSystem2.types b/tests/baselines/reference/importCallExpressionInSystem2.types index 44b17eb51fd..160da81b214 100644 --- a/tests/baselines/reference/importCallExpressionInSystem2.types +++ b/tests/baselines/reference/importCallExpressionInSystem2.types @@ -41,6 +41,6 @@ function foo(x: Promise) { foo(import("./0")); >foo(import("./0")) : void >foo : (x: Promise) => void ->import("./0") : Promise +>import("./0") : Promise >"./0" : "./0" diff --git a/tests/baselines/reference/importCallExpressionInSystem3.types b/tests/baselines/reference/importCallExpressionInSystem3.types index e517be6e722..08bf03fb506 100644 --- a/tests/baselines/reference/importCallExpressionInSystem3.types +++ b/tests/baselines/reference/importCallExpressionInSystem3.types @@ -14,9 +14,9 @@ async function foo() { class C extends (await import("./0")).B {} >C : C >(await import("./0")).B : B ->(await import("./0")) : typeof "tests/cases/conformance/dynamicImport/0" ->await import("./0") : typeof "tests/cases/conformance/dynamicImport/0" ->import("./0") : Promise +>(await import("./0")) : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } +>await import("./0") : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } +>import("./0") : Promise >"./0" : "./0" >B : typeof B diff --git a/tests/baselines/reference/importCallExpressionInSystem4.types b/tests/baselines/reference/importCallExpressionInSystem4.types index 156247851c9..c9f2b2e5211 100644 --- a/tests/baselines/reference/importCallExpressionInSystem4.types +++ b/tests/baselines/reference/importCallExpressionInSystem4.types @@ -24,27 +24,27 @@ class C { >C : C private myModule = import("./0"); ->myModule : Promise ->import("./0") : Promise +>myModule : Promise +>import("./0") : Promise >"./0" : "./0" method() { >method : () => void const loadAsync = import("./0"); ->loadAsync : Promise ->import("./0") : Promise +>loadAsync : Promise +>import("./0") : Promise >"./0" : "./0" this.myModule.then(Zero => { >this.myModule.then(Zero => { console.log(Zero.foo()); }, async err => { console.log(err); let one = await import("./1"); console.log(one.backup()); }) : Promise ->this.myModule.then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0") => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise ->this.myModule : Promise +>this.myModule.then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>this.myModule : Promise >this : this ->myModule : Promise ->then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0") => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise ->Zero => { console.log(Zero.foo()); } : (Zero: typeof "tests/cases/conformance/dynamicImport/0") => void ->Zero : typeof "tests/cases/conformance/dynamicImport/0" +>myModule : Promise +>then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>Zero => { console.log(Zero.foo()); } : (Zero: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => void +>Zero : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } console.log(Zero.foo()); >console.log(Zero.foo()) : any @@ -53,7 +53,7 @@ class C { >log : any >Zero.foo() : string >Zero.foo : () => string ->Zero : typeof "tests/cases/conformance/dynamicImport/0" +>Zero : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } >foo : () => string }, async err => { @@ -68,9 +68,9 @@ class C { >err : any let one = await import("./1"); ->one : typeof "tests/cases/conformance/dynamicImport/1" ->await import("./1") : typeof "tests/cases/conformance/dynamicImport/1" ->import("./1") : Promise +>one : typeof "tests/cases/conformance/dynamicImport/1" & { default: typeof "tests/cases/conformance/dynamicImport/1"; } +>await import("./1") : typeof "tests/cases/conformance/dynamicImport/1" & { default: typeof "tests/cases/conformance/dynamicImport/1"; } +>import("./1") : Promise >"./1" : "./1" console.log(one.backup()); @@ -80,7 +80,7 @@ class C { >log : any >one.backup() : string >one.backup : () => string ->one : typeof "tests/cases/conformance/dynamicImport/1" +>one : typeof "tests/cases/conformance/dynamicImport/1" & { default: typeof "tests/cases/conformance/dynamicImport/1"; } >backup : () => string }); @@ -91,27 +91,27 @@ export class D { >D : D private myModule = import("./0"); ->myModule : Promise ->import("./0") : Promise +>myModule : Promise +>import("./0") : Promise >"./0" : "./0" method() { >method : () => void const loadAsync = import("./0"); ->loadAsync : Promise ->import("./0") : Promise +>loadAsync : Promise +>import("./0") : Promise >"./0" : "./0" this.myModule.then(Zero => { >this.myModule.then(Zero => { console.log(Zero.foo()); }, async err => { console.log(err); let one = await import("./1"); console.log(one.backup()); }) : Promise ->this.myModule.then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0") => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise ->this.myModule : Promise +>this.myModule.then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>this.myModule : Promise >this : this ->myModule : Promise ->then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0") => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise ->Zero => { console.log(Zero.foo()); } : (Zero: typeof "tests/cases/conformance/dynamicImport/0") => void ->Zero : typeof "tests/cases/conformance/dynamicImport/0" +>myModule : Promise +>then : (onfulfilled?: (value: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>Zero => { console.log(Zero.foo()); } : (Zero: typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; }) => void +>Zero : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } console.log(Zero.foo()); >console.log(Zero.foo()) : any @@ -120,7 +120,7 @@ export class D { >log : any >Zero.foo() : string >Zero.foo : () => string ->Zero : typeof "tests/cases/conformance/dynamicImport/0" +>Zero : typeof "tests/cases/conformance/dynamicImport/0" & { default: typeof "tests/cases/conformance/dynamicImport/0"; } >foo : () => string }, async err => { @@ -135,9 +135,9 @@ export class D { >err : any let one = await import("./1"); ->one : typeof "tests/cases/conformance/dynamicImport/1" ->await import("./1") : typeof "tests/cases/conformance/dynamicImport/1" ->import("./1") : Promise +>one : typeof "tests/cases/conformance/dynamicImport/1" & { default: typeof "tests/cases/conformance/dynamicImport/1"; } +>await import("./1") : typeof "tests/cases/conformance/dynamicImport/1" & { default: typeof "tests/cases/conformance/dynamicImport/1"; } +>import("./1") : Promise >"./1" : "./1" console.log(one.backup()); @@ -147,7 +147,7 @@ export class D { >log : any >one.backup() : string >one.backup : () => string ->one : typeof "tests/cases/conformance/dynamicImport/1" +>one : typeof "tests/cases/conformance/dynamicImport/1" & { default: typeof "tests/cases/conformance/dynamicImport/1"; } >backup : () => string }); diff --git a/tests/baselines/reference/incrementOperatorWithAnyOtherTypeInvalidOperations.errors.txt b/tests/baselines/reference/incrementOperatorWithAnyOtherTypeInvalidOperations.errors.txt index afdddab86a7..690f3391933 100644 --- a/tests/baselines/reference/incrementOperatorWithAnyOtherTypeInvalidOperations.errors.txt +++ b/tests/baselines/reference/incrementOperatorWithAnyOtherTypeInvalidOperations.errors.txt @@ -19,27 +19,21 @@ tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOp tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(46,26): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(47,26): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(48,26): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(48,27): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(48,34): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(48,27): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(49,26): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(49,27): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(49,34): error TS2531: Object is possibly 'null'. +tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(49,27): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(50,26): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(50,27): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(50,39): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(50,27): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(51,26): error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(52,26): error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(54,24): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(55,24): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(56,24): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(56,25): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(56,32): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(56,25): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(57,24): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(57,25): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(57,32): error TS2531: Object is possibly 'null'. +tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(57,25): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(58,24): error TS2357: The operand of an increment or decrement operator must be a variable or a property access. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(58,25): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(58,37): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(58,25): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(59,24): error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(60,24): error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(63,3): error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. @@ -53,7 +47,7 @@ tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOp tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts(69,12): error TS1109: Expression expected. -==== tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts (53 errors) ==== +==== tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOperatorWithAnyOtherTypeInvalidOperations.ts (47 errors) ==== // ++ operator on any type var ANY1: any; var ANY2: any[] = [1, 2]; @@ -144,24 +138,18 @@ tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOp var ResultIsNumber19 = ++(null + undefined); ~~~~~~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. var ResultIsNumber20 = ++(null + null); ~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. var ResultIsNumber21 = ++(undefined + undefined); ~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. var ResultIsNumber22 = ++obj1.x; ~~~~~~ !!! error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. @@ -178,24 +166,18 @@ tests/cases/conformance/expressions/unaryOperators/incrementOperator/incrementOp var ResultIsNumber26 = (null + undefined)++; ~~~~~~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. var ResultIsNumber27 = (null + null)++; ~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. var ResultIsNumber28 = (undefined + undefined)++; ~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2357: The operand of an increment or decrement operator must be a variable or a property access. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. var ResultIsNumber29 = obj1.x++; ~~~~~~ !!! error TS2356: An arithmetic operand must be of type 'any', 'number' or an enum type. diff --git a/tests/baselines/reference/indexSignatureAndMappedType.errors.txt b/tests/baselines/reference/indexSignatureAndMappedType.errors.txt new file mode 100644 index 00000000000..623fcd11bd0 --- /dev/null +++ b/tests/baselines/reference/indexSignatureAndMappedType.errors.txt @@ -0,0 +1,47 @@ +tests/cases/compiler/indexSignatureAndMappedType.ts(6,5): error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record'. +tests/cases/compiler/indexSignatureAndMappedType.ts(15,5): error TS2322: Type 'Record' is not assignable to type '{ [key: string]: T; }'. + Type 'U' is not assignable to type 'T'. +tests/cases/compiler/indexSignatureAndMappedType.ts(16,5): error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record'. + + +==== tests/cases/compiler/indexSignatureAndMappedType.ts (3 errors) ==== + // A mapped type { [P in K]: X }, where K is a generic type, is related to + // { [key: string]: Y } if X is related to Y. + + function f1(x: { [key: string]: T }, y: Record) { + x = y; + y = x; // Error + ~ +!!! error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record'. + } + + function f2(x: { [key: string]: T }, y: Record) { + x = y; + y = x; + } + + function f3(x: { [key: string]: T }, y: Record) { + x = y; // Error + ~ +!!! error TS2322: Type 'Record' is not assignable to type '{ [key: string]: T; }'. +!!! error TS2322: Type 'U' is not assignable to type 'T'. + y = x; // Error + ~ +!!! error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record'. + } + + // Repro from #14548 + + type Dictionary = { + [key: string]: string; + }; + + interface IBaseEntity { + name: string; + properties: Dictionary; + } + + interface IEntity extends IBaseEntity { + properties: Record; + } + \ No newline at end of file diff --git a/tests/baselines/reference/indexSignatureAndMappedType.js b/tests/baselines/reference/indexSignatureAndMappedType.js new file mode 100644 index 00000000000..be58286fb46 --- /dev/null +++ b/tests/baselines/reference/indexSignatureAndMappedType.js @@ -0,0 +1,73 @@ +//// [indexSignatureAndMappedType.ts] +// A mapped type { [P in K]: X }, where K is a generic type, is related to +// { [key: string]: Y } if X is related to Y. + +function f1(x: { [key: string]: T }, y: Record) { + x = y; + y = x; // Error +} + +function f2(x: { [key: string]: T }, y: Record) { + x = y; + y = x; +} + +function f3(x: { [key: string]: T }, y: Record) { + x = y; // Error + y = x; // Error +} + +// Repro from #14548 + +type Dictionary = { + [key: string]: string; +}; + +interface IBaseEntity { + name: string; + properties: Dictionary; +} + +interface IEntity extends IBaseEntity { + properties: Record; +} + + +//// [indexSignatureAndMappedType.js] +"use strict"; +// A mapped type { [P in K]: X }, where K is a generic type, is related to +// { [key: string]: Y } if X is related to Y. +function f1(x, y) { + x = y; + y = x; // Error +} +function f2(x, y) { + x = y; + y = x; +} +function f3(x, y) { + x = y; // Error + y = x; // Error +} + + +//// [indexSignatureAndMappedType.d.ts] +declare function f1(x: { + [key: string]: T; +}, y: Record): void; +declare function f2(x: { + [key: string]: T; +}, y: Record): void; +declare function f3(x: { + [key: string]: T; +}, y: Record): void; +declare type Dictionary = { + [key: string]: string; +}; +interface IBaseEntity { + name: string; + properties: Dictionary; +} +interface IEntity extends IBaseEntity { + properties: Record; +} diff --git a/tests/baselines/reference/invalidThisEmitInContextualObjectLiteral.types b/tests/baselines/reference/invalidThisEmitInContextualObjectLiteral.types index 290b0a78eb2..8cb32eb84bd 100644 --- a/tests/baselines/reference/invalidThisEmitInContextualObjectLiteral.types +++ b/tests/baselines/reference/invalidThisEmitInContextualObjectLiteral.types @@ -25,7 +25,7 @@ class TestController { >this.m : (def: IDef) => void >this : this >m : (def: IDef) => void ->{ p1: e => { }, p2: () => { return vvvvvvvvv => this; }, } : { p1: (e: string) => void; p2: () => {}; } +>{ p1: e => { }, p2: () => { return vvvvvvvvv => this; }, } : { p1: (e: string) => void; p2: () => (vvvvvvvvv: number) => this; } p1: e => { }, >p1 : (e: string) => void @@ -33,8 +33,8 @@ class TestController { >e : string p2: () => { return vvvvvvvvv => this; }, ->p2 : () => {} ->() => { return vvvvvvvvv => this; } : () => {} +>p2 : () => (vvvvvvvvv: number) => this +>() => { return vvvvvvvvv => this; } : () => (vvvvvvvvv: number) => this >vvvvvvvvv => this : (vvvvvvvvv: number) => this >vvvvvvvvv : number >this : this diff --git a/tests/baselines/reference/invalidTryStatements2.errors.txt b/tests/baselines/reference/invalidTryStatements2.errors.txt index f2e88cfdc22..a7c436236f2 100644 --- a/tests/baselines/reference/invalidTryStatements2.errors.txt +++ b/tests/baselines/reference/invalidTryStatements2.errors.txt @@ -1,24 +1,23 @@ -tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(3,13): error TS1005: '(' expected. -tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(6,5): error TS1005: 'try' expected. -tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(12,5): error TS1005: 'try' expected. -tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(13,5): error TS1005: 'try' expected. -tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(22,5): error TS1005: 'try' expected. -tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(26,5): error TS1005: 'try' expected. +tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(2,5): error TS1005: 'try' expected. +tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(6,12): error TS1005: 'finally' expected. +tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(10,5): error TS1005: 'try' expected. +tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(11,5): error TS1005: 'try' expected. +tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(15,5): error TS1005: 'try' expected. +tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(17,5): error TS1005: 'try' expected. +tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(19,20): error TS1003: Identifier expected. -==== tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts (6 errors) ==== +==== tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts (7 errors) ==== function fn() { - try { - } catch { // syntax error, missing '(x)' - ~ -!!! error TS1005: '(' expected. - } - catch(x) { } // error missing try ~~~~~ !!! error TS1005: 'try' expected. - finally{ } // potential error; can be absorbed by the 'catch' + finally { } // potential error; can be absorbed by the 'catch' + + try { }; // error missing finally + ~ +!!! error TS1005: 'finally' expected. } function fn2() { @@ -28,22 +27,18 @@ tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts(26,5): catch (x) { } // error missing try ~~~~~ !!! error TS1005: 'try' expected. + + try { } finally { } // statement is here, so the 'catch' clause above doesn't absorb errors from the 'finally' clause below - // no error - try { - } - finally { - } - - // error missing try - finally { + finally { } // error missing try ~~~~~~~ !!! error TS1005: 'try' expected. - } - - // error missing try - catch (x) { + + catch (x) { } // error missing try ~~~~~ !!! error TS1005: 'try' expected. - } + + try { } catch () { } // error missing catch binding + ~ +!!! error TS1003: Identifier expected. } \ No newline at end of file diff --git a/tests/baselines/reference/invalidTryStatements2.js b/tests/baselines/reference/invalidTryStatements2.js index 118b607817a..50aff00c515 100644 --- a/tests/baselines/reference/invalidTryStatements2.js +++ b/tests/baselines/reference/invalidTryStatements2.js @@ -1,43 +1,34 @@ //// [invalidTryStatements2.ts] function fn() { - try { - } catch { // syntax error, missing '(x)' - } - catch(x) { } // error missing try - finally{ } // potential error; can be absorbed by the 'catch' + finally { } // potential error; can be absorbed by the 'catch' + + try { }; // error missing finally } function fn2() { finally { } // error missing try catch (x) { } // error missing try + + try { } finally { } // statement is here, so the 'catch' clause above doesn't absorb errors from the 'finally' clause below - // no error - try { - } - finally { - } + finally { } // error missing try + + catch (x) { } // error missing try - // error missing try - finally { - } - - // error missing try - catch (x) { - } + try { } catch () { } // error missing catch binding } //// [invalidTryStatements2.js] function fn() { - try { - } - catch () { - } try { } catch (x) { } // error missing try finally { } // potential error; can be absorbed by the 'catch' + try { } + finally { } + ; // error missing finally } function fn2() { try { @@ -46,19 +37,14 @@ function fn2() { try { } catch (x) { } // error missing try - // no error + try { } + finally { } // statement is here, so the 'catch' clause above doesn't absorb errors from the 'finally' clause below try { } - finally { - } - // error missing try + finally { } // error missing try try { } - finally { - } - // error missing try - try { - } - catch (x) { - } + catch (x) { } // error missing try + try { } + catch () { } // error missing catch binding } diff --git a/tests/baselines/reference/jsdocCastCommentEmit.js b/tests/baselines/reference/jsdocCastCommentEmit.js new file mode 100644 index 00000000000..d071f8f6b2b --- /dev/null +++ b/tests/baselines/reference/jsdocCastCommentEmit.js @@ -0,0 +1,17 @@ +//// [jsdocCastCommentEmit.ts] +// allowJs: true +// checkJs: true +// outDir: out/ +// filename: input.js +function f() { + return /* @type {number} */ 42; +} + +//// [jsdocCastCommentEmit.js] +// allowJs: true +// checkJs: true +// outDir: out/ +// filename: input.js +function f() { + return /* @type {number} */ 42; +} diff --git a/tests/baselines/reference/jsdocCastCommentEmit.symbols b/tests/baselines/reference/jsdocCastCommentEmit.symbols new file mode 100644 index 00000000000..5490315bc19 --- /dev/null +++ b/tests/baselines/reference/jsdocCastCommentEmit.symbols @@ -0,0 +1,10 @@ +=== tests/cases/compiler/jsdocCastCommentEmit.ts === +// allowJs: true +// checkJs: true +// outDir: out/ +// filename: input.js +function f() { +>f : Symbol(f, Decl(jsdocCastCommentEmit.ts, 0, 0)) + + return /* @type {number} */ 42; +} diff --git a/tests/baselines/reference/jsdocCastCommentEmit.types b/tests/baselines/reference/jsdocCastCommentEmit.types new file mode 100644 index 00000000000..3d4c3e47e46 --- /dev/null +++ b/tests/baselines/reference/jsdocCastCommentEmit.types @@ -0,0 +1,11 @@ +=== tests/cases/compiler/jsdocCastCommentEmit.ts === +// allowJs: true +// checkJs: true +// outDir: out/ +// filename: input.js +function f() { +>f : () => number + + return /* @type {number} */ 42; +>42 : 42 +} diff --git a/tests/baselines/reference/jsdocTypeTagCast.js b/tests/baselines/reference/jsdocTypeTagCast.js index ffe59d0138d..efada5ee4dc 100644 --- a/tests/baselines/reference/jsdocTypeTagCast.js +++ b/tests/baselines/reference/jsdocTypeTagCast.js @@ -97,7 +97,7 @@ var a; /** @type {string} */ var s; var a = ("" + 4); -var s = "" +/** @type {*} */ (4); +var s = "" + /** @type {*} */ (4); var SomeBase = (function () { function SomeBase() { this.p = 42; @@ -128,19 +128,19 @@ var someBase = new SomeBase(); var someDerived = new SomeDerived(); var someOther = new SomeOther(); var someFakeClass = new SomeFakeClass(); -someBase =/** @type {SomeBase} */ (someDerived); -someBase =/** @type {SomeBase} */ (someBase); -someBase =/** @type {SomeBase} */ (someOther); // Error -someDerived =/** @type {SomeDerived} */ (someDerived); -someDerived =/** @type {SomeDerived} */ (someBase); -someDerived =/** @type {SomeDerived} */ (someOther); // Error -someOther =/** @type {SomeOther} */ (someDerived); // Error -someOther =/** @type {SomeOther} */ (someBase); // Error -someOther =/** @type {SomeOther} */ (someOther); +someBase = /** @type {SomeBase} */ (someDerived); +someBase = /** @type {SomeBase} */ (someBase); +someBase = /** @type {SomeBase} */ (someOther); // Error +someDerived = /** @type {SomeDerived} */ (someDerived); +someDerived = /** @type {SomeDerived} */ (someBase); +someDerived = /** @type {SomeDerived} */ (someOther); // Error +someOther = /** @type {SomeOther} */ (someDerived); // Error +someOther = /** @type {SomeOther} */ (someBase); // Error +someOther = /** @type {SomeOther} */ (someOther); someFakeClass = someBase; someFakeClass = someDerived; someBase = someFakeClass; // Error -someBase =/** @type {SomeBase} */ (someFakeClass); +someBase = /** @type {SomeBase} */ (someFakeClass); // Type assertion cannot be a type-predicate type /** @type {number | string} */ var numOrStr; diff --git a/tests/baselines/reference/logicalNotOperatorWithAnyOtherType.errors.txt b/tests/baselines/reference/logicalNotOperatorWithAnyOtherType.errors.txt index 0cb9869ca4c..ad951adf5f7 100644 --- a/tests/baselines/reference/logicalNotOperatorWithAnyOtherType.errors.txt +++ b/tests/baselines/reference/logicalNotOperatorWithAnyOtherType.errors.txt @@ -1,13 +1,10 @@ -tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts(45,27): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts(45,34): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts(46,27): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts(46,34): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts(47,27): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts(47,39): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts(45,27): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. +tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts(46,27): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. +tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts(47,27): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts(57,1): error TS2695: Left side of comma operator is unused and has no side effects. -==== tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts (7 errors) ==== +==== tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNotOperatorWithAnyOtherType.ts (4 errors) ==== // ! operator on any type var ANY: any; @@ -53,20 +50,14 @@ tests/cases/conformance/expressions/unaryOperators/logicalNotOperator/logicalNot var ResultIsBoolean15 = !A.foo(); var ResultIsBoolean16 = !(ANY + ANY1); var ResultIsBoolean17 = !(null + undefined); - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. var ResultIsBoolean18 = !(null + null); - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. var ResultIsBoolean19 = !(undefined + undefined); - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. // multiple ! operators var ResultIsBoolean20 = !!ANY; diff --git a/tests/baselines/reference/mixingApparentTypeOverrides.js b/tests/baselines/reference/mixingApparentTypeOverrides.js new file mode 100644 index 00000000000..20db894abb8 --- /dev/null +++ b/tests/baselines/reference/mixingApparentTypeOverrides.js @@ -0,0 +1,84 @@ +//// [mixingApparentTypeOverrides.ts] +type Constructor = new(...args: any[]) => T; +function Tagged>(Base: T) { + return class extends Base { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = ""; + } + }; +} + +class A { + toString () { + return "class A"; + } +} + +class B extends Tagged(A) { + toString () { // Should not be an error + return "class B"; + } +} + +class C extends A { + toString () { // Should not be an error + return "class C"; + } +} + +//// [mixingApparentTypeOverrides.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +function Tagged(Base) { + return (function (_super) { + __extends(class_1, _super); + function class_1() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var _this = _super.apply(this, args) || this; + _this._tag = ""; + return _this; + } + return class_1; + }(Base)); +} +var A = (function () { + function A() { + } + A.prototype.toString = function () { + return "class A"; + }; + return A; +}()); +var B = (function (_super) { + __extends(B, _super); + function B() { + return _super !== null && _super.apply(this, arguments) || this; + } + B.prototype.toString = function () { + return "class B"; + }; + return B; +}(Tagged(A))); +var C = (function (_super) { + __extends(C, _super); + function C() { + return _super !== null && _super.apply(this, arguments) || this; + } + C.prototype.toString = function () { + return "class C"; + }; + return C; +}(A)); diff --git a/tests/baselines/reference/mixingApparentTypeOverrides.symbols b/tests/baselines/reference/mixingApparentTypeOverrides.symbols new file mode 100644 index 00000000000..ea0adbb8acd --- /dev/null +++ b/tests/baselines/reference/mixingApparentTypeOverrides.symbols @@ -0,0 +1,67 @@ +=== tests/cases/compiler/mixingApparentTypeOverrides.ts === +type Constructor = new(...args: any[]) => T; +>Constructor : Symbol(Constructor, Decl(mixingApparentTypeOverrides.ts, 0, 0)) +>T : Symbol(T, Decl(mixingApparentTypeOverrides.ts, 0, 17)) +>args : Symbol(args, Decl(mixingApparentTypeOverrides.ts, 0, 26)) +>T : Symbol(T, Decl(mixingApparentTypeOverrides.ts, 0, 17)) + +function Tagged>(Base: T) { +>Tagged : Symbol(Tagged, Decl(mixingApparentTypeOverrides.ts, 0, 47)) +>T : Symbol(T, Decl(mixingApparentTypeOverrides.ts, 1, 16)) +>Constructor : Symbol(Constructor, Decl(mixingApparentTypeOverrides.ts, 0, 0)) +>Base : Symbol(Base, Decl(mixingApparentTypeOverrides.ts, 1, 43)) +>T : Symbol(T, Decl(mixingApparentTypeOverrides.ts, 1, 16)) + + return class extends Base { +>Base : Symbol(Base, Decl(mixingApparentTypeOverrides.ts, 1, 43)) + + _tag: string; +>_tag : Symbol((Anonymous class)._tag, Decl(mixingApparentTypeOverrides.ts, 2, 29)) + + constructor(...args: any[]) { +>args : Symbol(args, Decl(mixingApparentTypeOverrides.ts, 4, 16)) + + super(...args); +>super : Symbol(T, Decl(mixingApparentTypeOverrides.ts, 1, 16)) +>args : Symbol(args, Decl(mixingApparentTypeOverrides.ts, 4, 16)) + + this._tag = ""; +>this._tag : Symbol((Anonymous class)._tag, Decl(mixingApparentTypeOverrides.ts, 2, 29)) +>this : Symbol((Anonymous class), Decl(mixingApparentTypeOverrides.ts, 2, 8)) +>_tag : Symbol((Anonymous class)._tag, Decl(mixingApparentTypeOverrides.ts, 2, 29)) + } + }; +} + +class A { +>A : Symbol(A, Decl(mixingApparentTypeOverrides.ts, 9, 1)) + + toString () { +>toString : Symbol(A.toString, Decl(mixingApparentTypeOverrides.ts, 11, 9)) + + return "class A"; + } +} + +class B extends Tagged(A) { +>B : Symbol(B, Decl(mixingApparentTypeOverrides.ts, 15, 1)) +>Tagged : Symbol(Tagged, Decl(mixingApparentTypeOverrides.ts, 0, 47)) +>A : Symbol(A, Decl(mixingApparentTypeOverrides.ts, 9, 1)) + + toString () { // Should not be an error +>toString : Symbol(B.toString, Decl(mixingApparentTypeOverrides.ts, 17, 27)) + + return "class B"; + } +} + +class C extends A { +>C : Symbol(C, Decl(mixingApparentTypeOverrides.ts, 21, 1)) +>A : Symbol(A, Decl(mixingApparentTypeOverrides.ts, 9, 1)) + + toString () { // Should not be an error +>toString : Symbol(C.toString, Decl(mixingApparentTypeOverrides.ts, 23, 19)) + + return "class C"; + } +} diff --git a/tests/baselines/reference/mixingApparentTypeOverrides.types b/tests/baselines/reference/mixingApparentTypeOverrides.types new file mode 100644 index 00000000000..eae9c391a78 --- /dev/null +++ b/tests/baselines/reference/mixingApparentTypeOverrides.types @@ -0,0 +1,76 @@ +=== tests/cases/compiler/mixingApparentTypeOverrides.ts === +type Constructor = new(...args: any[]) => T; +>Constructor : Constructor +>T : T +>args : any[] +>T : T + +function Tagged>(Base: T) { +>Tagged : >(Base: T) => { new (...args: any[]): (Anonymous class); prototype: Tagged.(Anonymous class); } & T +>T : T +>Constructor : Constructor +>Base : T +>T : T + + return class extends Base { +>class extends Base { _tag: string; constructor(...args: any[]) { super(...args); this._tag = ""; } } : { new (...args: any[]): (Anonymous class); prototype: Tagged.(Anonymous class); } & T +>Base : {} + + _tag: string; +>_tag : string + + constructor(...args: any[]) { +>args : any[] + + super(...args); +>super(...args) : void +>super : T +>...args : any +>args : any[] + + this._tag = ""; +>this._tag = "" : "" +>this._tag : string +>this : this +>_tag : string +>"" : "" + } + }; +} + +class A { +>A : A + + toString () { +>toString : () => string + + return "class A"; +>"class A" : "class A" + } +} + +class B extends Tagged(A) { +>B : B +>Tagged(A) : Tagged.(Anonymous class) & A +>Tagged : >(Base: T) => { new (...args: any[]): (Anonymous class); prototype: Tagged.(Anonymous class); } & T +>A : typeof A + + toString () { // Should not be an error +>toString : () => string + + return "class B"; +>"class B" : "class B" + } +} + +class C extends A { +>C : C +>A : A + + toString () { // Should not be an error +>toString : () => string + + return "class C"; +>"class C" : "class C" + } +} diff --git a/tests/baselines/reference/moduleResolutionWithSymlinks_preserveSymlinks.errors.txt b/tests/baselines/reference/moduleResolutionWithSymlinks_preserveSymlinks.errors.txt new file mode 100644 index 00000000000..229fec978eb --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithSymlinks_preserveSymlinks.errors.txt @@ -0,0 +1,25 @@ +/app/app.ts(9,1): error TS90010: Type 'C' is not assignable to type 'C'. Two different types with this name exist, but they are unrelated. + Types have separate declarations of a private property 'x'. + + +==== /app/app.ts (1 errors) ==== + // We shouldn't resolve symlinks for references either. See the trace. + /// + + import { C as C1 } from "linked"; + import { C as C2 } from "linked2"; + + let x = new C1(); + // Should fail. We no longer resolve any symlinks. + x = new C2(); + ~ +!!! error TS90010: Type 'C' is not assignable to type 'C'. Two different types with this name exist, but they are unrelated. +!!! error TS90010: Types have separate declarations of a private property 'x'. + +==== /linked/index.d.ts (0 errors) ==== + export { real } from "real"; + export class C { private x; } + +==== /app/node_modules/real/index.d.ts (0 errors) ==== + export const real: string; + \ No newline at end of file diff --git a/tests/baselines/reference/moduleResolutionWithSymlinks_preserveSymlinks.js b/tests/baselines/reference/moduleResolutionWithSymlinks_preserveSymlinks.js new file mode 100644 index 00000000000..ca740146c0f --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithSymlinks_preserveSymlinks.js @@ -0,0 +1,31 @@ +//// [tests/cases/compiler/moduleResolutionWithSymlinks_preserveSymlinks.ts] //// + +//// [index.d.ts] +export { real } from "real"; +export class C { private x; } + +//// [index.d.ts] +export const real: string; + +//// [app.ts] +// We shouldn't resolve symlinks for references either. See the trace. +/// + +import { C as C1 } from "linked"; +import { C as C2 } from "linked2"; + +let x = new C1(); +// Should fail. We no longer resolve any symlinks. +x = new C2(); + + +//// [app.js] +"use strict"; +// We shouldn't resolve symlinks for references either. See the trace. +/// +exports.__esModule = true; +var linked_1 = require("linked"); +var linked2_1 = require("linked2"); +var x = new linked_1.C(); +// Should fail. We no longer resolve any symlinks. +x = new linked2_1.C(); diff --git a/tests/baselines/reference/moduleResolutionWithSymlinks_preserveSymlinks.trace.json b/tests/baselines/reference/moduleResolutionWithSymlinks_preserveSymlinks.trace.json new file mode 100644 index 00000000000..837b740ffee --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithSymlinks_preserveSymlinks.trace.json @@ -0,0 +1,55 @@ +[ + "======== Resolving type reference directive 'linked', containing file '/app/app.ts', root directory not set. ========", + "Root directory cannot be determined, skipping primary search paths.", + "Looking up in 'node_modules' folder, initial location '/app'.", + "File '/app/node_modules/linked.d.ts' does not exist.", + "File '/app/node_modules/linked/package.json' does not exist.", + "File '/app/node_modules/linked/index.d.ts' exist - use it as a name resolution result.", + "======== Type reference directive 'linked' was successfully resolved to '/app/node_modules/linked/index.d.ts', primary: false. ========", + "======== Resolving module 'real' from '/app/node_modules/linked/index.d.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module 'real' from 'node_modules' folder, target file type 'TypeScript'.", + "Directory '/app/node_modules/linked/node_modules' does not exist, skipping all lookups in it.", + "File '/app/node_modules/real.ts' does not exist.", + "File '/app/node_modules/real.tsx' does not exist.", + "File '/app/node_modules/real.d.ts' does not exist.", + "File '/app/node_modules/real/package.json' does not exist.", + "File '/app/node_modules/real/index.ts' does not exist.", + "File '/app/node_modules/real/index.tsx' does not exist.", + "File '/app/node_modules/real/index.d.ts' exist - use it as a name resolution result.", + "======== Module name 'real' was successfully resolved to '/app/node_modules/real/index.d.ts'. ========", + "======== Resolving module 'linked' from '/app/app.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module 'linked' from 'node_modules' folder, target file type 'TypeScript'.", + "File '/app/node_modules/linked.ts' does not exist.", + "File '/app/node_modules/linked.tsx' does not exist.", + "File '/app/node_modules/linked.d.ts' does not exist.", + "File '/app/node_modules/linked/package.json' does not exist.", + "File '/app/node_modules/linked/index.ts' does not exist.", + "File '/app/node_modules/linked/index.tsx' does not exist.", + "File '/app/node_modules/linked/index.d.ts' exist - use it as a name resolution result.", + "======== Module name 'linked' was successfully resolved to '/app/node_modules/linked/index.d.ts'. ========", + "======== Resolving module 'linked2' from '/app/app.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module 'linked2' from 'node_modules' folder, target file type 'TypeScript'.", + "File '/app/node_modules/linked2.ts' does not exist.", + "File '/app/node_modules/linked2.tsx' does not exist.", + "File '/app/node_modules/linked2.d.ts' does not exist.", + "File '/app/node_modules/linked2/package.json' does not exist.", + "File '/app/node_modules/linked2/index.ts' does not exist.", + "File '/app/node_modules/linked2/index.tsx' does not exist.", + "File '/app/node_modules/linked2/index.d.ts' exist - use it as a name resolution result.", + "======== Module name 'linked2' was successfully resolved to '/app/node_modules/linked2/index.d.ts'. ========", + "======== Resolving module 'real' from '/app/node_modules/linked2/index.d.ts'. ========", + "Explicitly specified module resolution kind: 'NodeJs'.", + "Loading module 'real' from 'node_modules' folder, target file type 'TypeScript'.", + "Directory '/app/node_modules/linked2/node_modules' does not exist, skipping all lookups in it.", + "File '/app/node_modules/real.ts' does not exist.", + "File '/app/node_modules/real.tsx' does not exist.", + "File '/app/node_modules/real.d.ts' does not exist.", + "File '/app/node_modules/real/package.json' does not exist.", + "File '/app/node_modules/real/index.ts' does not exist.", + "File '/app/node_modules/real/index.tsx' does not exist.", + "File '/app/node_modules/real/index.d.ts' exist - use it as a name resolution result.", + "======== Module name 'real' was successfully resolved to '/app/node_modules/real/index.d.ts'. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/noUnusedLocals_selfReference.errors.txt b/tests/baselines/reference/noUnusedLocals_selfReference.errors.txt new file mode 100644 index 00000000000..af40081e71c --- /dev/null +++ b/tests/baselines/reference/noUnusedLocals_selfReference.errors.txt @@ -0,0 +1,28 @@ +tests/cases/compiler/noUnusedLocals_selfReference.ts(3,10): error TS6133: 'f' is declared but never used. +tests/cases/compiler/noUnusedLocals_selfReference.ts(4,7): error TS6133: 'C' is declared but never used. +tests/cases/compiler/noUnusedLocals_selfReference.ts(7,6): error TS6133: 'E' is declared but never used. + + +==== tests/cases/compiler/noUnusedLocals_selfReference.ts (3 errors) ==== + export {}; // Make this a module scope, so these are local variables. + + function f() { f; } + ~ +!!! error TS6133: 'f' is declared but never used. + class C { + ~ +!!! error TS6133: 'C' is declared but never used. + m() { C; } + } + enum E { A = 0, B = E.A } + ~ +!!! error TS6133: 'E' is declared but never used. + + // Does not detect mutual recursion. + function g() { D; } + class D { m() { g; } } + + // Does not work on private methods. + class P { private m() { this.m; } } + P; + \ No newline at end of file diff --git a/tests/baselines/reference/noUnusedLocals_selfReference.js b/tests/baselines/reference/noUnusedLocals_selfReference.js new file mode 100644 index 00000000000..74a39923d57 --- /dev/null +++ b/tests/baselines/reference/noUnusedLocals_selfReference.js @@ -0,0 +1,49 @@ +//// [noUnusedLocals_selfReference.ts] +export {}; // Make this a module scope, so these are local variables. + +function f() { f; } +class C { + m() { C; } +} +enum E { A = 0, B = E.A } + +// Does not detect mutual recursion. +function g() { D; } +class D { m() { g; } } + +// Does not work on private methods. +class P { private m() { this.m; } } +P; + + +//// [noUnusedLocals_selfReference.js] +"use strict"; +exports.__esModule = true; +function f() { f; } +var C = (function () { + function C() { + } + C.prototype.m = function () { C; }; + return C; +}()); +var E; +(function (E) { + E[E["A"] = 0] = "A"; + E[E["B"] = 0] = "B"; +})(E || (E = {})); +// Does not detect mutual recursion. +function g() { D; } +var D = (function () { + function D() { + } + D.prototype.m = function () { g; }; + return D; +}()); +// Does not work on private methods. +var P = (function () { + function P() { + } + P.prototype.m = function () { this.m; }; + return P; +}()); +P; diff --git a/tests/baselines/reference/null.errors.txt b/tests/baselines/reference/null.errors.txt index 6b05bfb94d1..ed6db12d99b 100644 --- a/tests/baselines/reference/null.errors.txt +++ b/tests/baselines/reference/null.errors.txt @@ -1,12 +1,12 @@ -tests/cases/compiler/null.ts(3,9): error TS2531: Object is possibly 'null'. +tests/cases/compiler/null.ts(3,7): error TS2365: Operator '+' cannot be applied to types '3' and 'null'. ==== tests/cases/compiler/null.ts (1 errors) ==== var x=null; var y=3+x; var z=3+null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types '3' and 'null'. class C { } function f() { diff --git a/tests/baselines/reference/operatorAddNullUndefined.errors.txt b/tests/baselines/reference/operatorAddNullUndefined.errors.txt index 871ef3fc98f..c6a74080394 100644 --- a/tests/baselines/reference/operatorAddNullUndefined.errors.txt +++ b/tests/baselines/reference/operatorAddNullUndefined.errors.txt @@ -1,68 +1,56 @@ -tests/cases/compiler/operatorAddNullUndefined.ts(2,10): error TS2531: Object is possibly 'null'. -tests/cases/compiler/operatorAddNullUndefined.ts(2,17): error TS2531: Object is possibly 'null'. -tests/cases/compiler/operatorAddNullUndefined.ts(3,10): error TS2531: Object is possibly 'null'. -tests/cases/compiler/operatorAddNullUndefined.ts(3,17): error TS2532: Object is possibly 'undefined'. -tests/cases/compiler/operatorAddNullUndefined.ts(4,10): error TS2532: Object is possibly 'undefined'. -tests/cases/compiler/operatorAddNullUndefined.ts(4,22): error TS2531: Object is possibly 'null'. -tests/cases/compiler/operatorAddNullUndefined.ts(5,10): error TS2532: Object is possibly 'undefined'. -tests/cases/compiler/operatorAddNullUndefined.ts(5,22): error TS2532: Object is possibly 'undefined'. -tests/cases/compiler/operatorAddNullUndefined.ts(6,14): error TS2531: Object is possibly 'null'. -tests/cases/compiler/operatorAddNullUndefined.ts(7,14): error TS2532: Object is possibly 'undefined'. -tests/cases/compiler/operatorAddNullUndefined.ts(8,10): error TS2531: Object is possibly 'null'. -tests/cases/compiler/operatorAddNullUndefined.ts(9,10): error TS2532: Object is possibly 'undefined'. -tests/cases/compiler/operatorAddNullUndefined.ts(14,11): error TS2531: Object is possibly 'null'. -tests/cases/compiler/operatorAddNullUndefined.ts(15,11): error TS2532: Object is possibly 'undefined'. -tests/cases/compiler/operatorAddNullUndefined.ts(16,17): error TS2531: Object is possibly 'null'. -tests/cases/compiler/operatorAddNullUndefined.ts(17,17): error TS2532: Object is possibly 'undefined'. +tests/cases/compiler/operatorAddNullUndefined.ts(2,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. +tests/cases/compiler/operatorAddNullUndefined.ts(3,10): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. +tests/cases/compiler/operatorAddNullUndefined.ts(4,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'null'. +tests/cases/compiler/operatorAddNullUndefined.ts(5,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. +tests/cases/compiler/operatorAddNullUndefined.ts(6,10): error TS2365: Operator '+' cannot be applied to types '1' and 'null'. +tests/cases/compiler/operatorAddNullUndefined.ts(7,10): error TS2365: Operator '+' cannot be applied to types '1' and 'undefined'. +tests/cases/compiler/operatorAddNullUndefined.ts(8,10): error TS2365: Operator '+' cannot be applied to types 'null' and '1'. +tests/cases/compiler/operatorAddNullUndefined.ts(9,10): error TS2365: Operator '+' cannot be applied to types 'undefined' and '1'. +tests/cases/compiler/operatorAddNullUndefined.ts(14,11): error TS2365: Operator '+' cannot be applied to types 'null' and 'E'. +tests/cases/compiler/operatorAddNullUndefined.ts(15,11): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'E'. +tests/cases/compiler/operatorAddNullUndefined.ts(16,11): error TS2365: Operator '+' cannot be applied to types 'E' and 'null'. +tests/cases/compiler/operatorAddNullUndefined.ts(17,11): error TS2365: Operator '+' cannot be applied to types 'E' and 'undefined'. -==== tests/cases/compiler/operatorAddNullUndefined.ts (16 errors) ==== +==== tests/cases/compiler/operatorAddNullUndefined.ts (12 errors) ==== enum E { x } var x1 = null + null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. var x2 = null + undefined; - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. var x3 = undefined + null; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'null'. var x4 = undefined + undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. var x5 = 1 + null; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types '1' and 'null'. var x6 = 1 + undefined; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types '1' and 'undefined'. var x7 = null + 1; - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and '1'. var x8 = undefined + 1; - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and '1'. var x9 = "test" + null; var x10 = "test" + undefined; var x11 = null + "test"; var x12 = undefined + "test"; var x13 = null + E.x - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'E'. var x14 = undefined + E.x - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'E'. var x15 = E.x + null - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'E' and 'null'. var x16 = E.x + undefined - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. \ No newline at end of file + ~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'E' and 'undefined'. \ No newline at end of file diff --git a/tests/baselines/reference/parser15.4.4.14-9-2.js b/tests/baselines/reference/parser15.4.4.14-9-2.js index e24da870d3e..78ddfbac26b 100644 --- a/tests/baselines/reference/parser15.4.4.14-9-2.js +++ b/tests/baselines/reference/parser15.4.4.14-9-2.js @@ -41,9 +41,9 @@ function testcase() { var one = 1; var _float = -(4 / 3); var a = new Array(false, undefined, null, "0", obj, -1.3333333333333, "str", -0, true, +0, one, 1, 0, false, _float, -(4 / 3)); - if (a.indexOf(-(4 / 3)) === 14 &&// a[14]=_float===-(4/3) - a.indexOf(0) === 7 &&// a[7] = +0, 0===+0 - a.indexOf(-0) === 7 &&// a[7] = +0, -0===+0 + if (a.indexOf(-(4 / 3)) === 14 && // a[14]=_float===-(4/3) + a.indexOf(0) === 7 && // a[7] = +0, 0===+0 + a.indexOf(-0) === 7 && // a[7] = +0, -0===+0 a.indexOf(1) === 10) { return true; } diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js index 7ff9e380dcf..11275fe94b8 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js @@ -7,5 +7,5 @@ //// [parserGreaterThanTokenAmbiguity10.js] 1 // before - >>>// after + >>> // after 2; diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js index 03e6211ae15..af9e5498874 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js @@ -7,5 +7,5 @@ //// [parserGreaterThanTokenAmbiguity15.js] 1 // before - >>=// after + >>= // after 2; diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js index ba5e380043d..a8c960bd159 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js @@ -7,5 +7,5 @@ //// [parserGreaterThanTokenAmbiguity20.js] 1 // Before - >>>=// after + >>>= // after 2; diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js index e240746caa4..7252cfb39e0 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js @@ -7,5 +7,5 @@ //// [parserGreaterThanTokenAmbiguity5.js] 1 // before - >>// after + >> // after 2; diff --git a/tests/baselines/reference/plusOperatorWithAnyOtherType.errors.txt b/tests/baselines/reference/plusOperatorWithAnyOtherType.errors.txt index d693aaa17d9..561215bbd95 100644 --- a/tests/baselines/reference/plusOperatorWithAnyOtherType.errors.txt +++ b/tests/baselines/reference/plusOperatorWithAnyOtherType.errors.txt @@ -1,15 +1,12 @@ tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(34,24): error TS2532: Object is possibly 'undefined'. tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(35,24): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(46,26): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(46,33): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(47,26): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(47,33): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(48,26): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(48,38): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(46,26): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. +tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(47,26): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. +tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(48,26): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts(54,1): error TS2695: Left side of comma operator is unused and has no side effects. -==== tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts (9 errors) ==== +==== tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWithAnyOtherType.ts (6 errors) ==== // + operator on any type var ANY: any; @@ -60,20 +57,14 @@ tests/cases/conformance/expressions/unaryOperators/plusOperator/plusOperatorWith var ResultIsNumber15 = +A.foo(); var ResultIsNumber16 = +(ANY + ANY1); var ResultIsNumber17 = +(null + undefined); - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. var ResultIsNumber18 = +(null + null); - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. var ResultIsNumber19 = +(undefined + undefined); - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. // miss assignment operators +ANY; diff --git a/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/amd/nodeModulesMaxDepthExceeded.errors.txt b/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/amd/nodeModulesMaxDepthExceeded.errors.txt index a1b170ce665..bd2dd6231f3 100644 --- a/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/amd/nodeModulesMaxDepthExceeded.errors.txt +++ b/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/amd/nodeModulesMaxDepthExceeded.errors.txt @@ -9,7 +9,7 @@ maxDepthExceeded/root.ts(4,4): error TS2540: Cannot assign to 'rel' because it i "maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file "outDir": "built" }, - "include": ["**/*"], + "include": ["**/*", "node_modules/**/*"], "exclude": ["node_modules/m2/**/*"] } diff --git a/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/node/nodeModulesMaxDepthExceeded.errors.txt b/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/node/nodeModulesMaxDepthExceeded.errors.txt index a1b170ce665..bd2dd6231f3 100644 --- a/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/node/nodeModulesMaxDepthExceeded.errors.txt +++ b/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/node/nodeModulesMaxDepthExceeded.errors.txt @@ -9,7 +9,7 @@ maxDepthExceeded/root.ts(4,4): error TS2540: Cannot assign to 'rel' because it i "maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file "outDir": "built" }, - "include": ["**/*"], + "include": ["**/*", "node_modules/**/*"], "exclude": ["node_modules/m2/**/*"] } diff --git a/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.js b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.js new file mode 100644 index 00000000000..0be7fa1444d --- /dev/null +++ b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.js @@ -0,0 +1,30 @@ +//// [signatureInstantiationWithRecursiveConstraints.ts] +// Repro from #17148 + +class Foo { + myFunc(arg: T) {} +} + +class Bar { + myFunc(arg: T) {} +} + +const myVar: Foo = new Bar(); + + +//// [signatureInstantiationWithRecursiveConstraints.js] +"use strict"; +// Repro from #17148 +var Foo = (function () { + function Foo() { + } + Foo.prototype.myFunc = function (arg) { }; + return Foo; +}()); +var Bar = (function () { + function Bar() { + } + Bar.prototype.myFunc = function (arg) { }; + return Bar; +}()); +var myVar = new Bar(); diff --git a/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.symbols b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.symbols new file mode 100644 index 00000000000..ebc1b625d9e --- /dev/null +++ b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.symbols @@ -0,0 +1,30 @@ +=== tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts === +// Repro from #17148 + +class Foo { +>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0)) + + myFunc(arg: T) {} +>myFunc : Symbol(Foo.myFunc, Decl(signatureInstantiationWithRecursiveConstraints.ts, 2, 11)) +>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 9)) +>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0)) +>arg : Symbol(arg, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 24)) +>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 9)) +} + +class Bar { +>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1)) + + myFunc(arg: T) {} +>myFunc : Symbol(Bar.myFunc, Decl(signatureInstantiationWithRecursiveConstraints.ts, 6, 11)) +>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 9)) +>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1)) +>arg : Symbol(arg, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 24)) +>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 9)) +} + +const myVar: Foo = new Bar(); +>myVar : Symbol(myVar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 10, 5)) +>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1)) + diff --git a/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.types b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.types new file mode 100644 index 00000000000..2368835be08 --- /dev/null +++ b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.types @@ -0,0 +1,31 @@ +=== tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts === +// Repro from #17148 + +class Foo { +>Foo : Foo + + myFunc(arg: T) {} +>myFunc : (arg: T) => void +>T : T +>Foo : Foo +>arg : T +>T : T +} + +class Bar { +>Bar : Bar + + myFunc(arg: T) {} +>myFunc : (arg: T) => void +>T : T +>Bar : Bar +>arg : T +>T : T +} + +const myVar: Foo = new Bar(); +>myVar : Foo +>Foo : Foo +>new Bar() : Bar +>Bar : typeof Bar + diff --git a/tests/baselines/reference/stringLiteralTypesWithTemplateStrings01.errors.txt b/tests/baselines/reference/stringLiteralTypesWithTemplateStrings01.errors.txt deleted file mode 100644 index a1819ddb82f..00000000000 --- a/tests/baselines/reference/stringLiteralTypesWithTemplateStrings01.errors.txt +++ /dev/null @@ -1,17 +0,0 @@ -tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings01.ts(1,5): error TS2322: Type 'string' is not assignable to type '"ABC"'. -tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings01.ts(2,5): error TS2322: Type 'string' is not assignable to type '"DE\nF"'. -tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings01.ts(5,5): error TS2322: Type 'string' is not assignable to type '"JK`L"'. - - -==== tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings01.ts (3 errors) ==== - let ABC: "ABC" = `ABC`; - ~~~ -!!! error TS2322: Type 'string' is not assignable to type '"ABC"'. - let DE_NEWLINE_F: "DE\nF" = `DE - ~~~~~~~~~~~~ -!!! error TS2322: Type 'string' is not assignable to type '"DE\nF"'. - F`; - let G_QUOTE_HI: 'G"HI'; - let JK_BACKTICK_L: "JK`L" = `JK\`L`; - ~~~~~~~~~~~~~ -!!! error TS2322: Type 'string' is not assignable to type '"JK`L"'. \ No newline at end of file diff --git a/tests/baselines/reference/stringLiteralTypesWithTemplateStrings01.symbols b/tests/baselines/reference/stringLiteralTypesWithTemplateStrings01.symbols new file mode 100644 index 00000000000..3ca04bd04af --- /dev/null +++ b/tests/baselines/reference/stringLiteralTypesWithTemplateStrings01.symbols @@ -0,0 +1,14 @@ +=== tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings01.ts === +let ABC: "ABC" = `ABC`; +>ABC : Symbol(ABC, Decl(stringLiteralTypesWithTemplateStrings01.ts, 0, 3)) + +let DE_NEWLINE_F: "DE\nF" = `DE +>DE_NEWLINE_F : Symbol(DE_NEWLINE_F, Decl(stringLiteralTypesWithTemplateStrings01.ts, 1, 3)) + +F`; +let G_QUOTE_HI: 'G"HI'; +>G_QUOTE_HI : Symbol(G_QUOTE_HI, Decl(stringLiteralTypesWithTemplateStrings01.ts, 3, 3)) + +let JK_BACKTICK_L: "JK`L" = `JK\`L`; +>JK_BACKTICK_L : Symbol(JK_BACKTICK_L, Decl(stringLiteralTypesWithTemplateStrings01.ts, 4, 3)) + diff --git a/tests/baselines/reference/stringLiteralTypesWithTemplateStrings01.types b/tests/baselines/reference/stringLiteralTypesWithTemplateStrings01.types new file mode 100644 index 00000000000..abe2943d07d --- /dev/null +++ b/tests/baselines/reference/stringLiteralTypesWithTemplateStrings01.types @@ -0,0 +1,17 @@ +=== tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings01.ts === +let ABC: "ABC" = `ABC`; +>ABC : "ABC" +>`ABC` : "ABC" + +let DE_NEWLINE_F: "DE\nF" = `DE +>DE_NEWLINE_F : "DE\nF" +>`DEF` : "DE\nF" + +F`; +let G_QUOTE_HI: 'G"HI'; +>G_QUOTE_HI : "G\"HI" + +let JK_BACKTICK_L: "JK`L" = `JK\`L`; +>JK_BACKTICK_L : "JK`L" +>`JK\`L` : "JK`L" + diff --git a/tests/baselines/reference/stringLiteralTypesWithTemplateStrings02.errors.txt b/tests/baselines/reference/stringLiteralTypesWithTemplateStrings02.errors.txt index 8c69e595e25..4137661f0be 100644 --- a/tests/baselines/reference/stringLiteralTypesWithTemplateStrings02.errors.txt +++ b/tests/baselines/reference/stringLiteralTypesWithTemplateStrings02.errors.txt @@ -1,11 +1,11 @@ -tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings02.ts(1,5): error TS2322: Type 'string' is not assignable to type '"AB\r\nC"'. +tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings02.ts(1,5): error TS2322: Type '"AB\nC"' is not assignable to type '"AB\r\nC"'. tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings02.ts(3,5): error TS2322: Type 'string' is not assignable to type '"DE\nF"'. ==== tests/cases/conformance/types/stringLiteral/stringLiteralTypesWithTemplateStrings02.ts (2 errors) ==== let abc: "AB\r\nC" = `AB ~~~ -!!! error TS2322: Type 'string' is not assignable to type '"AB\r\nC"'. +!!! error TS2322: Type '"AB\nC"' is not assignable to type '"AB\r\nC"'. C`; let de_NEWLINE_f: "DE\nF" = `DE${"\n"}F`; ~~~~~~~~~~~~ diff --git a/tests/baselines/reference/syntheticDefaultExportsWithDynamicImports.js b/tests/baselines/reference/syntheticDefaultExportsWithDynamicImports.js new file mode 100644 index 00000000000..c49924191e5 --- /dev/null +++ b/tests/baselines/reference/syntheticDefaultExportsWithDynamicImports.js @@ -0,0 +1,19 @@ +//// [tests/cases/compiler/syntheticDefaultExportsWithDynamicImports.ts] //// + +//// [index.d.ts] +declare function packageExport(x: number): string; +export = packageExport; + +//// [index.ts] +import("package").then(({default: foo}) => foo(42)); + +//// [index.js] +System.register([], function (exports_1, context_1) { + var __moduleName = context_1 && context_1.id; + return { + setters: [], + execute: function () { + context_1.import("package").then(({ default: foo }) => foo(42)); + } + }; +}); diff --git a/tests/baselines/reference/syntheticDefaultExportsWithDynamicImports.symbols b/tests/baselines/reference/syntheticDefaultExportsWithDynamicImports.symbols new file mode 100644 index 00000000000..85019711153 --- /dev/null +++ b/tests/baselines/reference/syntheticDefaultExportsWithDynamicImports.symbols @@ -0,0 +1,17 @@ +=== tests/cases/compiler/node_modules/package/index.d.ts === +declare function packageExport(x: number): string; +>packageExport : Symbol(packageExport, Decl(index.d.ts, 0, 0)) +>x : Symbol(x, Decl(index.d.ts, 0, 31)) + +export = packageExport; +>packageExport : Symbol(packageExport, Decl(index.d.ts, 0, 0)) + +=== tests/cases/compiler/index.ts === +import("package").then(({default: foo}) => foo(42)); +>import("package").then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>"package" : Symbol("tests/cases/compiler/node_modules/package/index", Decl(index.d.ts, 0, 0)) +>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>default : Symbol(default) +>foo : Symbol(foo, Decl(index.ts, 0, 25)) +>foo : Symbol(foo, Decl(index.ts, 0, 25)) + diff --git a/tests/baselines/reference/syntheticDefaultExportsWithDynamicImports.types b/tests/baselines/reference/syntheticDefaultExportsWithDynamicImports.types new file mode 100644 index 00000000000..1c38ecc466c --- /dev/null +++ b/tests/baselines/reference/syntheticDefaultExportsWithDynamicImports.types @@ -0,0 +1,22 @@ +=== tests/cases/compiler/node_modules/package/index.d.ts === +declare function packageExport(x: number): string; +>packageExport : (x: number) => string +>x : number + +export = packageExport; +>packageExport : (x: number) => string + +=== tests/cases/compiler/index.ts === +import("package").then(({default: foo}) => foo(42)); +>import("package").then(({default: foo}) => foo(42)) : Promise +>import("package").then : string) & { default: (x: number) => string; }, TResult2 = never>(onfulfilled?: (value: ((x: number) => string) & { default: (x: number) => string; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>import("package") : Promise<((x: number) => string) & { default: (x: number) => string; }> +>"package" : "package" +>then : string) & { default: (x: number) => string; }, TResult2 = never>(onfulfilled?: (value: ((x: number) => string) & { default: (x: number) => string; }) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>({default: foo}) => foo(42) : ({ default: foo }: ((x: number) => string) & { default: (x: number) => string; }) => string +>default : any +>foo : (x: number) => string +>foo(42) : string +>foo : (x: number) => string +>42 : 42 + diff --git a/tests/baselines/reference/taggedTemplateStringsPlainCharactersThatArePartsOfEscapes01.types b/tests/baselines/reference/taggedTemplateStringsPlainCharactersThatArePartsOfEscapes01.types index 9b394f2bceb..9f4ee0eb73a 100644 --- a/tests/baselines/reference/taggedTemplateStringsPlainCharactersThatArePartsOfEscapes01.types +++ b/tests/baselines/reference/taggedTemplateStringsPlainCharactersThatArePartsOfEscapes01.types @@ -8,5 +8,5 @@ function f(...x: any[]) { f `0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` >f `0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` : void >f : (...x: any[]) => void ->`0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` : string +>`0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` : "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n" diff --git a/tests/baselines/reference/taggedTemplateStringsPlainCharactersThatArePartsOfEscapes01_ES6.types b/tests/baselines/reference/taggedTemplateStringsPlainCharactersThatArePartsOfEscapes01_ES6.types index 775914faeea..89a33c69fe0 100644 --- a/tests/baselines/reference/taggedTemplateStringsPlainCharactersThatArePartsOfEscapes01_ES6.types +++ b/tests/baselines/reference/taggedTemplateStringsPlainCharactersThatArePartsOfEscapes01_ES6.types @@ -8,5 +8,5 @@ function f(...x: any[]) { f `0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` >f `0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` : void >f : (...x: any[]) => void ->`0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` : string +>`0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` : "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n" diff --git a/tests/baselines/reference/taggedTemplateStringsWithMultilineTemplate.types b/tests/baselines/reference/taggedTemplateStringsWithMultilineTemplate.types index 52f7a517a4a..d37c81fb884 100644 --- a/tests/baselines/reference/taggedTemplateStringsWithMultilineTemplate.types +++ b/tests/baselines/reference/taggedTemplateStringsWithMultilineTemplate.types @@ -7,7 +7,7 @@ function f(...args: any[]): void { f ` >f `\` : void >f : (...args: any[]) => void ->`\` : string +>`\` : "\n\n" \ diff --git a/tests/baselines/reference/taggedTemplateStringsWithMultilineTemplateES6.types b/tests/baselines/reference/taggedTemplateStringsWithMultilineTemplateES6.types index 35374be6436..6582d8eb4a5 100644 --- a/tests/baselines/reference/taggedTemplateStringsWithMultilineTemplateES6.types +++ b/tests/baselines/reference/taggedTemplateStringsWithMultilineTemplateES6.types @@ -7,7 +7,7 @@ function f(...args: any[]): void { f ` >f `\` : void >f : (...args: any[]) => void ->`\` : string +>`\` : "\n\n" \ diff --git a/tests/baselines/reference/taggedTemplateStringsWithTagsTypedAsAny.types b/tests/baselines/reference/taggedTemplateStringsWithTagsTypedAsAny.types index 9e894240c93..9bd975bb472 100644 --- a/tests/baselines/reference/taggedTemplateStringsWithTagsTypedAsAny.types +++ b/tests/baselines/reference/taggedTemplateStringsWithTagsTypedAsAny.types @@ -5,7 +5,7 @@ var f: any; f `abc` >f `abc` : any >f : any ->`abc` : string +>`abc` : "abc" f `abc${1}def${2}ghi`; >f `abc${1}def${2}ghi` : any @@ -21,7 +21,7 @@ f.g.h `abc` >f : any >g : any >h : any ->`abc` : string +>`abc` : "abc" f.g.h `abc${1}def${2}ghi`; >f.g.h `abc${1}def${2}ghi` : any @@ -38,7 +38,7 @@ f `abc`.member >f `abc`.member : any >f `abc` : any >f : any ->`abc` : string +>`abc` : "abc" >member : any f `abc${1}def${2}ghi`.member; @@ -54,7 +54,7 @@ f `abc`["member"]; >f `abc`["member"] : any >f `abc` : any >f : any ->`abc` : string +>`abc` : "abc" >"member" : "member" f `abc${1}def${2}ghi`["member"]; @@ -72,7 +72,7 @@ f `abc`["member"].someOtherTag `abc${1}def${2}ghi`; >f `abc`["member"] : any >f `abc` : any >f : any ->`abc` : string +>`abc` : "abc" >"member" : "member" >someOtherTag : any >`abc${1}def${2}ghi` : string @@ -99,7 +99,7 @@ f.thisIsNotATag(`abc`); >f.thisIsNotATag : any >f : any >thisIsNotATag : any ->`abc` : string +>`abc` : "abc" f.thisIsNotATag(`abc${1}def${2}ghi`); >f.thisIsNotATag(`abc${1}def${2}ghi`) : any diff --git a/tests/baselines/reference/taggedTemplateStringsWithTagsTypedAsAnyES6.types b/tests/baselines/reference/taggedTemplateStringsWithTagsTypedAsAnyES6.types index 99bb10f3546..3fde2cd552d 100644 --- a/tests/baselines/reference/taggedTemplateStringsWithTagsTypedAsAnyES6.types +++ b/tests/baselines/reference/taggedTemplateStringsWithTagsTypedAsAnyES6.types @@ -5,7 +5,7 @@ var f: any; f `abc` >f `abc` : any >f : any ->`abc` : string +>`abc` : "abc" f `abc${1}def${2}ghi`; >f `abc${1}def${2}ghi` : any @@ -21,7 +21,7 @@ f.g.h `abc` >f : any >g : any >h : any ->`abc` : string +>`abc` : "abc" f.g.h `abc${1}def${2}ghi`; >f.g.h `abc${1}def${2}ghi` : any @@ -38,7 +38,7 @@ f `abc`.member >f `abc`.member : any >f `abc` : any >f : any ->`abc` : string +>`abc` : "abc" >member : any f `abc${1}def${2}ghi`.member; @@ -54,7 +54,7 @@ f `abc`["member"]; >f `abc`["member"] : any >f `abc` : any >f : any ->`abc` : string +>`abc` : "abc" >"member" : "member" f `abc${1}def${2}ghi`["member"]; @@ -72,7 +72,7 @@ f `abc`["member"].someOtherTag `abc${1}def${2}ghi`; >f `abc`["member"] : any >f `abc` : any >f : any ->`abc` : string +>`abc` : "abc" >"member" : "member" >someOtherTag : any >`abc${1}def${2}ghi` : string @@ -99,7 +99,7 @@ f.thisIsNotATag(`abc`); >f.thisIsNotATag : any >f : any >thisIsNotATag : any ->`abc` : string +>`abc` : "abc" f.thisIsNotATag(`abc${1}def${2}ghi`); >f.thisIsNotATag(`abc${1}def${2}ghi`) : any diff --git a/tests/baselines/reference/taggedTemplateStringsWithTypedTags.types b/tests/baselines/reference/taggedTemplateStringsWithTypedTags.types index 69b9368f765..02c50b835c1 100644 --- a/tests/baselines/reference/taggedTemplateStringsWithTypedTags.types +++ b/tests/baselines/reference/taggedTemplateStringsWithTypedTags.types @@ -36,7 +36,7 @@ var f: I; f `abc` >f `abc` : I >f : I ->`abc` : string +>`abc` : "abc" f `abc${1}def${2}ghi`; >f `abc${1}def${2}ghi` : I @@ -49,7 +49,7 @@ f `abc`.member >f `abc`.member : I >f `abc` : I >f : I ->`abc` : string +>`abc` : "abc" >member : I f `abc${1}def${2}ghi`.member; @@ -65,7 +65,7 @@ f `abc`["member"]; >f `abc`["member"] : I >f `abc` : I >f : I ->`abc` : string +>`abc` : "abc" >"member" : "member" f `abc${1}def${2}ghi`["member"]; @@ -83,7 +83,7 @@ f `abc`[0].member `abc${1}def${2}ghi`; >f `abc`[0] : I >f `abc` : I >f : I ->`abc` : string +>`abc` : "abc" >0 : 0 >member : I >`abc${1}def${2}ghi` : string @@ -110,7 +110,7 @@ f.thisIsNotATag(`abc`); >f.thisIsNotATag : (x: string) => void >f : I >thisIsNotATag : (x: string) => void ->`abc` : string +>`abc` : "abc" f.thisIsNotATag(`abc${1}def${2}ghi`); >f.thisIsNotATag(`abc${1}def${2}ghi`) : void diff --git a/tests/baselines/reference/taggedTemplateStringsWithTypedTagsES6.types b/tests/baselines/reference/taggedTemplateStringsWithTypedTagsES6.types index 97e2f011ca6..51fd5907f3c 100644 --- a/tests/baselines/reference/taggedTemplateStringsWithTypedTagsES6.types +++ b/tests/baselines/reference/taggedTemplateStringsWithTypedTagsES6.types @@ -36,7 +36,7 @@ var f: I; f `abc` >f `abc` : I >f : I ->`abc` : string +>`abc` : "abc" f `abc${1}def${2}ghi`; >f `abc${1}def${2}ghi` : I @@ -49,7 +49,7 @@ f `abc`.member >f `abc`.member : I >f `abc` : I >f : I ->`abc` : string +>`abc` : "abc" >member : I f `abc${1}def${2}ghi`.member; @@ -65,7 +65,7 @@ f `abc`["member"]; >f `abc`["member"] : I >f `abc` : I >f : I ->`abc` : string +>`abc` : "abc" >"member" : "member" f `abc${1}def${2}ghi`["member"]; @@ -83,7 +83,7 @@ f `abc`[0].member `abc${1}def${2}ghi`; >f `abc`[0] : I >f `abc` : I >f : I ->`abc` : string +>`abc` : "abc" >0 : 0 >member : I >`abc${1}def${2}ghi` : string @@ -110,7 +110,7 @@ f.thisIsNotATag(`abc`); >f.thisIsNotATag : (x: string) => void >f : I >thisIsNotATag : (x: string) => void ->`abc` : string +>`abc` : "abc" f.thisIsNotATag(`abc${1}def${2}ghi`); >f.thisIsNotATag(`abc${1}def${2}ghi`) : void diff --git a/tests/baselines/reference/taggedTemplateStringsWithWhitespaceEscapes.types b/tests/baselines/reference/taggedTemplateStringsWithWhitespaceEscapes.types index e8c94095445..97fb79dc1b4 100644 --- a/tests/baselines/reference/taggedTemplateStringsWithWhitespaceEscapes.types +++ b/tests/baselines/reference/taggedTemplateStringsWithWhitespaceEscapes.types @@ -7,5 +7,5 @@ function f(...args: any[]) { f `\t\n\v\f\r\\`; >f `\t\n\v\f\r\\` : void >f : (...args: any[]) => void ->`\t\n\v\f\r\\` : string +>`\t\n\v\f\r\\` : "\t\n\v\f\r\\" diff --git a/tests/baselines/reference/taggedTemplateStringsWithWhitespaceEscapesES6.types b/tests/baselines/reference/taggedTemplateStringsWithWhitespaceEscapesES6.types index a4fa109cec7..df78ab603b5 100644 --- a/tests/baselines/reference/taggedTemplateStringsWithWhitespaceEscapesES6.types +++ b/tests/baselines/reference/taggedTemplateStringsWithWhitespaceEscapesES6.types @@ -7,5 +7,5 @@ function f(...args: any[]) { f `\t\n\v\f\r\\`; >f `\t\n\v\f\r\\` : void >f : (...args: any[]) => void ->`\t\n\v\f\r\\` : string +>`\t\n\v\f\r\\` : "\t\n\v\f\r\\" diff --git a/tests/baselines/reference/taggedTemplateUntypedTagCall01.types b/tests/baselines/reference/taggedTemplateUntypedTagCall01.types index 3949869550c..4fc03e8c080 100644 --- a/tests/baselines/reference/taggedTemplateUntypedTagCall01.types +++ b/tests/baselines/reference/taggedTemplateUntypedTagCall01.types @@ -6,5 +6,5 @@ var tag: Function; tag `Hello world!`; >tag `Hello world!` : any >tag : Function ->`Hello world!` : string +>`Hello world!` : "Hello world!" diff --git a/tests/baselines/reference/templateStringControlCharacterEscapes01.types b/tests/baselines/reference/templateStringControlCharacterEscapes01.types index 7bd2e89839c..3fe51d1d1b1 100644 --- a/tests/baselines/reference/templateStringControlCharacterEscapes01.types +++ b/tests/baselines/reference/templateStringControlCharacterEscapes01.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/templates/templateStringControlCharacterEscapes01.ts === var x = `\0\x00\u0000 0 00 0000`; >x : string ->`\0\x00\u0000 0 00 0000` : string +>`\0\x00\u0000 0 00 0000` : "\0\0\0 0 00 0000" diff --git a/tests/baselines/reference/templateStringControlCharacterEscapes01_ES6.types b/tests/baselines/reference/templateStringControlCharacterEscapes01_ES6.types index 80962ecdd6a..2d1a609200f 100644 --- a/tests/baselines/reference/templateStringControlCharacterEscapes01_ES6.types +++ b/tests/baselines/reference/templateStringControlCharacterEscapes01_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/templates/templateStringControlCharacterEscapes01_ES6.ts === var x = `\0\x00\u0000 0 00 0000`; >x : string ->`\0\x00\u0000 0 00 0000` : string +>`\0\x00\u0000 0 00 0000` : "\0\0\0 0 00 0000" diff --git a/tests/baselines/reference/templateStringControlCharacterEscapes02.types b/tests/baselines/reference/templateStringControlCharacterEscapes02.types index 4656be748ec..451d1f0dcd8 100644 --- a/tests/baselines/reference/templateStringControlCharacterEscapes02.types +++ b/tests/baselines/reference/templateStringControlCharacterEscapes02.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/templates/templateStringControlCharacterEscapes02.ts === var x = `\x19\u0019 19`; >x : string ->`\x19\u0019 19` : string +>`\x19\u0019 19` : "\u0019\u0019 19" diff --git a/tests/baselines/reference/templateStringControlCharacterEscapes02_ES6.types b/tests/baselines/reference/templateStringControlCharacterEscapes02_ES6.types index d50196f5913..b1bb248ffce 100644 --- a/tests/baselines/reference/templateStringControlCharacterEscapes02_ES6.types +++ b/tests/baselines/reference/templateStringControlCharacterEscapes02_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/templates/templateStringControlCharacterEscapes02_ES6.ts === var x = `\x19\u0019 19`; >x : string ->`\x19\u0019 19` : string +>`\x19\u0019 19` : "\u0019\u0019 19" diff --git a/tests/baselines/reference/templateStringControlCharacterEscapes03.types b/tests/baselines/reference/templateStringControlCharacterEscapes03.types index b509b2ccd6c..f65ef32da44 100644 --- a/tests/baselines/reference/templateStringControlCharacterEscapes03.types +++ b/tests/baselines/reference/templateStringControlCharacterEscapes03.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/templates/templateStringControlCharacterEscapes03.ts === var x = `\x1F\u001f 1F 1f`; >x : string ->`\x1F\u001f 1F 1f` : string +>`\x1F\u001f 1F 1f` : "\u001F\u001F 1F 1f" diff --git a/tests/baselines/reference/templateStringControlCharacterEscapes03_ES6.types b/tests/baselines/reference/templateStringControlCharacterEscapes03_ES6.types index 4d35fe85c41..d6e79953a5f 100644 --- a/tests/baselines/reference/templateStringControlCharacterEscapes03_ES6.types +++ b/tests/baselines/reference/templateStringControlCharacterEscapes03_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/templates/templateStringControlCharacterEscapes03_ES6.ts === var x = `\x1F\u001f 1F 1f`; >x : string ->`\x1F\u001f 1F 1f` : string +>`\x1F\u001f 1F 1f` : "\u001F\u001F 1F 1f" diff --git a/tests/baselines/reference/templateStringControlCharacterEscapes04.types b/tests/baselines/reference/templateStringControlCharacterEscapes04.types index ca72e253d56..c13ddcf113c 100644 --- a/tests/baselines/reference/templateStringControlCharacterEscapes04.types +++ b/tests/baselines/reference/templateStringControlCharacterEscapes04.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/templates/templateStringControlCharacterEscapes04.ts === var x = `\x20\u0020 20`; >x : string ->`\x20\u0020 20` : string +>`\x20\u0020 20` : " 20" diff --git a/tests/baselines/reference/templateStringControlCharacterEscapes04_ES6.types b/tests/baselines/reference/templateStringControlCharacterEscapes04_ES6.types index 01b3f35ba9a..5edabb6971e 100644 --- a/tests/baselines/reference/templateStringControlCharacterEscapes04_ES6.types +++ b/tests/baselines/reference/templateStringControlCharacterEscapes04_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/templates/templateStringControlCharacterEscapes04_ES6.ts === var x = `\x20\u0020 20`; >x : string ->`\x20\u0020 20` : string +>`\x20\u0020 20` : " 20" diff --git a/tests/baselines/reference/templateStringInEqualityChecks.types b/tests/baselines/reference/templateStringInEqualityChecks.types index dd78aa131d4..cddc533a239 100644 --- a/tests/baselines/reference/templateStringInEqualityChecks.types +++ b/tests/baselines/reference/templateStringInEqualityChecks.types @@ -5,13 +5,13 @@ var x = `abc${0}abc` === `abc` || >`abc${0}abc` === `abc` : boolean >`abc${0}abc` : string >0 : 0 ->`abc` : string +>`abc` : "abc" `abc` !== `abc${0}abc` && >`abc` !== `abc${0}abc` && `abc${0}abc` == "abc0abc" && "abc0abc" !== `abc${0}abc` : boolean >`abc` !== `abc${0}abc` && `abc${0}abc` == "abc0abc" : boolean >`abc` !== `abc${0}abc` : boolean ->`abc` : string +>`abc` : "abc" >`abc${0}abc` : string >0 : 0 diff --git a/tests/baselines/reference/templateStringInEqualityChecksES6.types b/tests/baselines/reference/templateStringInEqualityChecksES6.types index b3ef2b89720..58a24300725 100644 --- a/tests/baselines/reference/templateStringInEqualityChecksES6.types +++ b/tests/baselines/reference/templateStringInEqualityChecksES6.types @@ -5,13 +5,13 @@ var x = `abc${0}abc` === `abc` || >`abc${0}abc` === `abc` : boolean >`abc${0}abc` : string >0 : 0 ->`abc` : string +>`abc` : "abc" `abc` !== `abc${0}abc` && >`abc` !== `abc${0}abc` && `abc${0}abc` == "abc0abc" && "abc0abc" !== `abc${0}abc` : boolean >`abc` !== `abc${0}abc` && `abc${0}abc` == "abc0abc" : boolean >`abc` !== `abc${0}abc` : boolean ->`abc` : string +>`abc` : "abc" >`abc${0}abc` : string >0 : 0 diff --git a/tests/baselines/reference/templateStringInIndexExpression.types b/tests/baselines/reference/templateStringInIndexExpression.types index b453c37f4c5..f0faac5f45c 100644 --- a/tests/baselines/reference/templateStringInIndexExpression.types +++ b/tests/baselines/reference/templateStringInIndexExpression.types @@ -3,5 +3,5 @@ >`abc${0}abc`[`0`] : any >`abc${0}abc` : string >0 : 0 ->`0` : string +>`0` : "0" diff --git a/tests/baselines/reference/templateStringInIndexExpressionES6.types b/tests/baselines/reference/templateStringInIndexExpressionES6.types index b7be602e893..eabc71c9fac 100644 --- a/tests/baselines/reference/templateStringInIndexExpressionES6.types +++ b/tests/baselines/reference/templateStringInIndexExpressionES6.types @@ -3,5 +3,5 @@ >`abc${0}abc`[`0`] : any >`abc${0}abc` : string >0 : 0 ->`0` : string +>`0` : "0" diff --git a/tests/baselines/reference/templateStringInSwitchAndCase.types b/tests/baselines/reference/templateStringInSwitchAndCase.types index a0f8602167a..d54891ff051 100644 --- a/tests/baselines/reference/templateStringInSwitchAndCase.types +++ b/tests/baselines/reference/templateStringInSwitchAndCase.types @@ -4,10 +4,10 @@ switch (`abc${0}abc`) { >0 : 0 case `abc`: ->`abc` : string +>`abc` : "abc" case `123`: ->`123` : string +>`123` : "123" case `abc${0}abc`: >`abc${0}abc` : string diff --git a/tests/baselines/reference/templateStringInSwitchAndCaseES6.types b/tests/baselines/reference/templateStringInSwitchAndCaseES6.types index 0cde7e58756..8c7c4fee2c1 100644 --- a/tests/baselines/reference/templateStringInSwitchAndCaseES6.types +++ b/tests/baselines/reference/templateStringInSwitchAndCaseES6.types @@ -4,10 +4,10 @@ switch (`abc${0}abc`) { >0 : 0 case `abc`: ->`abc` : string +>`abc` : "abc" case `123`: ->`123` : string +>`123` : "123" case `abc${0}abc`: >`abc${0}abc` : string diff --git a/tests/baselines/reference/templateStringMultiline1.types b/tests/baselines/reference/templateStringMultiline1.types index f66dd89a7bc..daccdf56d82 100644 --- a/tests/baselines/reference/templateStringMultiline1.types +++ b/tests/baselines/reference/templateStringMultiline1.types @@ -1,7 +1,7 @@ === tests/cases/conformance/es6/templates/templateStringMultiline1.ts === // newlines are ` ->`\` : string +>`\` : "\n" \ ` diff --git a/tests/baselines/reference/templateStringMultiline1_ES6.types b/tests/baselines/reference/templateStringMultiline1_ES6.types index 71e82a2e5c5..a5b08120fc4 100644 --- a/tests/baselines/reference/templateStringMultiline1_ES6.types +++ b/tests/baselines/reference/templateStringMultiline1_ES6.types @@ -1,7 +1,7 @@ === tests/cases/conformance/es6/templates/templateStringMultiline1_ES6.ts === // newlines are ` ->`\` : string +>`\` : "\n" \ ` diff --git a/tests/baselines/reference/templateStringMultiline2.types b/tests/baselines/reference/templateStringMultiline2.types index 6184cb098b6..96518853b28 100644 --- a/tests/baselines/reference/templateStringMultiline2.types +++ b/tests/baselines/reference/templateStringMultiline2.types @@ -1,7 +1,7 @@ === tests/cases/conformance/es6/templates/templateStringMultiline2.ts === // newlines are ` ->`\` : string +>`\` : "\n" \ ` diff --git a/tests/baselines/reference/templateStringMultiline2_ES6.types b/tests/baselines/reference/templateStringMultiline2_ES6.types index 3d123975330..be555557874 100644 --- a/tests/baselines/reference/templateStringMultiline2_ES6.types +++ b/tests/baselines/reference/templateStringMultiline2_ES6.types @@ -1,7 +1,7 @@ === tests/cases/conformance/es6/templates/templateStringMultiline2_ES6.ts === // newlines are ` ->`\` : string +>`\` : "\n" \ ` diff --git a/tests/baselines/reference/templateStringMultiline3.types b/tests/baselines/reference/templateStringMultiline3.types index 1d77fca9c56..2908dd936ea 100644 --- a/tests/baselines/reference/templateStringMultiline3.types +++ b/tests/baselines/reference/templateStringMultiline3.types @@ -1,7 +1,7 @@ === tests/cases/conformance/es6/templates/templateStringMultiline3.ts === // newlines are ` ->`\` : string +>`\` : "\n" \ ` diff --git a/tests/baselines/reference/templateStringMultiline3_ES6.types b/tests/baselines/reference/templateStringMultiline3_ES6.types index a1a696ebcfd..24d523c5bc2 100644 --- a/tests/baselines/reference/templateStringMultiline3_ES6.types +++ b/tests/baselines/reference/templateStringMultiline3_ES6.types @@ -1,7 +1,7 @@ === tests/cases/conformance/es6/templates/templateStringMultiline3_ES6.ts === // newlines are ` ->`\` : string +>`\` : "\n" \ ` diff --git a/tests/baselines/reference/templateStringPlainCharactersThatArePartsOfEscapes01.types b/tests/baselines/reference/templateStringPlainCharactersThatArePartsOfEscapes01.types index acdf7898b71..4a3f79a9928 100644 --- a/tests/baselines/reference/templateStringPlainCharactersThatArePartsOfEscapes01.types +++ b/tests/baselines/reference/templateStringPlainCharactersThatArePartsOfEscapes01.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringPlainCharactersThatArePartsOfEscapes01.ts === `0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` ->`0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` : string +>`0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` : "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n" diff --git a/tests/baselines/reference/templateStringPlainCharactersThatArePartsOfEscapes01_ES6.types b/tests/baselines/reference/templateStringPlainCharactersThatArePartsOfEscapes01_ES6.types index 110676b704b..5ff16707e36 100644 --- a/tests/baselines/reference/templateStringPlainCharactersThatArePartsOfEscapes01_ES6.types +++ b/tests/baselines/reference/templateStringPlainCharactersThatArePartsOfEscapes01_ES6.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringPlainCharactersThatArePartsOfEscapes01_ES6.ts === `0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` ->`0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` : string +>`0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n` : "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n" diff --git a/tests/baselines/reference/templateStringTermination1.types b/tests/baselines/reference/templateStringTermination1.types index fa40007c96a..e92463d6d80 100644 --- a/tests/baselines/reference/templateStringTermination1.types +++ b/tests/baselines/reference/templateStringTermination1.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringTermination1.ts === `` ->`` : string +>`` : "" diff --git a/tests/baselines/reference/templateStringTermination1_ES6.types b/tests/baselines/reference/templateStringTermination1_ES6.types index 9aa852a576a..1a873bf8304 100644 --- a/tests/baselines/reference/templateStringTermination1_ES6.types +++ b/tests/baselines/reference/templateStringTermination1_ES6.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringTermination1_ES6.ts === `` ->`` : string +>`` : "" diff --git a/tests/baselines/reference/templateStringTermination2.types b/tests/baselines/reference/templateStringTermination2.types index 205cb1e620a..fe7ab32c9f7 100644 --- a/tests/baselines/reference/templateStringTermination2.types +++ b/tests/baselines/reference/templateStringTermination2.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringTermination2.ts === `\\` ->`\\` : string +>`\\` : "\\" diff --git a/tests/baselines/reference/templateStringTermination2_ES6.types b/tests/baselines/reference/templateStringTermination2_ES6.types index bd8859a493f..364a684fd57 100644 --- a/tests/baselines/reference/templateStringTermination2_ES6.types +++ b/tests/baselines/reference/templateStringTermination2_ES6.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringTermination2_ES6.ts === `\\` ->`\\` : string +>`\\` : "\\" diff --git a/tests/baselines/reference/templateStringTermination3.types b/tests/baselines/reference/templateStringTermination3.types index bdb09e00dc3..7a1cb752a5a 100644 --- a/tests/baselines/reference/templateStringTermination3.types +++ b/tests/baselines/reference/templateStringTermination3.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringTermination3.ts === `\`` ->`\`` : string +>`\`` : "`" diff --git a/tests/baselines/reference/templateStringTermination3_ES6.types b/tests/baselines/reference/templateStringTermination3_ES6.types index 86ed6373bab..c30f77c60b9 100644 --- a/tests/baselines/reference/templateStringTermination3_ES6.types +++ b/tests/baselines/reference/templateStringTermination3_ES6.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringTermination3_ES6.ts === `\`` ->`\`` : string +>`\`` : "`" diff --git a/tests/baselines/reference/templateStringTermination4.types b/tests/baselines/reference/templateStringTermination4.types index 35a33014858..a700c1e473f 100644 --- a/tests/baselines/reference/templateStringTermination4.types +++ b/tests/baselines/reference/templateStringTermination4.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringTermination4.ts === `\\\\` ->`\\\\` : string +>`\\\\` : "\\\\" diff --git a/tests/baselines/reference/templateStringTermination4_ES6.types b/tests/baselines/reference/templateStringTermination4_ES6.types index 92c07f768a4..6f10132ac72 100644 --- a/tests/baselines/reference/templateStringTermination4_ES6.types +++ b/tests/baselines/reference/templateStringTermination4_ES6.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringTermination4_ES6.ts === `\\\\` ->`\\\\` : string +>`\\\\` : "\\\\" diff --git a/tests/baselines/reference/templateStringTermination5.types b/tests/baselines/reference/templateStringTermination5.types index 34c6cf9fb82..5f4a464b89e 100644 --- a/tests/baselines/reference/templateStringTermination5.types +++ b/tests/baselines/reference/templateStringTermination5.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringTermination5.ts === `\\\\\\` ->`\\\\\\` : string +>`\\\\\\` : "\\\\\\" diff --git a/tests/baselines/reference/templateStringTermination5_ES6.types b/tests/baselines/reference/templateStringTermination5_ES6.types index 193608250d9..685b2c1011a 100644 --- a/tests/baselines/reference/templateStringTermination5_ES6.types +++ b/tests/baselines/reference/templateStringTermination5_ES6.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringTermination5_ES6.ts === `\\\\\\` ->`\\\\\\` : string +>`\\\\\\` : "\\\\\\" diff --git a/tests/baselines/reference/templateStringWhitespaceEscapes1.types b/tests/baselines/reference/templateStringWhitespaceEscapes1.types index e554f45685b..e99205aebfe 100644 --- a/tests/baselines/reference/templateStringWhitespaceEscapes1.types +++ b/tests/baselines/reference/templateStringWhitespaceEscapes1.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringWhitespaceEscapes1.ts === `\t\n\v\f\r`; ->`\t\n\v\f\r` : string +>`\t\n\v\f\r` : "\t\n\v\f\r" diff --git a/tests/baselines/reference/templateStringWhitespaceEscapes1_ES6.types b/tests/baselines/reference/templateStringWhitespaceEscapes1_ES6.types index 3888c1be068..219c7c926dc 100644 --- a/tests/baselines/reference/templateStringWhitespaceEscapes1_ES6.types +++ b/tests/baselines/reference/templateStringWhitespaceEscapes1_ES6.types @@ -1,4 +1,4 @@ === tests/cases/conformance/es6/templates/templateStringWhitespaceEscapes1_ES6.ts === `\t\n\v\f\r`; ->`\t\n\v\f\r` : string +>`\t\n\v\f\r` : "\t\n\v\f\r" diff --git a/tests/baselines/reference/templateStringWhitespaceEscapes2.types b/tests/baselines/reference/templateStringWhitespaceEscapes2.types index 3a43fe5e5a2..e78cdaa2bc5 100644 --- a/tests/baselines/reference/templateStringWhitespaceEscapes2.types +++ b/tests/baselines/reference/templateStringWhitespaceEscapes2.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/templates/templateStringWhitespaceEscapes2.ts === // , , , , , `\u0009\u000B\u000C\u0020\u00A0\uFEFF`; ->`\u0009\u000B\u000C\u0020\u00A0\uFEFF` : string +>`\u0009\u000B\u000C\u0020\u00A0\uFEFF` : "\t\v\f  " diff --git a/tests/baselines/reference/templateStringWhitespaceEscapes2_ES6.types b/tests/baselines/reference/templateStringWhitespaceEscapes2_ES6.types index 1627945e1c4..a8239149ce4 100644 --- a/tests/baselines/reference/templateStringWhitespaceEscapes2_ES6.types +++ b/tests/baselines/reference/templateStringWhitespaceEscapes2_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/templates/templateStringWhitespaceEscapes2_ES6.ts === // , , , , , `\u0009\u000B\u000C\u0020\u00A0\uFEFF`; ->`\u0009\u000B\u000C\u0020\u00A0\uFEFF` : string +>`\u0009\u000B\u000C\u0020\u00A0\uFEFF` : "\t\v\f  " diff --git a/tests/baselines/reference/templateStringWithBackslashEscapes01.types b/tests/baselines/reference/templateStringWithBackslashEscapes01.types index e65c751b9d4..49bffce32f9 100644 --- a/tests/baselines/reference/templateStringWithBackslashEscapes01.types +++ b/tests/baselines/reference/templateStringWithBackslashEscapes01.types @@ -1,17 +1,17 @@ === tests/cases/conformance/es6/templates/templateStringWithBackslashEscapes01.ts === var a = `hello\world`; >a : string ->`hello\world` : string +>`hello\world` : "helloworld" var b = `hello\\world`; >b : string ->`hello\\world` : string +>`hello\\world` : "hello\\world" var c = `hello\\\world`; >c : string ->`hello\\\world` : string +>`hello\\\world` : "hello\\world" var d = `hello\\\\world`; >d : string ->`hello\\\\world` : string +>`hello\\\\world` : "hello\\\\world" diff --git a/tests/baselines/reference/templateStringWithBackslashEscapes01_ES6.types b/tests/baselines/reference/templateStringWithBackslashEscapes01_ES6.types index 9d1622609be..7482ed48501 100644 --- a/tests/baselines/reference/templateStringWithBackslashEscapes01_ES6.types +++ b/tests/baselines/reference/templateStringWithBackslashEscapes01_ES6.types @@ -1,17 +1,17 @@ === tests/cases/conformance/es6/templates/templateStringWithBackslashEscapes01_ES6.ts === var a = `hello\world`; >a : string ->`hello\world` : string +>`hello\world` : "helloworld" var b = `hello\\world`; >b : string ->`hello\\world` : string +>`hello\\world` : "hello\\world" var c = `hello\\\world`; >c : string ->`hello\\\world` : string +>`hello\\\world` : "hello\\world" var d = `hello\\\\world`; >d : string ->`hello\\\\world` : string +>`hello\\\\world` : "hello\\\\world" diff --git a/tests/baselines/reference/templateStringWithEmptyLiteralPortions.types b/tests/baselines/reference/templateStringWithEmptyLiteralPortions.types index f20ab1552e3..efc2723c270 100644 --- a/tests/baselines/reference/templateStringWithEmptyLiteralPortions.types +++ b/tests/baselines/reference/templateStringWithEmptyLiteralPortions.types @@ -1,7 +1,7 @@ === tests/cases/conformance/es6/templates/templateStringWithEmptyLiteralPortions.ts === var a = ``; >a : string ->`` : string +>`` : "" var b = `${ 0 }`; >b : string diff --git a/tests/baselines/reference/templateStringWithEmptyLiteralPortionsES6.types b/tests/baselines/reference/templateStringWithEmptyLiteralPortionsES6.types index 73eeeaae045..aad4f1eb095 100644 --- a/tests/baselines/reference/templateStringWithEmptyLiteralPortionsES6.types +++ b/tests/baselines/reference/templateStringWithEmptyLiteralPortionsES6.types @@ -1,7 +1,7 @@ === tests/cases/conformance/es6/templates/templateStringWithEmptyLiteralPortionsES6.ts === var a = ``; >a : string ->`` : string +>`` : "" var b = `${ 0 }`; >b : string diff --git a/tests/baselines/reference/templateStringWithPropertyAccess.types b/tests/baselines/reference/templateStringWithPropertyAccess.types index 21749c848da..faa2fda3888 100644 --- a/tests/baselines/reference/templateStringWithPropertyAccess.types +++ b/tests/baselines/reference/templateStringWithPropertyAccess.types @@ -5,5 +5,5 @@ >`abc${0}abc` : string >0 : 0 >indexOf : (searchString: string, position?: number) => number ->`abc` : string +>`abc` : "abc" diff --git a/tests/baselines/reference/templateStringWithPropertyAccessES6.types b/tests/baselines/reference/templateStringWithPropertyAccessES6.types index 3e297e6bf07..36a84452808 100644 --- a/tests/baselines/reference/templateStringWithPropertyAccessES6.types +++ b/tests/baselines/reference/templateStringWithPropertyAccessES6.types @@ -5,5 +5,5 @@ >`abc${0}abc` : string >0 : 0 >indexOf : (searchString: string, position?: number) => number ->`abc` : string +>`abc` : "abc" diff --git a/tests/baselines/reference/thisTypeInTypePredicate.js b/tests/baselines/reference/thisTypeInTypePredicate.js new file mode 100644 index 00000000000..1b9a48daeea --- /dev/null +++ b/tests/baselines/reference/thisTypeInTypePredicate.js @@ -0,0 +1,7 @@ +//// [thisTypeInTypePredicate.ts] +declare function filter(f: (this: void, x: any) => x is S): S[]; +const numbers = filter((x): x is number => 'number' == typeof x) + + +//// [thisTypeInTypePredicate.js] +var numbers = filter(function (x) { return 'number' == typeof x; }); diff --git a/tests/baselines/reference/thisTypeInTypePredicate.symbols b/tests/baselines/reference/thisTypeInTypePredicate.symbols new file mode 100644 index 00000000000..ff18f450a0e --- /dev/null +++ b/tests/baselines/reference/thisTypeInTypePredicate.symbols @@ -0,0 +1,18 @@ +=== tests/cases/conformance/types/thisType/thisTypeInTypePredicate.ts === +declare function filter(f: (this: void, x: any) => x is S): S[]; +>filter : Symbol(filter, Decl(thisTypeInTypePredicate.ts, 0, 0)) +>S : Symbol(S, Decl(thisTypeInTypePredicate.ts, 0, 24)) +>f : Symbol(f, Decl(thisTypeInTypePredicate.ts, 0, 27)) +>this : Symbol(this, Decl(thisTypeInTypePredicate.ts, 0, 31)) +>x : Symbol(x, Decl(thisTypeInTypePredicate.ts, 0, 42)) +>x : Symbol(x, Decl(thisTypeInTypePredicate.ts, 0, 42)) +>S : Symbol(S, Decl(thisTypeInTypePredicate.ts, 0, 24)) +>S : Symbol(S, Decl(thisTypeInTypePredicate.ts, 0, 24)) + +const numbers = filter((x): x is number => 'number' == typeof x) +>numbers : Symbol(numbers, Decl(thisTypeInTypePredicate.ts, 1, 5)) +>filter : Symbol(filter, Decl(thisTypeInTypePredicate.ts, 0, 0)) +>x : Symbol(x, Decl(thisTypeInTypePredicate.ts, 1, 32)) +>x : Symbol(x, Decl(thisTypeInTypePredicate.ts, 1, 32)) +>x : Symbol(x, Decl(thisTypeInTypePredicate.ts, 1, 32)) + diff --git a/tests/baselines/reference/thisTypeInTypePredicate.types b/tests/baselines/reference/thisTypeInTypePredicate.types new file mode 100644 index 00000000000..cc92ce811b8 --- /dev/null +++ b/tests/baselines/reference/thisTypeInTypePredicate.types @@ -0,0 +1,23 @@ +=== tests/cases/conformance/types/thisType/thisTypeInTypePredicate.ts === +declare function filter(f: (this: void, x: any) => x is S): S[]; +>filter : (f: (this: void, x: any) => x is S) => S[] +>S : S +>f : (this: void, x: any) => x is S +>this : void +>x : any +>x : any +>S : S +>S : S + +const numbers = filter((x): x is number => 'number' == typeof x) +>numbers : number[] +>filter((x): x is number => 'number' == typeof x) : number[] +>filter : (f: (this: void, x: any) => x is S) => S[] +>(x): x is number => 'number' == typeof x : (this: void, x: any) => x is number +>x : any +>x : any +>'number' == typeof x : boolean +>'number' : "number" +>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : any + diff --git a/tests/baselines/reference/tryStatements.js b/tests/baselines/reference/tryStatements.js index 723014c1b52..64fe2cf06a7 100644 --- a/tests/baselines/reference/tryStatements.js +++ b/tests/baselines/reference/tryStatements.js @@ -1,26 +1,47 @@ //// [tryStatements.ts] function fn() { - try { + try { } catch { } - } catch (x) { - var x: any; + try { } catch { + try { } catch { + try { } catch { } + } + try { } catch { } } + try { } catch (x) { var x: any; } + try { } finally { } - try { }catch(z){ } finally { } + try { } catch { } finally { } + + try { } catch (z) { } finally { } } //// [tryStatements.js] function fn() { - try { + try { } + catch (_a) { } + try { } + catch (_b) { + try { } + catch (_c) { + try { } + catch (_d) { } + } + try { } + catch (_e) { } } + try { } catch (x) { var x; } try { } finally { } try { } + catch (_f) { } + finally { } + try { } catch (z) { } finally { } } diff --git a/tests/baselines/reference/tryStatements.symbols b/tests/baselines/reference/tryStatements.symbols index 945ca2d9d88..69e9918a98b 100644 --- a/tests/baselines/reference/tryStatements.symbols +++ b/tests/baselines/reference/tryStatements.symbols @@ -2,17 +2,23 @@ function fn() { >fn : Symbol(fn, Decl(tryStatements.ts, 0, 0)) - try { + try { } catch { } - } catch (x) { ->x : Symbol(x, Decl(tryStatements.ts, 3, 13)) - - var x: any; ->x : Symbol(x, Decl(tryStatements.ts, 4, 11)) + try { } catch { + try { } catch { + try { } catch { } + } + try { } catch { } } + try { } catch (x) { var x: any; } +>x : Symbol(x, Decl(tryStatements.ts, 10, 19)) +>x : Symbol(x, Decl(tryStatements.ts, 10, 27)) + try { } finally { } - try { }catch(z){ } finally { } ->z : Symbol(z, Decl(tryStatements.ts, 9, 17)) + try { } catch { } finally { } + + try { } catch (z) { } finally { } +>z : Symbol(z, Decl(tryStatements.ts, 16, 19)) } diff --git a/tests/baselines/reference/tryStatements.types b/tests/baselines/reference/tryStatements.types index 07bc3997d83..05c02568135 100644 --- a/tests/baselines/reference/tryStatements.types +++ b/tests/baselines/reference/tryStatements.types @@ -2,17 +2,23 @@ function fn() { >fn : () => void - try { + try { } catch { } - } catch (x) { ->x : any - - var x: any; ->x : any + try { } catch { + try { } catch { + try { } catch { } + } + try { } catch { } } + try { } catch (x) { var x: any; } +>x : any +>x : any + try { } finally { } - try { }catch(z){ } finally { } + try { } catch { } finally { } + + try { } catch (z) { } finally { } >z : any } diff --git a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json index b79b4f0f181..0f5b2378468 100644 --- a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json @@ -39,6 +39,7 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json index ecbaaf9d961..a545124a723 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -39,6 +39,7 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json index 321547908d9..b53ac2d8552 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -39,6 +39,7 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json index ec0ac6e4c32..4e06e06d159 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json @@ -39,6 +39,7 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json index 655ece4d6d0..94808d89ed0 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json @@ -39,6 +39,7 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json index b79b4f0f181..0f5b2378468 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json @@ -39,6 +39,7 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json index 81b1636b8cb..d165b0f2775 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -39,6 +39,7 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json index 66acf6df045..2a169b3aaaf 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -39,6 +39,7 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ "types": ["jquery","mocha"] /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ /* Source Map Options */ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ diff --git a/tests/baselines/reference/tsxUnionSpread.js b/tests/baselines/reference/tsxUnionSpread.js new file mode 100644 index 00000000000..f84e6ab2166 --- /dev/null +++ b/tests/baselines/reference/tsxUnionSpread.js @@ -0,0 +1,38 @@ +//// [index.tsx] +namespace JSX { + export interface Element {} +} + +export type CatInfo = { type: 'Cat'; subType: string; }; +export type DogInfo = { type: 'Dog'; }; +export type AnimalInfo = CatInfo | DogInfo; + +function AnimalComponent(info: AnimalInfo): JSX.Element { + return undefined as any; +} + +function getProps(): AnimalInfo { + // this may be from server or whatever ... + return { type: 'Cat', subType: 'Large' }; +} + +var props:AnimalInfo = getProps(); +var component = + +var props2:AnimalInfo = { type: 'Cat', subType: 'Large' }; +var component2 = + +//// [index.jsx] +"use strict"; +exports.__esModule = true; +function AnimalComponent(info) { + return undefined; +} +function getProps() { + // this may be from server or whatever ... + return { type: 'Cat', subType: 'Large' }; +} +var props = getProps(); +var component = ; +var props2 = { type: 'Cat', subType: 'Large' }; +var component2 = ; diff --git a/tests/baselines/reference/tsxUnionSpread.symbols b/tests/baselines/reference/tsxUnionSpread.symbols new file mode 100644 index 00000000000..fa838773a8a --- /dev/null +++ b/tests/baselines/reference/tsxUnionSpread.symbols @@ -0,0 +1,64 @@ +=== tests/cases/compiler/index.tsx === +namespace JSX { +>JSX : Symbol(JSX, Decl(index.tsx, 0, 0)) + + export interface Element {} +>Element : Symbol(Element, Decl(index.tsx, 0, 15)) +} + +export type CatInfo = { type: 'Cat'; subType: string; }; +>CatInfo : Symbol(CatInfo, Decl(index.tsx, 2, 1)) +>type : Symbol(type, Decl(index.tsx, 4, 23)) +>subType : Symbol(subType, Decl(index.tsx, 4, 36)) + +export type DogInfo = { type: 'Dog'; }; +>DogInfo : Symbol(DogInfo, Decl(index.tsx, 4, 56)) +>type : Symbol(type, Decl(index.tsx, 5, 23)) + +export type AnimalInfo = CatInfo | DogInfo; +>AnimalInfo : Symbol(AnimalInfo, Decl(index.tsx, 5, 39)) +>CatInfo : Symbol(CatInfo, Decl(index.tsx, 2, 1)) +>DogInfo : Symbol(DogInfo, Decl(index.tsx, 4, 56)) + +function AnimalComponent(info: AnimalInfo): JSX.Element { +>AnimalComponent : Symbol(AnimalComponent, Decl(index.tsx, 6, 43)) +>info : Symbol(info, Decl(index.tsx, 8, 25)) +>AnimalInfo : Symbol(AnimalInfo, Decl(index.tsx, 5, 39)) +>JSX : Symbol(JSX, Decl(index.tsx, 0, 0)) +>Element : Symbol(JSX.Element, Decl(index.tsx, 0, 15)) + + return undefined as any; +>undefined : Symbol(undefined) +} + +function getProps(): AnimalInfo { +>getProps : Symbol(getProps, Decl(index.tsx, 10, 1)) +>AnimalInfo : Symbol(AnimalInfo, Decl(index.tsx, 5, 39)) + + // this may be from server or whatever ... + return { type: 'Cat', subType: 'Large' }; +>type : Symbol(type, Decl(index.tsx, 14, 12)) +>subType : Symbol(subType, Decl(index.tsx, 14, 25)) +} + +var props:AnimalInfo = getProps(); +>props : Symbol(props, Decl(index.tsx, 17, 3)) +>AnimalInfo : Symbol(AnimalInfo, Decl(index.tsx, 5, 39)) +>getProps : Symbol(getProps, Decl(index.tsx, 10, 1)) + +var component = +>component : Symbol(component, Decl(index.tsx, 18, 3)) +>AnimalComponent : Symbol(AnimalComponent, Decl(index.tsx, 6, 43)) +>props : Symbol(props, Decl(index.tsx, 17, 3)) + +var props2:AnimalInfo = { type: 'Cat', subType: 'Large' }; +>props2 : Symbol(props2, Decl(index.tsx, 20, 3)) +>AnimalInfo : Symbol(AnimalInfo, Decl(index.tsx, 5, 39)) +>type : Symbol(type, Decl(index.tsx, 20, 25)) +>subType : Symbol(subType, Decl(index.tsx, 20, 38)) + +var component2 = +>component2 : Symbol(component2, Decl(index.tsx, 21, 3)) +>AnimalComponent : Symbol(AnimalComponent, Decl(index.tsx, 6, 43)) +>props2 : Symbol(props2, Decl(index.tsx, 20, 3)) + diff --git a/tests/baselines/reference/tsxUnionSpread.types b/tests/baselines/reference/tsxUnionSpread.types new file mode 100644 index 00000000000..11e6308fe18 --- /dev/null +++ b/tests/baselines/reference/tsxUnionSpread.types @@ -0,0 +1,74 @@ +=== tests/cases/compiler/index.tsx === +namespace JSX { +>JSX : any + + export interface Element {} +>Element : Element +} + +export type CatInfo = { type: 'Cat'; subType: string; }; +>CatInfo : CatInfo +>type : "Cat" +>subType : string + +export type DogInfo = { type: 'Dog'; }; +>DogInfo : DogInfo +>type : "Dog" + +export type AnimalInfo = CatInfo | DogInfo; +>AnimalInfo : AnimalInfo +>CatInfo : CatInfo +>DogInfo : DogInfo + +function AnimalComponent(info: AnimalInfo): JSX.Element { +>AnimalComponent : (info: AnimalInfo) => JSX.Element +>info : AnimalInfo +>AnimalInfo : AnimalInfo +>JSX : any +>Element : JSX.Element + + return undefined as any; +>undefined as any : any +>undefined : undefined +} + +function getProps(): AnimalInfo { +>getProps : () => AnimalInfo +>AnimalInfo : AnimalInfo + + // this may be from server or whatever ... + return { type: 'Cat', subType: 'Large' }; +>{ type: 'Cat', subType: 'Large' } : { type: "Cat"; subType: string; } +>type : string +>'Cat' : "Cat" +>subType : string +>'Large' : "Large" +} + +var props:AnimalInfo = getProps(); +>props : AnimalInfo +>AnimalInfo : AnimalInfo +>getProps() : AnimalInfo +>getProps : () => AnimalInfo + +var component = +>component : any +> : any +>AnimalComponent : (info: AnimalInfo) => JSX.Element +>props : AnimalInfo + +var props2:AnimalInfo = { type: 'Cat', subType: 'Large' }; +>props2 : AnimalInfo +>AnimalInfo : AnimalInfo +>{ type: 'Cat', subType: 'Large' } : { type: "Cat"; subType: string; } +>type : string +>'Cat' : "Cat" +>subType : string +>'Large' : "Large" + +var component2 = +>component2 : any +> : any +>AnimalComponent : (info: AnimalInfo) => JSX.Element +>props2 : CatInfo + diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.types b/tests/baselines/reference/typeGuardIntersectionTypes.types index b8ba6b318d8..98a8388f21e 100644 --- a/tests/baselines/reference/typeGuardIntersectionTypes.types +++ b/tests/baselines/reference/typeGuardIntersectionTypes.types @@ -210,7 +210,7 @@ function identifyBeast(beast: Beast) { log(`pegasus - 4 legs, wings`); >log(`pegasus - 4 legs, wings`) : void >log : (s: string) => void ->`pegasus - 4 legs, wings` : string +>`pegasus - 4 legs, wings` : "pegasus - 4 legs, wings" } else if (beast.legs === 2) { >beast.legs === 2 : boolean @@ -222,7 +222,7 @@ function identifyBeast(beast: Beast) { log(`bird - 2 legs, wings`); >log(`bird - 2 legs, wings`) : void >log : (s: string) => void ->`bird - 2 legs, wings` : string +>`bird - 2 legs, wings` : "bird - 2 legs, wings" } else { log(`unknown - ${beast.legs} legs, wings`); @@ -257,13 +257,13 @@ function identifyBeast(beast: Beast) { log(`quetzalcoatl - no legs, wings`) >log(`quetzalcoatl - no legs, wings`) : void >log : (s: string) => void ->`quetzalcoatl - no legs, wings` : string +>`quetzalcoatl - no legs, wings` : "quetzalcoatl - no legs, wings" } else { log(`snake - no legs, no wings`) >log(`snake - no legs, no wings`) : void >log : (s: string) => void ->`snake - no legs, no wings` : string +>`snake - no legs, no wings` : "snake - no legs, no wings" } } } diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.js b/tests/baselines/reference/typeGuardsInConditionalExpression.js index 8be83a887b1..fa44556c199 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.js +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.js @@ -138,7 +138,7 @@ function foo8(x) { var b; return typeof x === "string" ? x === "hello" - : ((b = x) &&// number | boolean + : ((b = x) && // number | boolean (typeof x === "boolean" ? x // boolean : x == 10)); // boolean diff --git a/tests/baselines/reference/typeParameterExtendsPrimitive.js b/tests/baselines/reference/typeParameterExtendsPrimitive.js new file mode 100644 index 00000000000..1774db19f6e --- /dev/null +++ b/tests/baselines/reference/typeParameterExtendsPrimitive.js @@ -0,0 +1,51 @@ +//// [typeParameterExtendsPrimitive.ts] +// #14473 +function f() { + var t: T; + var v = { + [t]: 0 + } + return t + t; +} + +// #15501 +interface I { x: number } +type IdMap = { [P in keyof T]: T[P] }; +function g(i: IdMap) { + const n: number = i.x; + return i.x * 2; +} + +// #17069 +function h, K extends string>(array: T[], prop: K): number { + let result = 0; + for (const v of array) { + result += v[prop]; + } + return result; +} + + +//// [typeParameterExtendsPrimitive.js] +// #14473 +function f() { + var t; + var v = (_a = {}, + _a[t] = 0, + _a); + return t + t; + var _a; +} +function g(i) { + var n = i.x; + return i.x * 2; +} +// #17069 +function h(array, prop) { + var result = 0; + for (var _i = 0, array_1 = array; _i < array_1.length; _i++) { + var v = array_1[_i]; + result += v[prop]; + } + return result; +} diff --git a/tests/baselines/reference/typeParameterExtendsPrimitive.symbols b/tests/baselines/reference/typeParameterExtendsPrimitive.symbols new file mode 100644 index 00000000000..acac75272ac --- /dev/null +++ b/tests/baselines/reference/typeParameterExtendsPrimitive.symbols @@ -0,0 +1,82 @@ +=== tests/cases/compiler/typeParameterExtendsPrimitive.ts === +// #14473 +function f() { +>f : Symbol(f, Decl(typeParameterExtendsPrimitive.ts, 0, 0)) +>T : Symbol(T, Decl(typeParameterExtendsPrimitive.ts, 1, 11)) + + var t: T; +>t : Symbol(t, Decl(typeParameterExtendsPrimitive.ts, 2, 7)) +>T : Symbol(T, Decl(typeParameterExtendsPrimitive.ts, 1, 11)) + + var v = { +>v : Symbol(v, Decl(typeParameterExtendsPrimitive.ts, 3, 7)) + + [t]: 0 +>t : Symbol(t, Decl(typeParameterExtendsPrimitive.ts, 2, 7)) + } + return t + t; +>t : Symbol(t, Decl(typeParameterExtendsPrimitive.ts, 2, 7)) +>t : Symbol(t, Decl(typeParameterExtendsPrimitive.ts, 2, 7)) +} + +// #15501 +interface I { x: number } +>I : Symbol(I, Decl(typeParameterExtendsPrimitive.ts, 7, 1)) +>x : Symbol(I.x, Decl(typeParameterExtendsPrimitive.ts, 10, 13)) + +type IdMap = { [P in keyof T]: T[P] }; +>IdMap : Symbol(IdMap, Decl(typeParameterExtendsPrimitive.ts, 10, 25)) +>T : Symbol(T, Decl(typeParameterExtendsPrimitive.ts, 11, 11)) +>P : Symbol(P, Decl(typeParameterExtendsPrimitive.ts, 11, 19)) +>T : Symbol(T, Decl(typeParameterExtendsPrimitive.ts, 11, 11)) +>T : Symbol(T, Decl(typeParameterExtendsPrimitive.ts, 11, 11)) +>P : Symbol(P, Decl(typeParameterExtendsPrimitive.ts, 11, 19)) + +function g(i: IdMap) { +>g : Symbol(g, Decl(typeParameterExtendsPrimitive.ts, 11, 41)) +>T : Symbol(T, Decl(typeParameterExtendsPrimitive.ts, 12, 11)) +>I : Symbol(I, Decl(typeParameterExtendsPrimitive.ts, 7, 1)) +>i : Symbol(i, Decl(typeParameterExtendsPrimitive.ts, 12, 24)) +>IdMap : Symbol(IdMap, Decl(typeParameterExtendsPrimitive.ts, 10, 25)) +>T : Symbol(T, Decl(typeParameterExtendsPrimitive.ts, 12, 11)) + + const n: number = i.x; +>n : Symbol(n, Decl(typeParameterExtendsPrimitive.ts, 13, 9)) +>i.x : Symbol(x, Decl(typeParameterExtendsPrimitive.ts, 10, 13)) +>i : Symbol(i, Decl(typeParameterExtendsPrimitive.ts, 12, 24)) +>x : Symbol(x, Decl(typeParameterExtendsPrimitive.ts, 10, 13)) + + return i.x * 2; +>i.x : Symbol(x, Decl(typeParameterExtendsPrimitive.ts, 10, 13)) +>i : Symbol(i, Decl(typeParameterExtendsPrimitive.ts, 12, 24)) +>x : Symbol(x, Decl(typeParameterExtendsPrimitive.ts, 10, 13)) +} + +// #17069 +function h, K extends string>(array: T[], prop: K): number { +>h : Symbol(h, Decl(typeParameterExtendsPrimitive.ts, 15, 1)) +>T : Symbol(T, Decl(typeParameterExtendsPrimitive.ts, 18, 11)) +>Record : Symbol(Record, Decl(lib.d.ts, --, --)) +>K : Symbol(K, Decl(typeParameterExtendsPrimitive.ts, 18, 39)) +>K : Symbol(K, Decl(typeParameterExtendsPrimitive.ts, 18, 39)) +>array : Symbol(array, Decl(typeParameterExtendsPrimitive.ts, 18, 58)) +>T : Symbol(T, Decl(typeParameterExtendsPrimitive.ts, 18, 11)) +>prop : Symbol(prop, Decl(typeParameterExtendsPrimitive.ts, 18, 69)) +>K : Symbol(K, Decl(typeParameterExtendsPrimitive.ts, 18, 39)) + + let result = 0; +>result : Symbol(result, Decl(typeParameterExtendsPrimitive.ts, 19, 7)) + + for (const v of array) { +>v : Symbol(v, Decl(typeParameterExtendsPrimitive.ts, 20, 14)) +>array : Symbol(array, Decl(typeParameterExtendsPrimitive.ts, 18, 58)) + + result += v[prop]; +>result : Symbol(result, Decl(typeParameterExtendsPrimitive.ts, 19, 7)) +>v : Symbol(v, Decl(typeParameterExtendsPrimitive.ts, 20, 14)) +>prop : Symbol(prop, Decl(typeParameterExtendsPrimitive.ts, 18, 69)) + } + return result; +>result : Symbol(result, Decl(typeParameterExtendsPrimitive.ts, 19, 7)) +} + diff --git a/tests/baselines/reference/typeParameterExtendsPrimitive.types b/tests/baselines/reference/typeParameterExtendsPrimitive.types new file mode 100644 index 00000000000..1e8eb7f40e0 --- /dev/null +++ b/tests/baselines/reference/typeParameterExtendsPrimitive.types @@ -0,0 +1,90 @@ +=== tests/cases/compiler/typeParameterExtendsPrimitive.ts === +// #14473 +function f() { +>f : () => number +>T : T + + var t: T; +>t : T +>T : T + + var v = { +>v : { [x: number]: number; } +>{ [t]: 0 } : { [x: number]: number; } + + [t]: 0 +>t : T +>0 : 0 + } + return t + t; +>t + t : number +>t : T +>t : T +} + +// #15501 +interface I { x: number } +>I : I +>x : number + +type IdMap = { [P in keyof T]: T[P] }; +>IdMap : IdMap +>T : T +>P : P +>T : T +>T : T +>P : P + +function g(i: IdMap) { +>g : (i: IdMap) => number +>T : T +>I : I +>i : IdMap +>IdMap : IdMap +>T : T + + const n: number = i.x; +>n : number +>i.x : T["x"] +>i : IdMap +>x : T["x"] + + return i.x * 2; +>i.x * 2 : number +>i.x : T["x"] +>i : IdMap +>x : T["x"] +>2 : 2 +} + +// #17069 +function h, K extends string>(array: T[], prop: K): number { +>h : , K extends string>(array: T[], prop: K) => number +>T : T +>Record : Record +>K : K +>K : K +>array : T[] +>T : T +>prop : K +>K : K + + let result = 0; +>result : number +>0 : 0 + + for (const v of array) { +>v : T +>array : T[] + + result += v[prop]; +>result += v[prop] : number +>result : number +>v[prop] : T[K] +>v : T +>prop : K + } + return result; +>result : number +} + diff --git a/tests/baselines/reference/typeReferenceDirectives1.js b/tests/baselines/reference/typeReferenceDirectives1.js index a0865e544a8..9ff2e66ff63 100644 --- a/tests/baselines/reference/typeReferenceDirectives1.js +++ b/tests/baselines/reference/typeReferenceDirectives1.js @@ -10,6 +10,7 @@ interface A { } //// [app.js] +/// //// [app.d.ts] diff --git a/tests/baselines/reference/typeReferenceDirectives3.js b/tests/baselines/reference/typeReferenceDirectives3.js index b320e602237..28d57f93fe5 100644 --- a/tests/baselines/reference/typeReferenceDirectives3.js +++ b/tests/baselines/reference/typeReferenceDirectives3.js @@ -16,6 +16,7 @@ interface A { } //// [app.js] +/// /// diff --git a/tests/baselines/reference/typeofOperatorWithAnyOtherType.errors.txt b/tests/baselines/reference/typeofOperatorWithAnyOtherType.errors.txt index 3680b1be60f..9357cf4e4cc 100644 --- a/tests/baselines/reference/typeofOperatorWithAnyOtherType.errors.txt +++ b/tests/baselines/reference/typeofOperatorWithAnyOtherType.errors.txt @@ -1,9 +1,6 @@ -tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(46,32): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(46,39): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(47,32): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(47,39): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(48,32): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(48,44): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(46,32): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. +tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(47,32): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. +tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(48,32): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(58,1): error TS2695: Left side of comma operator is unused and has no side effects. tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(68,1): error TS7028: Unused label. tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(69,1): error TS7028: Unused label. @@ -14,7 +11,7 @@ tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperator tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts(74,1): error TS7028: Unused label. -==== tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts (14 errors) ==== +==== tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperatorWithAnyOtherType.ts (11 errors) ==== // typeof operator on any type var ANY: any; @@ -61,20 +58,14 @@ tests/cases/conformance/expressions/unaryOperators/typeofOperator/typeofOperator var ResultIsString15 = typeof A.foo(); var ResultIsString16 = typeof (ANY + ANY1); var ResultIsString17 = typeof (null + undefined); - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. var ResultIsString18 = typeof (null + null); - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. var ResultIsString19 = typeof (undefined + undefined); - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. // multiple typeof operators var ResultIsString20 = typeof typeof ANY; diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates01_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates01_ES5.types index 427287812c1..983c1054cd8 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates01_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates01_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates01_ES5.ts === var x = `\u{0}`; >x : string ->`\u{0}` : string +>`\u{0}` : "\0" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates01_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates01_ES6.types index 482a6d5feab..9b28d3c69b0 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates01_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates01_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates01_ES6.ts === var x = `\u{0}`; >x : string ->`\u{0}` : string +>`\u{0}` : "\0" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates02_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates02_ES5.types index a6ff5ebdeb9..644df97b3d7 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates02_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates02_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates02_ES5.ts === var x = `\u{00}`; >x : string ->`\u{00}` : string +>`\u{00}` : "\0" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates02_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates02_ES6.types index badf78449a7..47ca4916deb 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates02_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates02_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates02_ES6.ts === var x = `\u{00}`; >x : string ->`\u{00}` : string +>`\u{00}` : "\0" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates03_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates03_ES5.types index ebd0ada1825..95444cd2a23 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates03_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates03_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates03_ES5.ts === var x = `\u{0000}`; >x : string ->`\u{0000}` : string +>`\u{0000}` : "\0" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates03_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates03_ES6.types index 8370399d2a4..05290b9abc2 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates03_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates03_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates03_ES6.ts === var x = `\u{0000}`; >x : string ->`\u{0000}` : string +>`\u{0000}` : "\0" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates04_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates04_ES5.types index 0cefe0224e0..4cae06cae30 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates04_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates04_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates04_ES5.ts === var x = `\u{00000000}`; >x : string ->`\u{00000000}` : string +>`\u{00000000}` : "\0" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates04_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates04_ES6.types index b3e9768be6d..cbe80b6df57 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates04_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates04_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates04_ES6.ts === var x = `\u{00000000}`; >x : string ->`\u{00000000}` : string +>`\u{00000000}` : "\0" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates05_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates05_ES5.types index f7e0d4b9f10..cde2db429c8 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates05_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates05_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates05_ES5.ts === var x = `\u{48}\u{65}\u{6c}\u{6c}\u{6f}\u{20}\u{77}\u{6f}\u{72}\u{6c}\u{64}`; >x : string ->`\u{48}\u{65}\u{6c}\u{6c}\u{6f}\u{20}\u{77}\u{6f}\u{72}\u{6c}\u{64}` : string +>`\u{48}\u{65}\u{6c}\u{6c}\u{6f}\u{20}\u{77}\u{6f}\u{72}\u{6c}\u{64}` : "Hello world" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates05_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates05_ES6.types index 4fddcde17d1..70ad36cd515 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates05_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates05_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates05_ES6.ts === var x = `\u{48}\u{65}\u{6c}\u{6c}\u{6f}\u{20}\u{77}\u{6f}\u{72}\u{6c}\u{64}`; >x : string ->`\u{48}\u{65}\u{6c}\u{6c}\u{6f}\u{20}\u{77}\u{6f}\u{72}\u{6c}\u{64}` : string +>`\u{48}\u{65}\u{6c}\u{6c}\u{6f}\u{20}\u{77}\u{6f}\u{72}\u{6c}\u{64}` : "Hello world" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates06_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates06_ES5.types index 4b3326b1384..05faa73bcdf 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates06_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates06_ES5.types @@ -3,5 +3,5 @@ // 1. Assert: 0 ≤ cp ≤ 0x10FFFF. var x = `\u{10FFFF}`; >x : string ->`\u{10FFFF}` : string +>`\u{10FFFF}` : "􏿿" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates06_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates06_ES6.types index 3a073b8b8f0..4bd9eeb279f 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates06_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates06_ES6.types @@ -3,5 +3,5 @@ // 1. Assert: 0 ≤ cp ≤ 0x10FFFF. var x = `\u{10FFFF}`; >x : string ->`\u{10FFFF}` : string +>`\u{10FFFF}` : "􏿿" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates08_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates08_ES5.types index ed0e43e6181..b3932c26b79 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates08_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates08_ES5.types @@ -4,5 +4,5 @@ // (FFFF == 65535) var x = `\u{FFFF}`; >x : string ->`\u{FFFF}` : string +>`\u{FFFF}` : "￿" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates08_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates08_ES6.types index b2cb3e2df43..fdb9537597c 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates08_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates08_ES6.types @@ -4,5 +4,5 @@ // (FFFF == 65535) var x = `\u{FFFF}`; >x : string ->`\u{FFFF}` : string +>`\u{FFFF}` : "￿" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates09_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates09_ES5.types index d0b79aa8431..5a44659ab44 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates09_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates09_ES5.types @@ -4,5 +4,5 @@ // (10000 == 65536) var x = `\u{10000}`; >x : string ->`\u{10000}` : string +>`\u{10000}` : "𐀀" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates09_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates09_ES6.types index 552ac1d367e..2d85454045a 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates09_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates09_ES6.types @@ -4,5 +4,5 @@ // (10000 == 65536) var x = `\u{10000}`; >x : string ->`\u{10000}` : string +>`\u{10000}` : "𐀀" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates10_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates10_ES5.types index dfb20480849..dce17fdb4e0 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates10_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates10_ES5.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = `\u{D800}`; >x : string ->`\u{D800}` : string +>`\u{D800}` : "�" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates10_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates10_ES6.types index 8fccf89a6ed..80e564effa5 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates10_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates10_ES6.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = `\u{D800}`; >x : string ->`\u{D800}` : string +>`\u{D800}` : "�" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates11_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates11_ES5.types index cf9540d7613..85ded8bf56e 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates11_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates11_ES5.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = `\u{DC00}`; >x : string ->`\u{DC00}` : string +>`\u{DC00}` : "�" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates11_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates11_ES6.types index 94512a38cb7..52a8fb383cf 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates11_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates11_ES6.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = `\u{DC00}`; >x : string ->`\u{DC00}` : string +>`\u{DC00}` : "�" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates13_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates13_ES5.types index 9178f005bb6..22f123d0ff2 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates13_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates13_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates13_ES5.ts === var x = `\u{DDDDD}`; >x : string ->`\u{DDDDD}` : string +>`\u{DDDDD}` : "󝷝" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates13_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates13_ES6.types index bbcd9ba8ae4..afd5dc995ae 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates13_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates13_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates13_ES6.ts === var x = `\u{DDDDD}`; >x : string ->`\u{DDDDD}` : string +>`\u{DDDDD}` : "󝷝" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates15_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates15_ES5.types index f38e8e6f30e..5e96bc878a6 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates15_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates15_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates15_ES5.ts === var x = `\u{abcd}\u{ef12}\u{3456}\u{7890}`; >x : string ->`\u{abcd}\u{ef12}\u{3456}\u{7890}` : string +>`\u{abcd}\u{ef12}\u{3456}\u{7890}` : "ꯍ㑖碐" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates15_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates15_ES6.types index 53e7fb1e470..467a6271b8e 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates15_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates15_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates15_ES6.ts === var x = `\u{abcd}\u{ef12}\u{3456}\u{7890}`; >x : string ->`\u{abcd}\u{ef12}\u{3456}\u{7890}` : string +>`\u{abcd}\u{ef12}\u{3456}\u{7890}` : "ꯍ㑖碐" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates16_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates16_ES5.types index be9f781bb9b..d145447e8de 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates16_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates16_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates16_ES5.ts === var x = `\u{ABCD}\u{EF12}\u{3456}\u{7890}`; >x : string ->`\u{ABCD}\u{EF12}\u{3456}\u{7890}` : string +>`\u{ABCD}\u{EF12}\u{3456}\u{7890}` : "ꯍ㑖碐" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates16_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates16_ES6.types index e33e97ed07c..eedcf8823c4 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates16_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates16_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates16_ES6.ts === var x = `\u{ABCD}\u{EF12}\u{3456}\u{7890}`; >x : string ->`\u{ABCD}\u{EF12}\u{3456}\u{7890}` : string +>`\u{ABCD}\u{EF12}\u{3456}\u{7890}` : "ꯍ㑖碐" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates18_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates18_ES5.types index 16250ea16dc..c3e26e19b88 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates18_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates18_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates18_ES5.ts === var x = `\u{65}\u{65}`; >x : string ->`\u{65}\u{65}` : string +>`\u{65}\u{65}` : "ee" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates18_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates18_ES6.types index fe818fdf47f..f7c1e53d0c7 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates18_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates18_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInTemplates18_ES6.ts === var x = `\u{65}\u{65}`; >x : string ->`\u{65}\u{65}` : string +>`\u{65}\u{65}` : "ee" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates20_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates20_ES5.types index 9117e3be130..ddb24f5de93 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates20_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates20_ES5.types @@ -2,5 +2,5 @@ var x = `\u{48}\u{65}\u{6c}\u{6c}\u{6f}${`\u{20}\u{020}\u{0020}\u{000020}`}\u{77}\u{6f}\u{72}\u{6c}\u{64}`; >x : string >`\u{48}\u{65}\u{6c}\u{6c}\u{6f}${`\u{20}\u{020}\u{0020}\u{000020}`}\u{77}\u{6f}\u{72}\u{6c}\u{64}` : string ->`\u{20}\u{020}\u{0020}\u{000020}` : string +>`\u{20}\u{020}\u{0020}\u{000020}` : " " diff --git a/tests/baselines/reference/unicodeExtendedEscapesInTemplates20_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInTemplates20_ES6.types index 6ff269e2806..8244bc5b7fd 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInTemplates20_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInTemplates20_ES6.types @@ -2,5 +2,5 @@ var x = `\u{48}\u{65}\u{6c}\u{6c}\u{6f}${`\u{20}\u{020}\u{0020}\u{000020}`}\u{77}\u{6f}\u{72}\u{6c}\u{64}`; >x : string >`\u{48}\u{65}\u{6c}\u{6c}\u{6f}${`\u{20}\u{020}\u{0020}\u{000020}`}\u{77}\u{6f}\u{72}\u{6c}\u{64}` : string ->`\u{20}\u{020}\u{0020}\u{000020}` : string +>`\u{20}\u{020}\u{0020}\u{000020}` : " " diff --git a/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt b/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt index 528ca9f9e73..e5c6f476480 100644 --- a/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt +++ b/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt @@ -1,8 +1,9 @@ tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(2,6): error TS6133: 'handler1' is declared but never used. +tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(5,10): error TS6133: 'foo' is declared but never used. tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(6,10): error TS6133: 'handler2' is declared but never used. -==== tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts (2 errors) ==== +==== tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts (3 errors) ==== // unused type handler1 = () => void; ~~~~~~~~ @@ -10,6 +11,8 @@ tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(6,10): error TS613 function foo() { + ~~~ +!!! error TS6133: 'foo' is declared but never used. type handler2 = () => void; ~~~~~~~~ !!! error TS6133: 'handler2' is declared but never used. diff --git a/tests/baselines/reference/voidOperatorWithAnyOtherType.errors.txt b/tests/baselines/reference/voidOperatorWithAnyOtherType.errors.txt index 5867a7ad6f5..7230f90c4f3 100644 --- a/tests/baselines/reference/voidOperatorWithAnyOtherType.errors.txt +++ b/tests/baselines/reference/voidOperatorWithAnyOtherType.errors.txt @@ -1,12 +1,9 @@ -tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWithAnyOtherType.ts(46,27): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWithAnyOtherType.ts(46,34): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWithAnyOtherType.ts(47,27): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWithAnyOtherType.ts(47,34): error TS2531: Object is possibly 'null'. -tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWithAnyOtherType.ts(48,27): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWithAnyOtherType.ts(48,39): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWithAnyOtherType.ts(46,27): error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. +tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWithAnyOtherType.ts(47,27): error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. +tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWithAnyOtherType.ts(48,27): error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. -==== tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWithAnyOtherType.ts (6 errors) ==== +==== tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWithAnyOtherType.ts (3 errors) ==== // void operator on any type var ANY: any; @@ -53,20 +50,14 @@ tests/cases/conformance/expressions/unaryOperators/voidOperator/voidOperatorWith var ResultIsAny15 = void A.foo(); var ResultIsAny16 = void (ANY + ANY1); var ResultIsAny17 = void (null + undefined); - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'undefined'. var ResultIsAny18 = void (null + null); - ~~~~ -!!! error TS2531: Object is possibly 'null'. - ~~~~ -!!! error TS2531: Object is possibly 'null'. + ~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'null' and 'null'. var ResultIsAny19 = void (undefined + undefined); - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. - ~~~~~~~~~ -!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2365: Operator '+' cannot be applied to types 'undefined' and 'undefined'. // multiple void operators var ResultIsAny20 = void void ANY; diff --git a/tests/baselines/reference/weakType.errors.txt b/tests/baselines/reference/weakType.errors.txt index 441ef70ac16..ffc1d237593 100644 --- a/tests/baselines/reference/weakType.errors.txt +++ b/tests/baselines/reference/weakType.errors.txt @@ -1,14 +1,17 @@ -tests/cases/compiler/weakType.ts(16,13): error TS2559: Type '12' has no properties in common with type 'Settings'. -tests/cases/compiler/weakType.ts(17,13): error TS2559: Type '"completely wrong"' has no properties in common with type 'Settings'. -tests/cases/compiler/weakType.ts(18,13): error TS2559: Type 'false' has no properties in common with type 'Settings'. -tests/cases/compiler/weakType.ts(35,18): error TS2559: Type '{ error?: number; }' has no properties in common with type 'ChangeOptions'. -tests/cases/compiler/weakType.ts(60,5): error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak & Spoiler'. +tests/cases/compiler/weakType.ts(15,13): error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? +tests/cases/compiler/weakType.ts(16,13): error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? +tests/cases/compiler/weakType.ts(17,13): error TS2560: Value of type 'CtorOnly' has no properties in common with type 'Settings'. Did you mean to call it? +tests/cases/compiler/weakType.ts(18,13): error TS2559: Type '12' has no properties in common with type 'Settings'. +tests/cases/compiler/weakType.ts(19,13): error TS2559: Type '"completely wrong"' has no properties in common with type 'Settings'. +tests/cases/compiler/weakType.ts(20,13): error TS2559: Type 'false' has no properties in common with type 'Settings'. +tests/cases/compiler/weakType.ts(37,18): error TS2559: Type '{ error?: number; }' has no properties in common with type 'ChangeOptions'. +tests/cases/compiler/weakType.ts(62,5): error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak & Spoiler'. Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak'. Types of property 'properties' are incompatible. Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'. -==== tests/cases/compiler/weakType.ts (5 errors) ==== +==== tests/cases/compiler/weakType.ts (8 errors) ==== interface Settings { timeout?: number; onError?(): void; @@ -17,13 +20,21 @@ tests/cases/compiler/weakType.ts(60,5): error TS2322: Type '{ properties: { wron function getDefaultSettings() { return { timeout: 1000 }; } + interface CtorOnly { + new(s: string): { timeout: 1000 } + } function doSomething(settings: Settings) { /* ... */ } // forgot to call `getDefaultSettings` - // but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); - // same for arrow expressions: - doSomething(() => { }); + ~~~~~~~~~~~~~~~~~~ +!!! error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? + doSomething(() => ({ timeout: 1000 })); + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? + doSomething(null as CtorOnly); + ~~~~~~~~~~~~~~~~ +!!! error TS2560: Value of type 'CtorOnly' has no properties in common with type 'Settings'. Did you mean to call it? doSomething(12); ~~ !!! error TS2559: Type '12' has no properties in common with type 'Settings'. @@ -82,4 +93,5 @@ tests/cases/compiler/weakType.ts(60,5): error TS2322: Type '{ properties: { wron !!! error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak'. !!! error TS2322: Types of property 'properties' are incompatible. !!! error TS2322: Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'. + \ No newline at end of file diff --git a/tests/baselines/reference/weakType.js b/tests/baselines/reference/weakType.js index 5637271ccec..2a1dc4ca0e4 100644 --- a/tests/baselines/reference/weakType.js +++ b/tests/baselines/reference/weakType.js @@ -7,13 +7,15 @@ interface Settings { function getDefaultSettings() { return { timeout: 1000 }; } +interface CtorOnly { + new(s: string): { timeout: 1000 } +} function doSomething(settings: Settings) { /* ... */ } // forgot to call `getDefaultSettings` -// but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); -// same for arrow expressions: -doSomething(() => { }); +doSomething(() => ({ timeout: 1000 })); +doSomething(null as CtorOnly); doSomething(12); doSomething('completely wrong'); doSomething(false); @@ -59,6 +61,7 @@ declare let unknown: { } } let weak: Weak & Spoiler = unknown + //// [weakType.js] @@ -67,10 +70,9 @@ function getDefaultSettings() { } function doSomething(settings) { } // forgot to call `getDefaultSettings` -// but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); -// same for arrow expressions: -doSomething(function () { }); +doSomething(function () { return ({ timeout: 1000 }); }); +doSomething(null); doSomething(12); doSomething('completely wrong'); doSomething(false); diff --git a/tests/cases/compiler/bindingPatternOmittedExpressionNesting.ts b/tests/cases/compiler/bindingPatternOmittedExpressionNesting.ts new file mode 100644 index 00000000000..56ae688e715 --- /dev/null +++ b/tests/cases/compiler/bindingPatternOmittedExpressionNesting.ts @@ -0,0 +1,2 @@ +// @declaration: true +export let [,,[,[],,[],]] = undefined as any; \ No newline at end of file diff --git a/tests/cases/compiler/checkTypePredicateForRedundantProperties.ts b/tests/cases/compiler/checkTypePredicateForRedundantProperties.ts new file mode 100644 index 00000000000..35222f1e9db --- /dev/null +++ b/tests/cases/compiler/checkTypePredicateForRedundantProperties.ts @@ -0,0 +1,3 @@ +function addProp2(x: any): x is { a: string; a: string; } { + return true; +} diff --git a/tests/cases/compiler/contextualTypingFunctionReturningFunction.ts b/tests/cases/compiler/contextualTypingFunctionReturningFunction.ts new file mode 100644 index 00000000000..556f42f7eb5 --- /dev/null +++ b/tests/cases/compiler/contextualTypingFunctionReturningFunction.ts @@ -0,0 +1,11 @@ +interface I { + a(s: string): void; + b(): (n: number) => void; +} + +declare function f(i: I): void; + +f({ + a: s => {}, + b: () => n => {}, +}); diff --git a/tests/cases/compiler/contextualTypingFunctionReturningFunction2.ts b/tests/cases/compiler/contextualTypingFunctionReturningFunction2.ts new file mode 100644 index 00000000000..9bcd34e4c0f --- /dev/null +++ b/tests/cases/compiler/contextualTypingFunctionReturningFunction2.ts @@ -0,0 +1,4 @@ +declare function f(n: number): void; +declare function f(cb: () => (n: number) => number): void; + +f(() => n => n); diff --git a/tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts b/tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts index 1ea001057a9..451e67092bb 100644 --- a/tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts +++ b/tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts @@ -1,3 +1,3 @@ var f10: (x: T, b: () => (a: T) => void, y: T) => T; -f10('', () => a => a.foo, ''); // a is string +f10('', () => a => a.foo, ''); // a is "" var r9 = f10('', () => (a => a.foo), 1); // error \ No newline at end of file diff --git a/tests/cases/compiler/duplicatePackage.ts b/tests/cases/compiler/duplicatePackage.ts new file mode 100644 index 00000000000..31df2d24508 --- /dev/null +++ b/tests/cases/compiler/duplicatePackage.ts @@ -0,0 +1,42 @@ +// @noImplicitReferences: true + +// @Filename: /node_modules/a/index.d.ts +import X from "x"; +export function a(x: X): void; + +// @Filename: /node_modules/a/node_modules/x/index.d.ts +export default class X { + private x: number; +} + +// @Filename: /node_modules/a/node_modules/x/package.json +{ "name": "x", "version": "1.2.3" } + +// @Filename: /node_modules/b/index.d.ts +import X from "x"; +export const b: X; + +// @Filename: /node_modules/b/node_modules/x/index.d.ts +content not parsed + +// @Filename: /node_modules/b/node_modules/x/package.json +{ "name": "x", "version": "1.2.3" } + +// @Filename: /node_modules/c/index.d.ts +import X from "x"; +export const c: X; + +// @Filename: /node_modules/c/node_modules/x/index.d.ts +export default class X { + private x: number; +} + +// @Filename: /node_modules/c/node_modules/x/package.json +{ "name": "x", "version": "1.2.4" } + +// @Filename: /src/a.ts +import { a } from "a"; +import { b } from "b"; +import { c } from "c"; +a(b); // Works +a(c); // Error, these are from different versions of the library. diff --git a/tests/cases/compiler/duplicatePackage_withErrors.ts b/tests/cases/compiler/duplicatePackage_withErrors.ts new file mode 100644 index 00000000000..266c7239971 --- /dev/null +++ b/tests/cases/compiler/duplicatePackage_withErrors.ts @@ -0,0 +1,23 @@ +// @noImplicitReferences: true + +// @Filename: /node_modules/a/index.d.ts +export { x } from "x"; + +// @Filename: /node_modules/a/node_modules/x/index.d.ts +export const x = 1 + 1; + +// @Filename: /node_modules/a/node_modules/x/package.json +{ "name": "x", "version": "1.2.3" } + +// @Filename: /node_modules/b/index.d.ts +export { x } from "x"; + +// @Filename: /node_modules/b/node_modules/x/index.d.ts +content not parsed + +// @Filename: /node_modules/b/node_modules/x/package.json +{ "name": "x", "version": "1.2.3" } + +// @Filename: /src/a.ts +import { x as xa } from "a"; +import { x as xb } from "b"; diff --git a/tests/cases/compiler/indexSignatureAndMappedType.ts b/tests/cases/compiler/indexSignatureAndMappedType.ts new file mode 100644 index 00000000000..b5f9e8a0030 --- /dev/null +++ b/tests/cases/compiler/indexSignatureAndMappedType.ts @@ -0,0 +1,35 @@ +// @strict: true +// @declaration: true + +// A mapped type { [P in K]: X }, where K is a generic type, is related to +// { [key: string]: Y } if X is related to Y. + +function f1(x: { [key: string]: T }, y: Record) { + x = y; + y = x; // Error +} + +function f2(x: { [key: string]: T }, y: Record) { + x = y; + y = x; +} + +function f3(x: { [key: string]: T }, y: Record) { + x = y; // Error + y = x; // Error +} + +// Repro from #14548 + +type Dictionary = { + [key: string]: string; +}; + +interface IBaseEntity { + name: string; + properties: Dictionary; +} + +interface IEntity extends IBaseEntity { + properties: Record; +} diff --git a/tests/cases/compiler/jsdocCastCommentEmit.ts b/tests/cases/compiler/jsdocCastCommentEmit.ts new file mode 100644 index 00000000000..5e7230bd049 --- /dev/null +++ b/tests/cases/compiler/jsdocCastCommentEmit.ts @@ -0,0 +1,7 @@ +// allowJs: true +// checkJs: true +// outDir: out/ +// filename: input.js +function f() { + return /* @type {number} */ 42; +} \ No newline at end of file diff --git a/tests/cases/compiler/mixingApparentTypeOverrides.ts b/tests/cases/compiler/mixingApparentTypeOverrides.ts new file mode 100644 index 00000000000..5d68c58d461 --- /dev/null +++ b/tests/cases/compiler/mixingApparentTypeOverrides.ts @@ -0,0 +1,28 @@ +type Constructor = new(...args: any[]) => T; +function Tagged>(Base: T) { + return class extends Base { + _tag: string; + constructor(...args: any[]) { + super(...args); + this._tag = ""; + } + }; +} + +class A { + toString () { + return "class A"; + } +} + +class B extends Tagged(A) { + toString () { // Should not be an error + return "class B"; + } +} + +class C extends A { + toString () { // Should not be an error + return "class C"; + } +} \ No newline at end of file diff --git a/tests/cases/compiler/moduleResolutionWithSymlinks_preserveSymlinks.ts b/tests/cases/compiler/moduleResolutionWithSymlinks_preserveSymlinks.ts new file mode 100644 index 00000000000..d5509eb5e20 --- /dev/null +++ b/tests/cases/compiler/moduleResolutionWithSymlinks_preserveSymlinks.ts @@ -0,0 +1,24 @@ +// @noImplicitReferences: true + +// @traceResolution: true +// @preserveSymlinks: true +// @moduleResolution: node + +// @filename: /linked/index.d.ts +// @symlink: /app/node_modules/linked/index.d.ts,/app/node_modules/linked2/index.d.ts +export { real } from "real"; +export class C { private x; } + +// @filename: /app/node_modules/real/index.d.ts +export const real: string; + +// @filename: /app/app.ts +// We shouldn't resolve symlinks for references either. See the trace. +/// + +import { C as C1 } from "linked"; +import { C as C2 } from "linked2"; + +let x = new C1(); +// Should fail. We no longer resolve any symlinks. +x = new C2(); diff --git a/tests/cases/compiler/noUnusedLocals_selfReference.ts b/tests/cases/compiler/noUnusedLocals_selfReference.ts new file mode 100644 index 00000000000..8eb528743c0 --- /dev/null +++ b/tests/cases/compiler/noUnusedLocals_selfReference.ts @@ -0,0 +1,17 @@ +// @noUnusedLocals: true + +export {}; // Make this a module scope, so these are local variables. + +function f() { f; } +class C { + m() { C; } +} +enum E { A = 0, B = E.A } + +// Does not detect mutual recursion. +function g() { D; } +class D { m() { g; } } + +// Does not work on private methods. +class P { private m() { this.m; } } +P; diff --git a/tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts b/tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts new file mode 100644 index 00000000000..4f25446aad8 --- /dev/null +++ b/tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts @@ -0,0 +1,13 @@ +// @strict: true + +// Repro from #17148 + +class Foo { + myFunc(arg: T) {} +} + +class Bar { + myFunc(arg: T) {} +} + +const myVar: Foo = new Bar(); diff --git a/tests/cases/compiler/syntheticDefaultExportsWithDynamicImports.ts b/tests/cases/compiler/syntheticDefaultExportsWithDynamicImports.ts new file mode 100644 index 00000000000..07f08d03185 --- /dev/null +++ b/tests/cases/compiler/syntheticDefaultExportsWithDynamicImports.ts @@ -0,0 +1,9 @@ +// @module: system +// @target: es6 +// @moduleResolution: node +// @filename: node_modules/package/index.d.ts +declare function packageExport(x: number): string; +export = packageExport; + +// @filename: index.ts +import("package").then(({default: foo}) => foo(42)); \ No newline at end of file diff --git a/tests/cases/compiler/tsxUnionSpread.tsx b/tests/cases/compiler/tsxUnionSpread.tsx new file mode 100644 index 00000000000..daa663db87f --- /dev/null +++ b/tests/cases/compiler/tsxUnionSpread.tsx @@ -0,0 +1,24 @@ +// @jsx: preserve +// @filename: index.tsx +namespace JSX { + export interface Element {} +} + +export type CatInfo = { type: 'Cat'; subType: string; }; +export type DogInfo = { type: 'Dog'; }; +export type AnimalInfo = CatInfo | DogInfo; + +function AnimalComponent(info: AnimalInfo): JSX.Element { + return undefined as any; +} + +function getProps(): AnimalInfo { + // this may be from server or whatever ... + return { type: 'Cat', subType: 'Large' }; +} + +var props:AnimalInfo = getProps(); +var component = + +var props2:AnimalInfo = { type: 'Cat', subType: 'Large' }; +var component2 = \ No newline at end of file diff --git a/tests/cases/compiler/typeParameterExtendsPrimitive.ts b/tests/cases/compiler/typeParameterExtendsPrimitive.ts new file mode 100644 index 00000000000..b94a44b4660 --- /dev/null +++ b/tests/cases/compiler/typeParameterExtendsPrimitive.ts @@ -0,0 +1,25 @@ +// #14473 +function f() { + var t: T; + var v = { + [t]: 0 + } + return t + t; +} + +// #15501 +interface I { x: number } +type IdMap = { [P in keyof T]: T[P] }; +function g(i: IdMap) { + const n: number = i.x; + return i.x * 2; +} + +// #17069 +function h, K extends string>(array: T[], prop: K): number { + let result = 0; + for (const v of array) { + result += v[prop]; + } + return result; +} diff --git a/tests/cases/compiler/weakType.ts b/tests/cases/compiler/weakType.ts index ffe51205e53..08c9d95e672 100644 --- a/tests/cases/compiler/weakType.ts +++ b/tests/cases/compiler/weakType.ts @@ -6,13 +6,15 @@ interface Settings { function getDefaultSettings() { return { timeout: 1000 }; } +interface CtorOnly { + new(s: string): { timeout: 1000 } +} function doSomething(settings: Settings) { /* ... */ } // forgot to call `getDefaultSettings` -// but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); -// same for arrow expressions: -doSomething(() => { }); +doSomething(() => ({ timeout: 1000 })); +doSomething(null as CtorOnly); doSomething(12); doSomething('completely wrong'); doSomething(false); @@ -58,3 +60,4 @@ declare let unknown: { } } let weak: Weak & Spoiler = unknown + diff --git a/tests/cases/conformance/emitter/esnext/noCatchBinding/emitter.noCatchBinding.esnext.ts b/tests/cases/conformance/emitter/esnext/noCatchBinding/emitter.noCatchBinding.esnext.ts new file mode 100644 index 00000000000..8e87b3c8c8f --- /dev/null +++ b/tests/cases/conformance/emitter/esnext/noCatchBinding/emitter.noCatchBinding.esnext.ts @@ -0,0 +1,8 @@ +// @target: esnext +function f() { + try { } catch { } + try { } catch { + try { } catch { } + } + try { } catch { } finally { } +} \ No newline at end of file diff --git a/tests/cases/conformance/es6/computedProperties/computedPropertyNames51_ES5.ts b/tests/cases/conformance/es6/computedProperties/computedPropertyNames51_ES5.ts new file mode 100644 index 00000000000..1d467245fb2 --- /dev/null +++ b/tests/cases/conformance/es6/computedProperties/computedPropertyNames51_ES5.ts @@ -0,0 +1,8 @@ +function f() { + var t: T; + var k: K; + var v = { + [t]: 0, + [k]: 1 + }; +} diff --git a/tests/cases/conformance/es6/computedProperties/computedPropertyNames51_ES6.ts b/tests/cases/conformance/es6/computedProperties/computedPropertyNames51_ES6.ts new file mode 100644 index 00000000000..ca209996c4c --- /dev/null +++ b/tests/cases/conformance/es6/computedProperties/computedPropertyNames51_ES6.ts @@ -0,0 +1,9 @@ +// @target: es6 +function f() { + var t: T; + var k: K; + var v = { + [t]: 0, + [k]: 1 + }; +} diff --git a/tests/cases/conformance/es6/destructuring/emptyAssignmentPatterns01_ES5.ts b/tests/cases/conformance/es6/destructuring/emptyAssignmentPatterns01_ES5.ts index 44175dbb63a..f093413b63f 100644 --- a/tests/cases/conformance/es6/destructuring/emptyAssignmentPatterns01_ES5.ts +++ b/tests/cases/conformance/es6/destructuring/emptyAssignmentPatterns01_ES5.ts @@ -4,4 +4,6 @@ var a: any; ({} = a); -([] = a); \ No newline at end of file +([] = a); + +var [,] = [1,2]; \ No newline at end of file diff --git a/tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts b/tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts index 6937e509845..7fb1b135c90 100644 --- a/tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts +++ b/tests/cases/conformance/statements/tryStatements/invalidTryStatements2.ts @@ -1,28 +1,20 @@ function fn() { - try { - } catch { // syntax error, missing '(x)' - } - catch(x) { } // error missing try - finally{ } // potential error; can be absorbed by the 'catch' + finally { } // potential error; can be absorbed by the 'catch' + + try { }; // error missing finally } function fn2() { finally { } // error missing try catch (x) { } // error missing try + + try { } finally { } // statement is here, so the 'catch' clause above doesn't absorb errors from the 'finally' clause below - // no error - try { - } - finally { - } + finally { } // error missing try + + catch (x) { } // error missing try - // error missing try - finally { - } - - // error missing try - catch (x) { - } + try { } catch () { } // error missing catch binding } \ No newline at end of file diff --git a/tests/cases/conformance/statements/tryStatements/tryStatements.ts b/tests/cases/conformance/statements/tryStatements/tryStatements.ts index 691c046d6d1..0a47ba7a94b 100644 --- a/tests/cases/conformance/statements/tryStatements/tryStatements.ts +++ b/tests/cases/conformance/statements/tryStatements/tryStatements.ts @@ -1,11 +1,19 @@ -function fn() { - try { - } catch (x) { - var x: any; +function fn() { + try { } catch { } + + try { } catch { + try { } catch { + try { } catch { } + } + try { } catch { } } + try { } catch (x) { var x: any; } + try { } finally { } - try { }catch(z){ } finally { } + try { } catch { } finally { } + + try { } catch (z) { } finally { } } \ No newline at end of file diff --git a/tests/cases/conformance/types/thisType/thisTypeInTypePredicate.ts b/tests/cases/conformance/types/thisType/thisTypeInTypePredicate.ts new file mode 100644 index 00000000000..ed0ebc75a22 --- /dev/null +++ b/tests/cases/conformance/types/thisType/thisTypeInTypePredicate.ts @@ -0,0 +1,2 @@ +declare function filter(f: (this: void, x: any) => x is S): S[]; +const numbers = filter((x): x is number => 'number' == typeof x) diff --git a/tests/cases/fourslash/completionListInObjectBindingPattern14.ts b/tests/cases/fourslash/completionListInObjectBindingPattern14.ts new file mode 100644 index 00000000000..425813a5543 --- /dev/null +++ b/tests/cases/fourslash/completionListInObjectBindingPattern14.ts @@ -0,0 +1,9 @@ +/// + +////const { b/**/ } = new class { +//// private ab; +//// protected bc; +////} + +goTo.marker(); +verify.completionListIsEmpty(); diff --git a/tests/cases/fourslash/completionListIsGlobalCompletion.ts b/tests/cases/fourslash/completionListIsGlobalCompletion.ts index 121ab940d65..49196bdabd4 100644 --- a/tests/cases/fourslash/completionListIsGlobalCompletion.ts +++ b/tests/cases/fourslash/completionListIsGlobalCompletion.ts @@ -47,7 +47,7 @@ verify.completionListIsGlobal(true); goTo.marker("6"); verify.completionListIsGlobal(false); goTo.marker("7"); -verify.completionListIsGlobal(true); +verify.completionListIsGlobal(false); goTo.marker("8"); verify.completionListIsGlobal(false); goTo.marker("9"); diff --git a/tests/cases/fourslash/duplicatePackageServices.ts b/tests/cases/fourslash/duplicatePackageServices.ts new file mode 100644 index 00000000000..360611ad140 --- /dev/null +++ b/tests/cases/fourslash/duplicatePackageServices.ts @@ -0,0 +1,46 @@ +/// +// @noImplicitReferences: true + +// @Filename: /node_modules/a/index.d.ts +////import /*useAX*/[|{| "isWriteAccess": true, "isDefinition": true |}X|] from "x"; +////export function a(x: [|X|]): void; + +// @Filename: /node_modules/a/node_modules/x/index.d.ts +////export default class /*defAX*/[|{| "isWriteAccess": true, "isDefinition": true |}X|] { +//// private x: number; +////} + +// @Filename: /node_modules/a/node_modules/x/package.json +////{ "name": "x", "version": "1.2.3" } + +// @Filename: /node_modules/b/index.d.ts +////import /*useBX*/[|{| "isWriteAccess": true, "isDefinition": true |}X|] from "x"; +////export const b: [|X|]; + +// @Filename: /node_modules/b/node_modules/x/index.d.ts +////export default class /*defBX*/[|{| "isWriteAccess": true, "isDefinition": true |}X|] { +//// private x: number; +////} + +// @Filename: /node_modules/b/node_modules/x/package.json +////{ "name": "x", "version": "1.2./*bVersionPatch*/3" } + +// @Filename: /src/a.ts +////import { a } from "a"; +////import { b } from "b"; +////a(/*error*/b); + +goTo.file("/src/a.ts"); +verify.numberOfErrorsInCurrentFile(0); +verify.goToDefinition("useAX", "defAX"); +verify.goToDefinition("useBX", "defAX"); + +const [r0, r1, r2, r3, r4, r5] = test.ranges(); +const aImport = { definition: "import X", ranges: [r0, r1] }; +const def = { definition: "class X", ranges: [r2] }; +const bImport = { definition: "import X", ranges: [r3, r4] }; +verify.referenceGroups([r0, r1], [aImport, def, bImport]); +verify.referenceGroups([r2], [def, aImport, bImport]); +verify.referenceGroups([r3, r4], [bImport, def, aImport]); + +verify.referenceGroups(r5, [def, aImport, bImport]); diff --git a/tests/cases/fourslash/duplicatePackageServices_fileChanges.ts b/tests/cases/fourslash/duplicatePackageServices_fileChanges.ts new file mode 100644 index 00000000000..203277d7ad4 --- /dev/null +++ b/tests/cases/fourslash/duplicatePackageServices_fileChanges.ts @@ -0,0 +1,57 @@ +/// +// @noImplicitReferences: true + +// @Filename: /node_modules/a/index.d.ts +////import X from "x"; +////export function a(x: X): void; + +// @Filename: /node_modules/a/node_modules/x/index.d.ts +////export default class /*defAX*/X { +//// private x: number; +////} + +// @Filename: /node_modules/a/node_modules/x/package.json +////{ "name": "x", "version": "1.2./*aVersionPatch*/3" } + +// @Filename: /node_modules/b/index.d.ts +////import X from "x"; +////export const b: X; + +// @Filename: /node_modules/b/node_modules/x/index.d.ts +////export default class /*defBX*/X { +//// private x: number; +////} + +// @Filename: /node_modules/b/node_modules/x/package.json +////{ "name": "x", "version": "1.2./*bVersionPatch*/3" } + +// @Filename: /src/a.ts +////import { a } from "a"; +////import { b } from "b"; +////a(/*error*/b); + +goTo.file("/src/a.ts"); +verify.numberOfErrorsInCurrentFile(0); + +testChangeAndChangeBack("aVersionPatch", "defAX"); +testChangeAndChangeBack("bVersionPatch", "defBX"); + +function testChangeAndChangeBack(versionPatch: string, def: string) { + goTo.marker(versionPatch); + edit.insert("4"); + goTo.marker(def); + edit.insert(" "); + + // No longer have identical packageId, so we get errors. + verify.errorExistsAfterMarker("error"); + + // Undo the change. + goTo.marker(versionPatch); + edit.deleteAtCaret(); + goTo.marker(def); + edit.deleteAtCaret(); + + // Back to being identical. + goTo.file("/src/a.ts"); + verify.numberOfErrorsInCurrentFile(0); +} diff --git a/tests/cases/fourslash/extract-method1.ts b/tests/cases/fourslash/extract-method1.ts new file mode 100644 index 00000000000..7f5c2ac2d50 --- /dev/null +++ b/tests/cases/fourslash/extract-method1.ts @@ -0,0 +1,33 @@ +/// + +//// class Foo { +//// someMethod(m: number) { +//// /*start*/var x = m; +//// x = x * 3; +//// var y = 30; +//// var z = y + x; +//// console.log(z);/*end*/ +//// var q = 10; +//// return q; +//// } +//// } + +goTo.select('start', 'end') +verify.refactorAvailable('Extract Method'); +edit.applyRefactor('Extract Method', "scope_0"); +verify.currentFileContentIs( +`class Foo { + someMethod(m: number) { + this.newFunction(m); + var q = 10; + return q; + } + + private newFunction(m: number) { + var x = m; + x = x * 3; + var y = 30; + var z = y + x; + console.log(z); + } +}`); diff --git a/tests/cases/fourslash/extract-method10.ts b/tests/cases/fourslash/extract-method10.ts new file mode 100644 index 00000000000..1a02bfa00f5 --- /dev/null +++ b/tests/cases/fourslash/extract-method10.ts @@ -0,0 +1,6 @@ +/// + +//// (x => x)(/*1*/x => x/*2*/)(1); + +goTo.select('1', '2'); +edit.applyRefactor('Extract Method', 'scope_0'); diff --git a/tests/cases/fourslash/extract-method11.ts b/tests/cases/fourslash/extract-method11.ts new file mode 100644 index 00000000000..705f2373104 --- /dev/null +++ b/tests/cases/fourslash/extract-method11.ts @@ -0,0 +1,28 @@ +/// + +// Nonexhaustive list of things it should be illegal to be extract-method on + +// * Import declarations +// * Super calls +// * Function body blocks +// * try/catch blocks + +//// /*1a*/import * as x from 'y';/*1b*/ +//// namespace N { +//// /*oka*/class C extends B { +//// constructor() { +//// /*2a*/super();/*2b*/ +//// } +//// }/*okb*/ +//// } +//// function f() /*3a*/{ return 0 }/*3b*/ +//// try /*4a*/{ console.log }/*4b*/ catch (e) /*5a*/{ console.log; }/*5b*/ + +for (const m of ['1', '2', '3', '4', '5']) { + goTo.select(m + 'a', m + 'b'); + verify.not.refactorAvailable('Extract Method'); +} + +// Verify we can still extract the entire class +goTo.select('oka', 'okb'); +verify.refactorAvailable('Extract Method'); diff --git a/tests/cases/fourslash/extract-method13.ts b/tests/cases/fourslash/extract-method13.ts new file mode 100644 index 00000000000..b9b7c0096aa --- /dev/null +++ b/tests/cases/fourslash/extract-method13.ts @@ -0,0 +1,30 @@ +/// + +// Extracting from a static context should make static methods. +// Also checks that we correctly find non-conflicting names in static contexts. + +//// class C { +//// static j = /*c*/100/*d*/; +//// constructor(q: string = /*a*/"hello"/*b*/) { +//// } +//// } + +goTo.select('a', 'b'); +edit.applyRefactor('Extract Method', 'scope_0'); + +goTo.select('c', 'd'); +edit.applyRefactor('Extract Method', 'scope_0'); + +verify.currentFileContentIs(`class C { + static j = C.newFunction_1(); + constructor(q: string = C.newFunction()) { + } + + private static newFunction(): string { + return "hello"; + } + + private static newFunction_1() { + return 100; + } +}`); \ No newline at end of file diff --git a/tests/cases/fourslash/extract-method14.ts b/tests/cases/fourslash/extract-method14.ts new file mode 100644 index 00000000000..c8bab1b3a56 --- /dev/null +++ b/tests/cases/fourslash/extract-method14.ts @@ -0,0 +1,24 @@ +/// + +// Don't emit type annotations in JavaScript files +// Also tests that single-variable return extractions don't get superfluous destructuring + +// @allowNonTsExtensions: true +// @Filename: foo.js +//// function foo() { +//// var i = 10; +//// /*a*/return i++;/*b*/ +//// } + +goTo.select('a', 'b'); +edit.applyRefactor('Extract Method', 'scope_1'); +verify.currentFileContentIs(`function foo() { + var i = 10; + var __return: any; + ({ __return, i } = newFunction(i)); + return __return; +} +function newFunction(i) { + return { __return: i++, i }; +} +`); \ No newline at end of file diff --git a/tests/cases/fourslash/extract-method15.ts b/tests/cases/fourslash/extract-method15.ts new file mode 100644 index 00000000000..ef62bd3fd3f --- /dev/null +++ b/tests/cases/fourslash/extract-method15.ts @@ -0,0 +1,22 @@ +/// + +// Extracting an increment expression (not statement) should do the right thing, +// including not generating extra destructuring unless needed + +//// function foo() { +//// var i = 10; +//// /*a*/i++/*b*/; +//// } + +goTo.select('a', 'b'); +edit.applyRefactor('Extract Method', 'scope_1'); + +verify.currentFileContentIs(`function foo() { + var i = 10; + i = newFunction(i); +} +function newFunction(i: number) { + i++; + return i; +} +`); diff --git a/tests/cases/fourslash/extract-method17.ts b/tests/cases/fourslash/extract-method17.ts new file mode 100644 index 00000000000..ce54896604d --- /dev/null +++ b/tests/cases/fourslash/extract-method17.ts @@ -0,0 +1,10 @@ +/// + +//// function foo () { +//// var x = 3; +//// var y = /*start*/x++ + 5/*end*/; +//// } + +goTo.select('start', 'end') +verify.refactorAvailable('Extract Method', 'scope_0'); +verify.not.refactorAvailable('Extract Method', 'scope_1'); diff --git a/tests/cases/fourslash/extract-method18.ts b/tests/cases/fourslash/extract-method18.ts new file mode 100644 index 00000000000..9d87979a1ed --- /dev/null +++ b/tests/cases/fourslash/extract-method18.ts @@ -0,0 +1,21 @@ +/// + +// Don't try to propagate property accessed variables back, +// or emit spurious returns when the value is clearly ignored + +//// function fn() { +//// const x = { m: 1 }; +//// /*a*/x.m = 3/*b*/; +//// } + +goTo.select('a', 'b') +verify.refactorAvailable('Extract Method'); +edit.applyRefactor('Extract Method', "scope_1"); +verify.currentFileContentIs(`function fn() { + const x = { m: 1 }; + newFunction(x); +} +function newFunction(x: { m: number; }) { + x.m = 3; +} +`); diff --git a/tests/cases/fourslash/extract-method19.ts b/tests/cases/fourslash/extract-method19.ts new file mode 100644 index 00000000000..54f79311cc7 --- /dev/null +++ b/tests/cases/fourslash/extract-method19.ts @@ -0,0 +1,22 @@ +/// + +// New function names should be totally new to the file + +//// function fn() { +//// /*a*/console.log("hi");/*b*/ +//// } +//// +//// function newFunction() { } + +goTo.select('a', 'b') +verify.refactorAvailable('Extract Method'); +edit.applyRefactor('Extract Method', "scope_0"); +verify.currentFileContentIs(`function fn() { + newFunction_1(); + + function newFunction_1() { + console.log("hi"); + } +} + +function newFunction() { }`); diff --git a/tests/cases/fourslash/extract-method2.ts b/tests/cases/fourslash/extract-method2.ts new file mode 100644 index 00000000000..0a4f346307b --- /dev/null +++ b/tests/cases/fourslash/extract-method2.ts @@ -0,0 +1,28 @@ +/// + +//// namespace NS { +//// class Q { +//// foo() { +//// console.log('100'); +//// const m = 10, j = "hello", k = {x: "what"}; +//// const q = /*start*/m + j + k/*end*/; +//// } +//// } +//// } +goTo.select('start', 'end') +verify.refactorAvailable('Extract Method'); +edit.applyRefactor('Extract Method', "scope_2"); +verify.currentFileContentIs( +`namespace NS { + class Q { + foo() { + console.log('100'); + const m = 10, j = "hello", k = {x: "what"}; + const q = newFunction(m, j, k); + } + } +} +function newFunction(m: number, j: string, k: { x: string; }) { + return m + j + k; +} +`); diff --git a/tests/cases/fourslash/extract-method20.ts b/tests/cases/fourslash/extract-method20.ts new file mode 100644 index 00000000000..04990001b5f --- /dev/null +++ b/tests/cases/fourslash/extract-method20.ts @@ -0,0 +1,14 @@ +/// + +// Shouldn't be able to extract a readonly property initializer outside the constructor + +//// class Foo { +//// readonly prop; +//// constructor() { +//// /*a*/this.prop = 10;/*b*/ +//// } +//// } + +goTo.select('a', 'b') +verify.refactorAvailable('Extract Method', 'scope_0'); +verify.not.refactorAvailable('Extract Method', 'scope_1'); diff --git a/tests/cases/fourslash/extract-method21.ts b/tests/cases/fourslash/extract-method21.ts new file mode 100644 index 00000000000..0168daf5fcb --- /dev/null +++ b/tests/cases/fourslash/extract-method21.ts @@ -0,0 +1,25 @@ +/// + +// Extracting from a static method should create a static method + +//// class Foo { +//// static method() { +//// /*start*/return 1;/*end*/ +//// } +//// } + +goTo.select('start', 'end') + +verify.refactorAvailable('Extract Method'); + +edit.applyRefactor('Extract Method', "scope_0"); + +verify.currentFileContentIs(`class Foo { + static method() { + return Foo.newFunction(); + } + + private static newFunction() { + return 1; + } +}`); \ No newline at end of file diff --git a/tests/cases/fourslash/extract-method22.ts b/tests/cases/fourslash/extract-method22.ts new file mode 100644 index 00000000000..7486520e700 --- /dev/null +++ b/tests/cases/fourslash/extract-method22.ts @@ -0,0 +1,10 @@ +/// + +// You may not extract variable declarations with the export modifier + +//// namespace NS { +//// /*start*/export var x = 10;/*end*/ +//// } + +goTo.select('start', 'end') +verify.not.refactorAvailable('Extract Method'); diff --git a/tests/cases/fourslash/extract-method23.ts b/tests/cases/fourslash/extract-method23.ts new file mode 100644 index 00000000000..7da8f175f13 --- /dev/null +++ b/tests/cases/fourslash/extract-method23.ts @@ -0,0 +1,8 @@ +/// + +//// declare namespace Foo { +//// const x = /*start*/3/*end*/; +//// } + +goTo.select('start', 'end') +verify.not.refactorAvailable('Extract Method'); diff --git a/tests/cases/fourslash/extract-method24.ts b/tests/cases/fourslash/extract-method24.ts new file mode 100644 index 00000000000..9eebc00316b --- /dev/null +++ b/tests/cases/fourslash/extract-method24.ts @@ -0,0 +1,19 @@ +/// + +//// function M() { +//// let a = [1,2,3]; +//// let x = 0; +//// console.log(/*a*/a[x]/*b*/); +//// } + +goTo.select('a', 'b') +edit.applyRefactor('Extract Method', 'scope_1'); +verify.currentFileContentIs(`function M() { + let a = [1,2,3]; + let x = 0; + console.log(newFunction(a, x)); +} +function newFunction(a: number[], x: number): any { + return a[x]; +} +`); \ No newline at end of file diff --git a/tests/cases/fourslash/extract-method25.ts b/tests/cases/fourslash/extract-method25.ts new file mode 100644 index 00000000000..ac7e7a23004 --- /dev/null +++ b/tests/cases/fourslash/extract-method25.ts @@ -0,0 +1,19 @@ +/// + +// Preserve newlines correctly when semicolons aren't present + +//// function fn() { +//// var q = /*a*/[0]/*b*/ +//// q[0]++ +//// } + +goTo.select('a', 'b') +edit.applyRefactor('Extract Method', 'scope_0'); +verify.currentFileContentIs(`function fn() { + var q = newFunction() + q[0]++ + + function newFunction() { + return [0]; + } +}`); diff --git a/tests/cases/fourslash/extract-method3.ts b/tests/cases/fourslash/extract-method3.ts new file mode 100644 index 00000000000..af543121eeb --- /dev/null +++ b/tests/cases/fourslash/extract-method3.ts @@ -0,0 +1,18 @@ +/// + +//// namespace NS { +//// class Q { +//// foo() { +//// console.log('100'); +//// const m = 10, j = "hello", k = {x: "what"}; +//// const q = /*a*/m/*b*/; +//// } +//// } +//// } + +// Don't offer to to 'extract method' a single identifier + +goTo.marker('a'); +verify.not.refactorAvailable('Extract Method'); +goTo.select('a', 'b'); +verify.not.refactorAvailable('Extract Method'); diff --git a/tests/cases/fourslash/extract-method4.ts b/tests/cases/fourslash/extract-method4.ts new file mode 100644 index 00000000000..ec8f39f3541 --- /dev/null +++ b/tests/cases/fourslash/extract-method4.ts @@ -0,0 +1,14 @@ +/// + +//// let a = 1, b = 2, c = 3, d = 4; +//// namespace NS { +//// class Q { +//// foo() { +//// a = /*1*/b = c/*2*/ = d; +//// } +//// } +//// } + +// Should rewrite to a = newFunc(); function() { return b = c = d; } +goTo.select('1', '2'); +verify.not.refactorAvailable('Extract Method'); diff --git a/tests/cases/fourslash/extract-method5.ts b/tests/cases/fourslash/extract-method5.ts new file mode 100644 index 00000000000..ac09f92cc05 --- /dev/null +++ b/tests/cases/fourslash/extract-method5.ts @@ -0,0 +1,20 @@ +/// + +// Extraction in the context of a contextual +// type needs to produce an explicit return type +// annotation in the extracted function + +//// function f() { +//// var x: 1 | 2 | 3 = /*start*/2/*end*/; +//// } + +goTo.select('start', 'end'); +edit.applyRefactor('Extract Method', 'scope_0'); +verify.currentFileContentIs( +`function f() { + var x: 1 | 2 | 3 = newFunction(); + + function newFunction(): 1 | 2 | 3 { + return 2; + } +}`); \ No newline at end of file diff --git a/tests/cases/fourslash/extract-method6.ts b/tests/cases/fourslash/extract-method6.ts new file mode 100644 index 00000000000..f188ab319e9 --- /dev/null +++ b/tests/cases/fourslash/extract-method6.ts @@ -0,0 +1,16 @@ +/// + +// Cannot extract globally-declared functions or +// those with non-selected local references + +//// /*f1a*/function f() { +//// /*g1a*/function g() { } +//// g();/*g1b*/ +//// g(); +//// }/*f1b*/ + +goTo.select('f1a', 'f1b'); +verify.not.refactorAvailable('Extract Method'); +goTo.select('g1a', 'g1b'); +verify.not.refactorAvailable('Extract Method'); + diff --git a/tests/cases/fourslash/extract-method7.ts b/tests/cases/fourslash/extract-method7.ts new file mode 100644 index 00000000000..4c95c6a551d --- /dev/null +++ b/tests/cases/fourslash/extract-method7.ts @@ -0,0 +1,16 @@ +/// + +// You cannot extract a function initializer into the function's body. +// The innermost scope (scope_0) is the sibling of the function, not the function itself. + +//// function fn(x = /*a*/3/*b*/) { +//// } + +goTo.select('a', 'b'); +edit.applyRefactor('Extract Method', 'scope_0'); +verify.currentFileContentIs(`function fn(x = newFunction()) { +} +function newFunction() { + return 3; +} +`); diff --git a/tests/cases/fourslash/extract-method8.ts b/tests/cases/fourslash/extract-method8.ts new file mode 100644 index 00000000000..28068dd7c78 --- /dev/null +++ b/tests/cases/fourslash/extract-method8.ts @@ -0,0 +1,17 @@ +/// + +// You cannot extract an exported function declaration + +//// namespace ns { +//// /*a*/export function fn() { +//// +//// } +//// fn(); +//// /*b*/ +//// } + +goTo.select('a', 'b'); +verify.not.refactorAvailable("Extract Method"); +edit.deleteAtCaret('export'.length); +goTo.select('a', 'b'); +verify.refactorAvailable("Extract Method"); diff --git a/tests/cases/fourslash/extract-method9.ts b/tests/cases/fourslash/extract-method9.ts new file mode 100644 index 00000000000..f70ef20a87b --- /dev/null +++ b/tests/cases/fourslash/extract-method9.ts @@ -0,0 +1,11 @@ +/// + +//// function f() { +//// /*a*/function q() { } +//// q();/*b*/ +//// q(); +//// } + +goTo.select('a', 'b'); +verify.not.refactorAvailable("Extract Method"); + diff --git a/tests/cases/fourslash/findAllReferencesDynamicImport3.ts b/tests/cases/fourslash/findAllReferencesDynamicImport3.ts new file mode 100644 index 00000000000..21d2e1097db --- /dev/null +++ b/tests/cases/fourslash/findAllReferencesDynamicImport3.ts @@ -0,0 +1,13 @@ +/// + +// @Filename: foo.ts +//// export function [|bar|]() { return "bar"; } + +//// import('./foo').then(({ [|bar|] }) => undefined); + +const [r0, r1] = test.ranges(); +// This is because bindingElement at r1 are both name and value +verify.referencesOf(r0, [r1, r0, r1, r0]); +verify.referencesOf(r1, [r0, r1, r1, r0]); +verify.renameLocations(r0, [r0, r1]); +verify.renameLocations(r1, [r1, r0, r0, r1]); \ No newline at end of file diff --git a/tests/cases/fourslash/findAllRefsTypeParameterInMergedInterface.ts b/tests/cases/fourslash/findAllRefsTypeParameterInMergedInterface.ts new file mode 100644 index 00000000000..d489a51ccdd --- /dev/null +++ b/tests/cases/fourslash/findAllRefsTypeParameterInMergedInterface.ts @@ -0,0 +1,6 @@ +/// + +////interface I<[|{| "isWriteAccess": true, "isDefinition": true |}T|]> { a: [|T|] } +////interface I<[|{| "isWriteAccess": true, "isDefinition": true |}T|]> { b: [|T|] } + +verify.singleReferenceGroup("(type parameter) T in I"); diff --git a/tests/cases/fourslash/formattingSpaceBetweenParent.ts b/tests/cases/fourslash/formattingSpaceBetweenParent.ts new file mode 100644 index 00000000000..60ec632f59c --- /dev/null +++ b/tests/cases/fourslash/formattingSpaceBetweenParent.ts @@ -0,0 +1,14 @@ +/// + +/////*1*/foo(() => 1); +/////*2*/foo(1); +/////*3*/if((true)){} + +format.setOption("InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis", true); +format.document(); +goTo.marker("1"); +verify.currentLineContentIs("foo( () => 1 );"); +goTo.marker("2"); +verify.currentLineContentIs("foo( 1 );"); +goTo.marker("3"); +verify.currentLineContentIs("if ( ( true ) ) { }"); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 59cb881ab7b..5175b52df6c 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -130,6 +130,7 @@ declare namespace FourSlashInterface { position(position: number, fileName?: string): any; file(index: number, content?: string, scriptKindName?: string): any; file(name: string, content?: string, scriptKindName?: string): any; + select(startMarker: string, endMarker: string): void; } class verifyNegatable { private negative; @@ -156,6 +157,8 @@ declare namespace FourSlashInterface { applicableRefactorAvailableAtMarker(markerName: string): void; codeFixDiagnosticsAvailableAtMarkers(markerNames: string[], diagnosticCode?: number): void; applicableRefactorAvailableForRange(): void; + + refactorAvailable(name?: string, subName?: string); } class verify extends verifyNegatable { assertHasRanges(ranges: Range[]): void; @@ -305,6 +308,8 @@ declare namespace FourSlashInterface { moveLeft(count?: number): void; enableFormatting(): void; disableFormatting(): void; + + applyRefactor(refactorName: string, actionName: string): void; } class debug { printCurrentParameterHelp(): void; diff --git a/tests/cases/fourslash/goToDefinitionDynamicImport3.ts b/tests/cases/fourslash/goToDefinitionDynamicImport3.ts new file mode 100644 index 00000000000..f8c5962f984 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionDynamicImport3.ts @@ -0,0 +1,8 @@ +/// + +// @Filename: foo.ts +//// export function /*Destination*/bar() { return "bar"; } + +//// import('./foo').then(({ ba/*1*/r }) => undefined); + +verify.goToDefinition("1", "Destination"); \ No newline at end of file diff --git a/tests/cases/fourslash/goToDefinitionDynamicImport4.ts b/tests/cases/fourslash/goToDefinitionDynamicImport4.ts new file mode 100644 index 00000000000..f8c5962f984 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionDynamicImport4.ts @@ -0,0 +1,8 @@ +/// + +// @Filename: foo.ts +//// export function /*Destination*/bar() { return "bar"; } + +//// import('./foo').then(({ ba/*1*/r }) => undefined); + +verify.goToDefinition("1", "Destination"); \ No newline at end of file diff --git a/tests/cases/fourslash/gotoDefinitionInObjectBindingPattern1.ts b/tests/cases/fourslash/gotoDefinitionInObjectBindingPattern1.ts new file mode 100644 index 00000000000..98c06c06d7b --- /dev/null +++ b/tests/cases/fourslash/gotoDefinitionInObjectBindingPattern1.ts @@ -0,0 +1,12 @@ +/// + +//// function bar(onfulfilled: (value: T) => void) { +//// return undefined; +//// } + +//// interface Test { +//// /*destination*/prop2: number +//// } +//// bar(({pr/*goto*/op2})=>{}); + +verify.goToDefinition("goto", "destination"); \ No newline at end of file diff --git a/tests/cases/fourslash/gotoDefinitionInObjectBindingPattern2.ts b/tests/cases/fourslash/gotoDefinitionInObjectBindingPattern2.ts new file mode 100644 index 00000000000..9e41f646c46 --- /dev/null +++ b/tests/cases/fourslash/gotoDefinitionInObjectBindingPattern2.ts @@ -0,0 +1,8 @@ +/// + +//// var p0 = ({a/*1*/a}) => {console.log(aa)}; +//// function f2({ a/*a1*/1, b/*b1*/1 }: { /*a1_dest*/a1: number, /*b1_dest*/b1: number } = { a1: 0, b1: 0 }) {} + +verify.goToDefinition("1", []); +verify.goToDefinition("a1", "a1_dest"); +verify.goToDefinition("b1", "b1_dest"); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportAmbient2.ts b/tests/cases/fourslash/importNameCodeFixNewImportAmbient2.ts index a23d7684d0a..64a8c28b3f8 100644 --- a/tests/cases/fourslash/importNameCodeFixNewImportAmbient2.ts +++ b/tests/cases/fourslash/importNameCodeFixNewImportAmbient2.ts @@ -1,6 +1,6 @@ /// -////[|/* +////[|/*! //// * I'm a license or something //// */ ////f1/*0*/();|] @@ -12,7 +12,7 @@ //// } verify.importFixAtPosition([ -`/* +`/*! * I'm a license or something */ import { f1 } from "ambient-module"; diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFileAllComments.ts b/tests/cases/fourslash/importNameCodeFixNewImportFileAllComments.ts new file mode 100644 index 00000000000..9dc914ba693 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFileAllComments.ts @@ -0,0 +1,35 @@ +/// + +//// [|/*! +//// * This is a license or something +//// */ +//// /// +//// /// +//// /// +//// /** +//// * This is a comment intended to be attached to this interface +//// */ +//// export interface SomeInterface { +//// } +//// f1/*0*/();|] + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; + +verify.importFixAtPosition([ +`/*! + * This is a license or something + */ +/// +/// +/// +import { f1 } from "./module"; + +/** + * This is a comment intended to be attached to this interface + */ +export interface SomeInterface { +} +f1();` +]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFileDetachedComments.ts b/tests/cases/fourslash/importNameCodeFixNewImportFileDetachedComments.ts new file mode 100644 index 00000000000..cfb537047fb --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFileDetachedComments.ts @@ -0,0 +1,23 @@ +/// + +//// [|/** +//// * This is a comment intended to be attached to this interface +//// */ +//// export interface SomeInterface { +//// } +//// f1/*0*/();|] + +// @Filename: module.ts +//// export function f1() {} +//// export var v1 = 5; + +verify.importFixAtPosition([ +`import { f1 } from "./module"; + +/** + * This is a comment intended to be attached to this interface + */ +export interface SomeInterface { +} +f1();` +]); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle0.ts b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle0.ts new file mode 100644 index 00000000000..6b5ef958e05 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle0.ts @@ -0,0 +1,18 @@ +/// + +//// [|import { v2 } from './module2'; +//// +//// f1/*0*/();|] + +// @Filename: module1.ts +//// export function f1() {} + +// @Filename: module2.ts +//// export var v2 = 6; + +verify.importFixAtPosition([ +`import { v2 } from './module2'; +import { f1 } from './module1'; + +f1();` +]); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle1.ts b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle1.ts new file mode 100644 index 00000000000..a9a12c41958 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle1.ts @@ -0,0 +1,18 @@ +/// + +//// [|import { v2 } from "./module2"; +//// +//// f1/*0*/();|] + +// @Filename: module1.ts +//// export function f1() {} + +// @Filename: module2.ts +//// export var v2 = 6; + +verify.importFixAtPosition([ +`import { v2 } from "./module2"; +import { f1 } from "./module1"; + +f1();` +]); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle2.ts b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle2.ts new file mode 100644 index 00000000000..c356e239ee8 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle2.ts @@ -0,0 +1,18 @@ +/// + +//// [|import m2 = require('./module2'); +//// +//// f1/*0*/();|] + +// @Filename: module1.ts +//// export function f1() {} + +// @Filename: module2.ts +//// export var v2 = 6; + +verify.importFixAtPosition([ +`import m2 = require('./module2'); +import { f1 } from './module1'; + +f1();` +]); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle3.ts b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle3.ts new file mode 100644 index 00000000000..5fa9f6e2c9a --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyle3.ts @@ -0,0 +1,19 @@ +/// + +//// [|export { v2 } from './module2'; +//// +//// f1/*0*/();|] + +// @Filename: module1.ts +//// export function f1() {} + +// @Filename: module2.ts +//// export var v2 = 6; + +verify.importFixAtPosition([ +`import { f1 } from './module1'; + +export { v2 } from './module2'; + +f1();` +]); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyleMixed0.ts b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyleMixed0.ts new file mode 100644 index 00000000000..2f17780df7f --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyleMixed0.ts @@ -0,0 +1,23 @@ +/// + +//// [|import { v2 } from "./module2"; +//// import { v3 } from './module3'; +//// +//// f1/*0*/();|] + +// @Filename: module1.ts +//// export function f1() {} + +// @Filename: module2.ts +//// export var v2 = 6; + +// @Filename: module3.ts +//// export var v3 = 6; + +verify.importFixAtPosition([ +`import { v2 } from "./module2"; +import { v3 } from './module3'; +import { f1 } from "./module1"; + +f1();` +]); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyleMixed1.ts b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyleMixed1.ts new file mode 100644 index 00000000000..ec68b3be0d8 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportFileQuoteStyleMixed1.ts @@ -0,0 +1,23 @@ +/// + +//// [|import { v2 } from './module2'; +//// import { v3 } from "./module3"; +//// +//// f1/*0*/();|] + +// @Filename: module1.ts +//// export function f1() {} + +// @Filename: module2.ts +//// export var v2 = 6; + +// @Filename: module3.ts +//// export var v3 = 6; + +verify.importFixAtPosition([ +`import { v2 } from './module2'; +import { v3 } from "./module3"; +import { f1 } from './module1'; + +f1();` +]); diff --git a/tests/cases/projects/NodeModulesSearch/maxDepthExceeded/tsconfig.json b/tests/cases/projects/NodeModulesSearch/maxDepthExceeded/tsconfig.json index 52633bb5a98..b2ee28482ba 100644 --- a/tests/cases/projects/NodeModulesSearch/maxDepthExceeded/tsconfig.json +++ b/tests/cases/projects/NodeModulesSearch/maxDepthExceeded/tsconfig.json @@ -4,6 +4,6 @@ "maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file "outDir": "built" }, - "include": ["**/*"], + "include": ["**/*", "node_modules/**/*"], "exclude": ["node_modules/m2/**/*"] } diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts index abfd71a8fff..5a3b4cc5048 100644 --- a/tests/webTestServer.ts +++ b/tests/webTestServer.ts @@ -125,22 +125,6 @@ function dir(dirPath: string, spec?: string, options?: any) { } } -// fs.rmdirSync won't delete directories with files in it -function deleteFolderRecursive(dirPath: string) { - if (fs.existsSync(dirPath)) { - fs.readdirSync(dirPath).forEach((file) => { - const curPath = path.join(dirPath, file); - if (fs.statSync(curPath).isDirectory()) { // recurse - deleteFolderRecursive(curPath); - } - else { // delete file - fs.unlinkSync(curPath); - } - }); - fs.rmdirSync(dirPath); - } -}; - function writeFile(path: string, data: any) { ensureDirectoriesExist(getDirectoryPath(path)); fs.writeFileSync(path, data); @@ -304,7 +288,7 @@ console.log(`Static file server running at\n => http://localhost:${port}/\nCTRL http.createServer((req: http.ServerRequest, res: http.ServerResponse) => { log(`${req.method} ${req.url}`); - const uri = url.parse(req.url).pathname; + const uri = decodeURIComponent(url.parse(req.url).pathname); const reqPath = path.join(process.cwd(), uri); const operation = getRequestOperation(req); handleRequestOperation(req, res, operation, reqPath); diff --git a/tslint.json b/tslint.json index bcd4dfa2223..de60ad7683a 100644 --- a/tslint.json +++ b/tslint.json @@ -7,6 +7,7 @@ "check-space" ], "curly":[true, "ignore-same-line"], + "debug-assert": true, "indent": [true, "spaces" ],