diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8ebd639dc4..37fa09e554 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1272,8 +1272,10 @@ namespace ts { case SyntaxKind.PropertyAccessExpression: return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined; case SyntaxKind.ExpressionWithTypeArguments: - Debug.assert(isEntityNameExpression((node).expression)); - return (node).expression; + if (isEntityNameExpression((node).expression)) { + return (node).expression; + } + // falls through default: return undefined; } @@ -4917,6 +4919,8 @@ namespace ts { */ function getBaseConstructorTypeOfClass(type: InterfaceType): Type { if (!type.resolvedBaseConstructorType) { + const decl = type.symbol.valueDeclaration; + const extended = getClassExtendsHeritageClauseElement(decl); const baseTypeNode = getBaseTypeNodeOfClass(type); if (!baseTypeNode) { return type.resolvedBaseConstructorType = undefinedType; @@ -4925,6 +4929,10 @@ namespace ts { return unknownType; } const baseConstructorType = checkExpression(baseTypeNode.expression); + if (extended && baseTypeNode !== extended) { + Debug.assert(!extended.typeArguments); // Because this is in a JS file, and baseTypeNode is in an @extends tag + checkExpression(extended.expression); + } if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection)) { // Resolving the members of a class requires us to resolve the base class of that class. // We force resolution here such that we catch circularities now. @@ -14866,6 +14874,7 @@ namespace ts { function getSuggestionForNonexistentSymbol(location: Node, name: __String, meaning: SymbolFlags): string { const result = resolveNameHelper(location, name, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ false, (symbols, name, meaning) => { + // `name` from the callback === the outer `name` const symbol = getSymbol(symbols, name, meaning); // Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function // So the table *contains* `x` but `x` isn't actually in scope. @@ -19792,7 +19801,7 @@ namespace ts { if (!getParameterSymbolFromJSDoc(node)) { error(node.name, Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name, - unescapeLeadingUnderscores((node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name).escapedText)); + idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name)); } } @@ -19808,9 +19817,7 @@ namespace ts { if (extend) { const className = getIdentifierFromEntityNameExpression(extend.expression); if (className && name.escapedText !== className.escapedText) { - error(name, Diagnostics.JSDoc_augments_0_does_not_match_the_extends_1_clause, - unescapeLeadingUnderscores(name.escapedText), - unescapeLeadingUnderscores(className.escapedText)); + error(name, Diagnostics.JSDoc_augments_0_does_not_match_the_extends_1_clause, idText(name), idText(className)); } } } diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 97b555e01b..5b4c12b4f2 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -378,7 +378,7 @@ namespace ts { const catchVariable = getGeneratedNameForNode(errorRecord); const returnMethod = createTempVariable(/*recordTempVariable*/ undefined); const callValues = createAsyncValuesHelper(context, expression, /*location*/ node.expression); - const callNext = createCall(createPropertyAccess(iterator, "next" ), /*typeArguments*/ undefined, []); + const callNext = createCall(createPropertyAccess(iterator, "next"), /*typeArguments*/ undefined, []); const getDone = createPropertyAccess(result, "done"); const getValue = createPropertyAccess(result, "value"); const callReturn = createFunctionCall(returnMethod, iterator, []); diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index ecea15a54b..debb729067 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2824,7 +2824,7 @@ Actual: ${stringify(fullActual)}`); const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range); const refactor = refactors.find(r => r.name === refactorName); if (!refactor) { - this.raiseError(`The expected refactor: ${refactorName} is not available at the marker location.`); + this.raiseError(`The expected refactor: ${refactorName} is not available at the marker location.\nAvailable refactors: ${refactors.map(r => r.name)}`); } const action = refactor.actions.find(a => a.name === actionName); diff --git a/src/harness/unittests/extractFunctions.ts b/src/harness/unittests/extractFunctions.ts index 522ea7b129..27c21ce3fb 100644 --- a/src/harness/unittests/extractFunctions.ts +++ b/src/harness/unittests/extractFunctions.ts @@ -109,16 +109,6 @@ namespace ts { return C.foo();|] } } -}`); - testExtractFunction("extractFunction8", - `namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return 1 + [#|a1 + x|] + 100; - } - } }`); testExtractFunction("extractFunction9", `namespace A { diff --git a/src/harness/unittests/extractRanges.ts b/src/harness/unittests/extractRanges.ts index fcffffeba9..2ddcac482a 100644 --- a/src/harness/unittests/extractRanges.ts +++ b/src/harness/unittests/extractRanges.ts @@ -152,18 +152,6 @@ namespace ts { } } `); - testExtractRange(` - function f() { - return [$|1 + [#|2 + 3|]|]; - } - } - `); - testExtractRange(` - function f() { - return [$|1 + 2 + [#|3 + 4|]|]; - } - } - `); }); testExtractRangeFailed("extractRangeFailed1", @@ -311,7 +299,18 @@ switch (x) { testExtractRangeFailed("extractRangeFailed9", `var x = ([#||]1 + 2);`, [ - "Cannot extract empty range." + refactor.extractSymbol.Messages.CannotExtractEmpty.message + ]); + + testExtractRangeFailed("extractRangeFailed10", + ` + function f() { + return 1 + [#|2 + 3|]; + } + } + `, + [ + refactor.extractSymbol.Messages.CannotExtractRange.message ]); testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.CannotExtractIdentifier.message]); diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 7e1205f76c..1813a00047 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -4874,7 +4874,7 @@ namespace ts.projectSystem { }; const openFiles = [commonFile1.path]; const host = createServerHost([commonFile1, libFile, configFile]); - const { verifyProjectsUpdatedInBackgroundEventHandler, verifyInitialOpen } = createSession(host, ); + const { verifyProjectsUpdatedInBackgroundEventHandler, verifyInitialOpen } = createSession(host); verifyInitialOpen(commonFile1); host.reloadFS([commonFile1, libFile, configFile, commonFile2]); diff --git a/src/lib/es2015.core.d.ts b/src/lib/es2015.core.d.ts index e7c0d47971..5c2438d905 100644 --- a/src/lib/es2015.core.d.ts +++ b/src/lib/es2015.core.d.ts @@ -162,7 +162,7 @@ interface Math { * If any argument is NaN, the result is NaN. * If all arguments are either +0 or −0, the result is +0. */ - hypot(...values: number[] ): number; + hypot(...values: number[]): number; /** * Returns the integral part of the a numeric expression, x, removing any fractional digits. diff --git a/src/server/project.ts b/src/server/project.ts index 76d4499f68..f4a83c9444 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -976,9 +976,10 @@ namespace ts.server { // unknown version - return everything const projectFileNames = this.getFileNames(); const externalFiles = this.getExternalFiles().map(f => toNormalizedPath(f)); - this.lastReportedFileNames = arrayToSet(projectFileNames.concat(externalFiles)); + const allFiles = projectFileNames.concat(externalFiles); + this.lastReportedFileNames = arrayToSet(allFiles); this.lastReportedVersion = this.projectStructureVersion; - return { info, files: projectFileNames, projectErrors: this.getGlobalProjectErrors() }; + return { info, files: allFiles, projectErrors: this.getGlobalProjectErrors() }; } } diff --git a/src/services/formatting/rulesMap.ts b/src/services/formatting/rulesMap.ts index 5b4eacd2c2..d1f6e4724f 100644 --- a/src/services/formatting/rulesMap.ts +++ b/src/services/formatting/rulesMap.ts @@ -6,38 +6,20 @@ namespace ts.formatting { public map: RulesBucket[]; public mapRowLength: number; - constructor() { - this.map = []; - this.mapRowLength = 0; - } - - static create(rules: Rule[]): RulesMap { - const result = new RulesMap(); - result.Initialize(rules); - return result; - } - - public Initialize(rules: Rule[]) { + constructor(rules: ReadonlyArray) { this.mapRowLength = SyntaxKind.LastToken + 1; - this.map = new Array(this.mapRowLength * this.mapRowLength); // new Array(this.mapRowLength * this.mapRowLength); + this.map = new Array(this.mapRowLength * this.mapRowLength); // This array is used only during construction of the rulesbucket in the map - const rulesBucketConstructionStateList: RulesBucketConstructionState[] = new Array(this.map.length); // new Array(this.map.length); - - this.FillRules(rules, rulesBucketConstructionStateList); - return this.map; - } - - public FillRules(rules: Rule[], rulesBucketConstructionStateList: RulesBucketConstructionState[]): void { - rules.forEach((rule) => { + const rulesBucketConstructionStateList: RulesBucketConstructionState[] = new Array(this.map.length); + for (const rule of rules) { this.FillRule(rule, rulesBucketConstructionStateList); - }); + } } private GetRuleBucketIndex(row: number, column: number): number { Debug.assert(row <= SyntaxKind.LastKeyword && column <= SyntaxKind.LastKeyword, "Must compute formatting context from tokens"); - const rulesBucketIndex = (row * this.mapRowLength) + column; - return rulesBucketIndex; + return (row * this.mapRowLength) + column; } private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void { @@ -57,7 +39,7 @@ namespace ts.formatting { }); } - public GetRule(context: FormattingContext): Rule { + public GetRule(context: FormattingContext): Rule | undefined { const bucketIndex = this.GetRuleBucketIndex(context.currentTokenSpan.kind, context.nextTokenSpan.kind); const bucket = this.map[bucketIndex]; if (bucket) { @@ -74,7 +56,7 @@ namespace ts.formatting { const MaskBitSize = 5; const Mask = 0x1f; - export enum RulesPosition { + enum RulesPosition { IgnoreRulesSpecific = 0, IgnoreRulesAny = MaskBitSize * 1, ContextRulesSpecific = MaskBitSize * 2, diff --git a/src/services/formatting/rulesProvider.ts b/src/services/formatting/rulesProvider.ts index 1dd7acbdc6..fcf0854189 100644 --- a/src/services/formatting/rulesProvider.ts +++ b/src/services/formatting/rulesProvider.ts @@ -10,7 +10,7 @@ namespace ts.formatting { constructor() { this.globalRules = new Rules(); const activeRules = this.globalRules.HighPriorityCommonRules.concat(this.globalRules.UserConfigurableRules).concat(this.globalRules.LowPriorityCommonRules); - this.rulesMap = RulesMap.create(activeRules); + this.rulesMap = new RulesMap(activeRules); } public getRulesMap() { diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 375acb7e58..b88a013f52 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -201,9 +201,9 @@ namespace ts.refactor.extractSymbol { // 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); + const 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 end = getParentNodeInSpan(findTokenOnLeftOfPosition(sourceFile, textSpanEnd(span)), sourceFile, span); const declarations: Symbol[] = []; @@ -217,31 +217,10 @@ namespace ts.refactor.extractSymbol { } 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 { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.CannotExtractRange)] }; - } + // start and end nodes belong to different subtrees + return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.CannotExtractRange)] }; } + if (start !== end) { // start and end should be statements and parent should be either block or a source file if (!isBlockLike(start.parent)) { diff --git a/tests/baselines/reference/classExtendsInterface_not.errors.txt b/tests/baselines/reference/classExtendsInterface_not.errors.txt new file mode 100644 index 0000000000..6a4b250cb3 --- /dev/null +++ b/tests/baselines/reference/classExtendsInterface_not.errors.txt @@ -0,0 +1,8 @@ +tests/cases/compiler/classExtendsInterface_not.ts(1,20): error TS2339: Property 'bogus' does not exist on type '""'. + + +==== tests/cases/compiler/classExtendsInterface_not.ts (1 errors) ==== + class C extends "".bogus {} + ~~~~~ +!!! error TS2339: Property 'bogus' does not exist on type '""'. + \ No newline at end of file diff --git a/tests/baselines/reference/classExtendsInterface_not.js b/tests/baselines/reference/classExtendsInterface_not.js new file mode 100644 index 0000000000..3bbd6c9ffb --- /dev/null +++ b/tests/baselines/reference/classExtendsInterface_not.js @@ -0,0 +1,22 @@ +//// [classExtendsInterface_not.ts] +class C extends "".bogus {} + + +//// [classExtendsInterface_not.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 __()); + }; +})(); +var C = /** @class */ (function (_super) { + __extends(C, _super); + function C() { + return _super !== null && _super.apply(this, arguments) || this; + } + return C; +}("".bogus)); diff --git a/tests/baselines/reference/classExtendsInterface_not.symbols b/tests/baselines/reference/classExtendsInterface_not.symbols new file mode 100644 index 0000000000..de4af3a5c1 --- /dev/null +++ b/tests/baselines/reference/classExtendsInterface_not.symbols @@ -0,0 +1,4 @@ +=== tests/cases/compiler/classExtendsInterface_not.ts === +class C extends "".bogus {} +>C : Symbol(C, Decl(classExtendsInterface_not.ts, 0, 0)) + diff --git a/tests/baselines/reference/classExtendsInterface_not.types b/tests/baselines/reference/classExtendsInterface_not.types new file mode 100644 index 0000000000..cd1d627247 --- /dev/null +++ b/tests/baselines/reference/classExtendsInterface_not.types @@ -0,0 +1,7 @@ +=== tests/cases/compiler/classExtendsInterface_not.ts === +class C extends "".bogus {} +>C : C +>"".bogus : any +>"" : "" +>bogus : any + diff --git a/tests/baselines/reference/extractFunction/extractFunction8.ts b/tests/baselines/reference/extractFunction/extractFunction8.ts deleted file mode 100644 index adb8adbe56..0000000000 --- a/tests/baselines/reference/extractFunction/extractFunction8.ts +++ /dev/null @@ -1,65 +0,0 @@ -// ==ORIGINAL== -namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return 1 + a1 + x + 100; - } - } -} -// ==SCOPE::Extract to inner function in function 'a'== -namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return /*RENAME*/newFunction() + 100; - - function newFunction() { - return 1 + a1 + x; - } - } - } -} -// ==SCOPE::Extract to function in namespace 'B'== -namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return /*RENAME*/newFunction(a1) + 100; - } - - function newFunction(a1: number) { - return 1 + a1 + x; - } - } -} -// ==SCOPE::Extract to function in namespace 'A'== -namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return /*RENAME*/newFunction(a1) + 100; - } - } - - function newFunction(a1: number) { - return 1 + a1 + x; - } -} -// ==SCOPE::Extract to function in global scope== -namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return /*RENAME*/newFunction(a1, x) + 100; - } - } -} -function newFunction(a1: number, x: number) { - return 1 + a1 + x; -} diff --git a/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.errors.txt b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.errors.txt new file mode 100644 index 0000000000..11a7311fc3 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.errors.txt @@ -0,0 +1,10 @@ +/a.js(3,17): error TS2304: Cannot find name 'err'. + + +==== /a.js (1 errors) ==== + class A {} + /** @augments A */ + class B extends err() {} + ~~~ +!!! error TS2304: Cannot find name 'err'. + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.symbols b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.symbols new file mode 100644 index 0000000000..d78feca914 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.symbols @@ -0,0 +1,8 @@ +=== /a.js === +class A {} +>A : Symbol(A, Decl(a.js, 0, 0)) + +/** @augments A */ +class B extends err() {} +>B : Symbol(B, Decl(a.js, 0, 10)) + diff --git a/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.types b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.types new file mode 100644 index 0000000000..1a0fa09d3c --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.types @@ -0,0 +1,10 @@ +=== /a.js === +class A {} +>A : A + +/** @augments A */ +class B extends err() {} +>B : B +>err() : A +>err : any + diff --git a/tests/cases/compiler/classExtendsInterface_not.ts b/tests/cases/compiler/classExtendsInterface_not.ts new file mode 100644 index 0000000000..25a93e787e --- /dev/null +++ b/tests/cases/compiler/classExtendsInterface_not.ts @@ -0,0 +1 @@ +class C extends "".bogus {} diff --git a/tests/cases/compiler/jsdocAugments_errorInExtendsExpression.ts b/tests/cases/compiler/jsdocAugments_errorInExtendsExpression.ts new file mode 100644 index 0000000000..2fba59d446 --- /dev/null +++ b/tests/cases/compiler/jsdocAugments_errorInExtendsExpression.ts @@ -0,0 +1,8 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /a.js +class A {} +/** @augments A */ +class B extends err() {} diff --git a/tslint.json b/tslint.json index 7e1830dc73..d5a182ff0c 100644 --- a/tslint.json +++ b/tslint.json @@ -41,6 +41,7 @@ "avoid-escape" ], "semicolon": [true, "always", "ignore-bound-class-methods"], + "space-within-parens": true, "triple-equals": true, "type-operator-spacing": true, "typedef-whitespace": [