From b21c46b9b56d1d8cf7a6ebe71dd26f43015fc552 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 22 Sep 2017 16:21:31 -0700 Subject: [PATCH 1/5] support @extends in jsdoc --- src/compiler/checker.ts | 4 +- src/compiler/parser.ts | 12 +- src/compiler/types.ts | 11 +- src/compiler/utilities.ts | 8 +- src/services/completions.ts | 4 +- tests/baselines/reference/APISample_jsdoc.js | 4 +- tests/cases/compiler/APISample_jsdoc.ts | 232 +++++++++---------- tests/cases/fourslash/jsDocAugments.ts | 3 +- tests/cases/fourslash/jsDocExtends.ts | 22 ++ 9 files changed, 164 insertions(+), 136 deletions(-) create mode 100644 tests/cases/fourslash/jsDocExtends.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5c835582da023..2dbfa6bd40995 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4986,10 +4986,10 @@ namespace ts { baseType = getReturnTypeOfSignature(constructors[0]); } - // In a JS file, you can use the @augments jsdoc tag to specify a base type with type parameters + // In a JS file, you can use the @augments and @extends jsdoc tags to specify a base type with type parameters const valueDecl = type.symbol.valueDeclaration; if (valueDecl && isInJavaScriptFile(valueDecl)) { - const augTag = getJSDocAugmentsTag(type.symbol.valueDeclaration); + const augTag = getJSDocAugmentsOrExtendsTag(type.symbol.valueDeclaration); if (augTag) { baseType = getTypeFromTypeNode(augTag.typeExpression.type); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3809a07142139..7e9ac8c580478 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -423,8 +423,9 @@ namespace ts { return visitNode(cbNode, (node).typeExpression); case SyntaxKind.JSDocTypeTag: return visitNode(cbNode, (node).typeExpression); - case SyntaxKind.JSDocAugmentsTag: - return visitNode(cbNode, (node).typeExpression); + case SyntaxKind.JSDocAugmentsOrExtendsTag: + case SyntaxKind.JSDocExtendsTag: + return visitNode(cbNode, (node).typeExpression); case SyntaxKind.JSDocTemplateTag: return visitNodes(cbNode, cbNodes, (node).typeParameters); case SyntaxKind.JSDocTypedefTag: @@ -6366,7 +6367,8 @@ namespace ts { if (tagName) { switch (tagName.escapedText) { case "augments": - tag = parseAugmentsTag(atToken, tagName); + case "extends": + tag = parseAugmentsOrExtendsTag(atToken, tagName); break; case "class": case "constructor": @@ -6603,10 +6605,10 @@ namespace ts { return finishNode(result); } - function parseAugmentsTag(atToken: AtToken, tagName: Identifier): JSDocAugmentsTag { + function parseAugmentsOrExtendsTag(atToken: AtToken, tagName: Identifier): JSDocAugmentsOrExtendsTag { const typeExpression = tryParseTypeExpression(); - const result = createNode(SyntaxKind.JSDocAugmentsTag, atToken.pos); + const result = createNode(SyntaxKind.JSDocAugmentsOrExtendsTag, atToken.pos); result.atToken = atToken; result.tagName = tagName; result.typeExpression = typeExpression; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4e5ca9f07e77f..e8f5423679801 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -363,7 +363,8 @@ namespace ts { JSDocVariadicType, JSDocComment, JSDocTag, - JSDocAugmentsTag, + JSDocAugmentsOrExtendsTag, + JSDocExtendsTag, JSDocClassTag, JSDocParameterTag, JSDocReturnTag, @@ -2159,8 +2160,12 @@ namespace ts { kind: SyntaxKind.JSDocTag; } - export interface JSDocAugmentsTag extends JSDocTag { - kind: SyntaxKind.JSDocAugmentsTag; + /** + * Note that `@extends` is a synonym of `@augments`. + * Both are covered by this interface. + */ + export interface JSDocAugmentsOrExtendsTag extends JSDocTag { + kind: SyntaxKind.JSDocAugmentsOrExtendsTag; typeExpression: JSDocTypeExpression; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 674215b05835e..f9a0c4c38ab3d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4072,8 +4072,8 @@ namespace ts { } /** Gets the JSDoc augments tag for the node if present */ - export function getJSDocAugmentsTag(node: Node): JSDocAugmentsTag | undefined { - return getFirstJSDocTag(node, SyntaxKind.JSDocAugmentsTag) as JSDocAugmentsTag; + export function getJSDocAugmentsOrExtendsTag(node: Node): JSDocAugmentsOrExtendsTag | undefined { + return getFirstJSDocTag(node, SyntaxKind.JSDocAugmentsOrExtendsTag) as JSDocAugmentsOrExtendsTag; } /** Gets the JSDoc class tag for the node if present */ @@ -4765,8 +4765,8 @@ namespace ts { return node.kind === SyntaxKind.JSDocComment; } - export function isJSDocAugmentsTag(node: Node): node is JSDocAugmentsTag { - return node.kind === SyntaxKind.JSDocAugmentsTag; + export function isJSDocAugmentsOrExtendsTag(node: Node): node is JSDocAugmentsOrExtendsTag { + return node.kind === SyntaxKind.JSDocAugmentsOrExtendsTag; } export function isJSDocParameterTag(node: Node): node is JSDocParameterTag { diff --git a/src/services/completions.ts b/src/services/completions.ts index e271ef1210405..a954f687cb5d8 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -581,11 +581,11 @@ namespace ts.Completions { return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), request, keywordFilters }; - type JSDocTagWithTypeExpression = JSDocAugmentsTag | JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag; + type JSDocTagWithTypeExpression = JSDocAugmentsOrExtendsTag | JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag; function isTagWithTypeExpression(tag: JSDocTag): tag is JSDocTagWithTypeExpression { switch (tag.kind) { - case SyntaxKind.JSDocAugmentsTag: + case SyntaxKind.JSDocAugmentsOrExtendsTag: case SyntaxKind.JSDocParameterTag: case SyntaxKind.JSDocPropertyTag: case SyntaxKind.JSDocReturnTag: diff --git a/tests/baselines/reference/APISample_jsdoc.js b/tests/baselines/reference/APISample_jsdoc.js index c74e188f38b22..33857d06a6a66 100644 --- a/tests/baselines/reference/APISample_jsdoc.js +++ b/tests/baselines/reference/APISample_jsdoc.js @@ -101,7 +101,7 @@ function getAllTags(node: ts.Node) { function getSomeOtherTags(node: ts.Node) { const tags: (ts.JSDocTag | undefined)[] = []; - tags.push(ts.getJSDocAugmentsTag(node)); + tags.push(ts.getJSDocAugmentsOrExtendsTag(node)); tags.push(ts.getJSDocClassTag(node)); tags.push(ts.getJSDocReturnTag(node)); const type = ts.getJSDocTypeTag(node); @@ -200,7 +200,7 @@ function getAllTags(node) { } function getSomeOtherTags(node) { var tags = []; - tags.push(ts.getJSDocAugmentsTag(node)); + tags.push(ts.getJSDocAugmentsOrExtendsTag(node)); tags.push(ts.getJSDocClassTag(node)); tags.push(ts.getJSDocReturnTag(node)); var type = ts.getJSDocTypeTag(node); diff --git a/tests/cases/compiler/APISample_jsdoc.ts b/tests/cases/compiler/APISample_jsdoc.ts index 70b814ffff487..491ff2b7a20d0 100644 --- a/tests/cases/compiler/APISample_jsdoc.ts +++ b/tests/cases/compiler/APISample_jsdoc.ts @@ -1,116 +1,116 @@ -// @module: commonjs -// @includebuiltfile: typescript_standalone.d.ts -// @strict:true - -/* - * Note: This test is a public API sample. The original sources can be found - * at: https://github.com/YousefED/typescript-json-schema - * https://github.com/vega/ts-json-schema-generator - * Please log a "breaking change" issue for any API breaking change affecting this issue - */ - -declare var console: any; - -import * as ts from "typescript"; - -// excerpted from https://github.com/YousefED/typescript-json-schema -// (converted from a method and modified; for example, `this: any` to compensate, among other changes) -function parseCommentsIntoDefinition(this: any, - symbol: ts.Symbol, - definition: {description?: string, [s: string]: string | undefined}, - otherAnnotations: { [s: string]: true}): void { - if (!symbol) { - return; - } - - // the comments for a symbol - let comments = symbol.getDocumentationComment(); - - if (comments.length) { - definition.description = comments.map(comment => comment.kind === "lineBreak" ? comment.text : comment.text.trim().replace(/\r\n/g, "\n")).join(""); - } - - // jsdocs are separate from comments - const jsdocs = symbol.getJsDocTags(); - jsdocs.forEach(doc => { - // if we have @TJS-... annotations, we have to parse them - const { name, text } = doc; - if (this.userValidationKeywords[name]) { - definition[name] = this.parseValue(text); - } else { - // special annotations - otherAnnotations[doc.name] = true; - } - }); -} - - -// excerpted from https://github.com/vega/ts-json-schema-generator -export interface Annotations { - [name: string]: any; -} -function getAnnotations(this: any, node: ts.Node): Annotations | undefined { - const symbol: ts.Symbol = (node as any).symbol; - if (!symbol) { - return undefined; - } - - const jsDocTags: ts.JSDocTagInfo[] = symbol.getJsDocTags(); - if (!jsDocTags || !jsDocTags.length) { - return undefined; - } - - const annotations: Annotations = jsDocTags.reduce((result: Annotations, jsDocTag: ts.JSDocTagInfo) => { - const value = this.parseJsDocTag(jsDocTag); - if (value !== undefined) { - result[jsDocTag.name] = value; - } - - return result; - }, {}); - return Object.keys(annotations).length ? annotations : undefined; -} - -// these examples are artificial and mostly nonsensical -function parseSpecificTags(node: ts.Node) { - if (node.kind === ts.SyntaxKind.Parameter) { - return ts.getJSDocParameterTags(node as ts.ParameterDeclaration); - } - if (node.kind === ts.SyntaxKind.FunctionDeclaration) { - const func = node as ts.FunctionDeclaration; - if (ts.hasJSDocParameterTags(func)) { - const flat: ts.JSDocTag[] = []; - for (const tags of func.parameters.map(ts.getJSDocParameterTags)) { - if (tags) flat.push(...tags); - } - return flat; - } - } -} - -function getReturnTypeFromJSDoc(node: ts.Node) { - if (node.kind === ts.SyntaxKind.FunctionDeclaration) { - return ts.getJSDocReturnType(node); - } - let type = ts.getJSDocType(node); - if (type && type.kind === ts.SyntaxKind.FunctionType) { - return (type as ts.FunctionTypeNode).type; - } -} - -function getAllTags(node: ts.Node) { - ts.getJSDocTags(node); -} - -function getSomeOtherTags(node: ts.Node) { - const tags: (ts.JSDocTag | undefined)[] = []; - tags.push(ts.getJSDocAugmentsTag(node)); - tags.push(ts.getJSDocClassTag(node)); - tags.push(ts.getJSDocReturnTag(node)); - const type = ts.getJSDocTypeTag(node); - if (type) { - tags.push(type); - } - tags.push(ts.getJSDocTemplateTag(node)); - return tags; -} +// @module: commonjs +// @includebuiltfile: typescript_standalone.d.ts +// @strict:true + +/* + * Note: This test is a public API sample. The original sources can be found + * at: https://github.com/YousefED/typescript-json-schema + * https://github.com/vega/ts-json-schema-generator + * Please log a "breaking change" issue for any API breaking change affecting this issue + */ + +declare var console: any; + +import * as ts from "typescript"; + +// excerpted from https://github.com/YousefED/typescript-json-schema +// (converted from a method and modified; for example, `this: any` to compensate, among other changes) +function parseCommentsIntoDefinition(this: any, + symbol: ts.Symbol, + definition: {description?: string, [s: string]: string | undefined}, + otherAnnotations: { [s: string]: true}): void { + if (!symbol) { + return; + } + + // the comments for a symbol + let comments = symbol.getDocumentationComment(); + + if (comments.length) { + definition.description = comments.map(comment => comment.kind === "lineBreak" ? comment.text : comment.text.trim().replace(/\r\n/g, "\n")).join(""); + } + + // jsdocs are separate from comments + const jsdocs = symbol.getJsDocTags(); + jsdocs.forEach(doc => { + // if we have @TJS-... annotations, we have to parse them + const { name, text } = doc; + if (this.userValidationKeywords[name]) { + definition[name] = this.parseValue(text); + } else { + // special annotations + otherAnnotations[doc.name] = true; + } + }); +} + + +// excerpted from https://github.com/vega/ts-json-schema-generator +export interface Annotations { + [name: string]: any; +} +function getAnnotations(this: any, node: ts.Node): Annotations | undefined { + const symbol: ts.Symbol = (node as any).symbol; + if (!symbol) { + return undefined; + } + + const jsDocTags: ts.JSDocTagInfo[] = symbol.getJsDocTags(); + if (!jsDocTags || !jsDocTags.length) { + return undefined; + } + + const annotations: Annotations = jsDocTags.reduce((result: Annotations, jsDocTag: ts.JSDocTagInfo) => { + const value = this.parseJsDocTag(jsDocTag); + if (value !== undefined) { + result[jsDocTag.name] = value; + } + + return result; + }, {}); + return Object.keys(annotations).length ? annotations : undefined; +} + +// these examples are artificial and mostly nonsensical +function parseSpecificTags(node: ts.Node) { + if (node.kind === ts.SyntaxKind.Parameter) { + return ts.getJSDocParameterTags(node as ts.ParameterDeclaration); + } + if (node.kind === ts.SyntaxKind.FunctionDeclaration) { + const func = node as ts.FunctionDeclaration; + if (ts.hasJSDocParameterTags(func)) { + const flat: ts.JSDocTag[] = []; + for (const tags of func.parameters.map(ts.getJSDocParameterTags)) { + if (tags) flat.push(...tags); + } + return flat; + } + } +} + +function getReturnTypeFromJSDoc(node: ts.Node) { + if (node.kind === ts.SyntaxKind.FunctionDeclaration) { + return ts.getJSDocReturnType(node); + } + let type = ts.getJSDocType(node); + if (type && type.kind === ts.SyntaxKind.FunctionType) { + return (type as ts.FunctionTypeNode).type; + } +} + +function getAllTags(node: ts.Node) { + ts.getJSDocTags(node); +} + +function getSomeOtherTags(node: ts.Node) { + const tags: (ts.JSDocTag | undefined)[] = []; + tags.push(ts.getJSDocAugmentsOrExtendsTag(node)); + tags.push(ts.getJSDocClassTag(node)); + tags.push(ts.getJSDocReturnTag(node)); + const type = ts.getJSDocTypeTag(node); + if (type) { + tags.push(type); + } + tags.push(ts.getJSDocTemplateTag(node)); + return tags; +} diff --git a/tests/cases/fourslash/jsDocAugments.ts b/tests/cases/fourslash/jsDocAugments.ts index 24458c529fb92..cd2190e548683 100644 --- a/tests/cases/fourslash/jsDocAugments.ts +++ b/tests/cases/fourslash/jsDocAugments.ts @@ -15,9 +15,8 @@ // @Filename: declarations.d.ts //// declare class Thing { -//// mine: T; +//// mine: T; //// } goTo.marker(); verify.quickInfoIs("(local var) x: string"); - diff --git a/tests/cases/fourslash/jsDocExtends.ts b/tests/cases/fourslash/jsDocExtends.ts new file mode 100644 index 0000000000000..6bce55695333a --- /dev/null +++ b/tests/cases/fourslash/jsDocExtends.ts @@ -0,0 +1,22 @@ +/// + +// @allowJs: true +// @Filename: dummy.js + +//// /** +//// * @extends {Thing} +//// */ +//// class MyStringThing extends Thing { +//// constructor() { +//// var x = this.mine; +//// x/**/; +//// } +//// } + +// @Filename: declarations.d.ts +//// declare class Thing { +//// mine: T; +//// } + +goTo.marker(); +verify.quickInfoIs("(local var) x: string"); From fbf8df66f006553bc720751389e18d98a3f9b3cf Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 6 Oct 2017 14:27:32 -0700 Subject: [PATCH 2/5] accept baselines --- tests/baselines/reference/APISample_jsdoc.js | 4 ++-- tests/baselines/reference/api/tsserverlibrary.d.ts | 4 ++++ tests/baselines/reference/api/typescript.d.ts | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/APISample_jsdoc.js b/tests/baselines/reference/APISample_jsdoc.js index 33857d06a6a66..c74e188f38b22 100644 --- a/tests/baselines/reference/APISample_jsdoc.js +++ b/tests/baselines/reference/APISample_jsdoc.js @@ -101,7 +101,7 @@ function getAllTags(node: ts.Node) { function getSomeOtherTags(node: ts.Node) { const tags: (ts.JSDocTag | undefined)[] = []; - tags.push(ts.getJSDocAugmentsOrExtendsTag(node)); + tags.push(ts.getJSDocAugmentsTag(node)); tags.push(ts.getJSDocClassTag(node)); tags.push(ts.getJSDocReturnTag(node)); const type = ts.getJSDocTypeTag(node); @@ -200,7 +200,7 @@ function getAllTags(node) { } function getSomeOtherTags(node) { var tags = []; - tags.push(ts.getJSDocAugmentsOrExtendsTag(node)); + tags.push(ts.getJSDocAugmentsTag(node)); tags.push(ts.getJSDocClassTag(node)); tags.push(ts.getJSDocReturnTag(node)); var type = ts.getJSDocTypeTag(node); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 3bb2ed1167488..ca8696b11fb8d 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1442,6 +1442,10 @@ declare namespace ts { interface JSDocUnknownTag extends JSDocTag { kind: SyntaxKind.JSDocTag; } + /** + * Note that `@extends` is a synonym of `@augments`. + * Both tags are represented by this interface. + */ interface JSDocAugmentsTag extends JSDocTag { kind: SyntaxKind.JSDocAugmentsTag; class: ExpressionWithTypeArguments & { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index d41db2eb413ee..820be44f1f1c4 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1442,6 +1442,10 @@ declare namespace ts { interface JSDocUnknownTag extends JSDocTag { kind: SyntaxKind.JSDocTag; } + /** + * Note that `@extends` is a synonym of `@augments`. + * Both tags are represented by this interface. + */ interface JSDocAugmentsTag extends JSDocTag { kind: SyntaxKind.JSDocAugmentsTag; class: ExpressionWithTypeArguments & { From 0afaadba3b83dfbad89a8c2c5d812ef8ab783361 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 6 Oct 2017 15:56:39 -0700 Subject: [PATCH 3/5] add error for multiple tags --- src/compiler/checker.ts | 14 ++++-- src/compiler/diagnosticMessages.json | 4 ++ src/compiler/utilities.ts | 6 +++ .../fourslash/jsDocAugmentsAndExtends.ts | 50 +++++++++++++++++++ 4 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 tests/cases/fourslash/jsDocAugmentsAndExtends.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 718fd76625f01..31c9acd8fb136 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20009,14 +20009,20 @@ namespace ts { } function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void { - const cls = getJSDocHost(node); - if (!isClassDeclaration(cls) && !isClassExpression(cls)) { - error(cls, Diagnostics.JSDoc_augments_is_not_attached_to_a_class_declaration); + const classLike = getJSDocHost(node); + if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) { + error(classLike, Diagnostics.JSDoc_augments_is_not_attached_to_a_class_declaration); return; } + const augmentsTags = getAllJSDocTagsOfKind(classLike, SyntaxKind.JSDocAugmentsTag); + Debug.assert(augmentsTags.length > 0); + if (augmentsTags.length > 1) { + error(augmentsTags[1], Diagnostics.The_total_number_of_augments_and_extends_tags_allowed_for_a_single_class_declaration_is_at_most_1); + } + const name = getIdentifierFromEntityNameExpression(node.class.expression); - const extend = getClassExtendsHeritageClauseElement(cls); + const extend = getClassExtendsHeritageClauseElement(classLike); if (extend) { const className = getIdentifierFromEntityNameExpression(extend.expression); if (className && name.escapedText !== className.escapedText) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index f3d6d4fcc4706..64c4d99d3496a 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3527,6 +3527,10 @@ "category": "Error", "code": 8024 }, + "The total number of `@augments` and `@extends` tags allowed for a single class declaration is at most 1.": { + "category": "Error", + "code": 8025 + }, "Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": { "category": "Error", "code": 9002 diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f0eb394adb7dd..825b310ddeb8b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4247,6 +4247,12 @@ namespace ts { return find(tags, doc => doc.kind === kind); } + /** Gets all JSDoc tags of a specified kind, or undefined if not present. */ + export function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray | undefined { + const tags = getJSDocTags(node); + return filter(tags, doc => doc.kind === kind); + } + } // Simple node tests of the form `node.kind === SyntaxKind.Foo`. diff --git a/tests/cases/fourslash/jsDocAugmentsAndExtends.ts b/tests/cases/fourslash/jsDocAugmentsAndExtends.ts new file mode 100644 index 0000000000000..e76a617a3cff9 --- /dev/null +++ b/tests/cases/fourslash/jsDocAugmentsAndExtends.ts @@ -0,0 +1,50 @@ +/// + +// @allowJs: true +// @checkJs: true +// @Filename: dummy.js + +//// /** +//// * @augments {Thing} +//// * @extends {Thing} +//// */ +//// class MyStringThing extends Thing { +//// constructor() { +//// var x = this.mine; +//// x/**/; +//// } +//// } + +// @Filename: declarations.d.ts +//// declare class Thing { +//// mine: T; +//// } + +// if more than one tag is present, report an error and take the type of the first entry. + +goTo.marker(); +verify.quickInfoIs("(local var) x: number"); +verify.getSemanticDiagnostics( +`[ + { + "message": "The total number of \`@augments\` and \`@extends\` tags allowed for a single class declaration is at most 1.", + "start": 36, + "length": 24, + "category": "error", + "code": 8025 + }, + { + "message": "Constructors for derived classes must contain a \'super\' call.", + "start": 105, + "length": 59, + "category": "error", + "code": 2377 + }, + { + "message": "\'super\' must be called before accessing \'this\' in the constructor of a derived class.", + "start": 137, + "length": 4, + "category": "error", + "code": 17009 + } +]`); \ No newline at end of file From 932b1b038c712b73eda432b5296263fe32af6a6d Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 6 Oct 2017 16:16:37 -0700 Subject: [PATCH 4/5] better error message --- src/compiler/checker.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- .../cases/fourslash/jsDocAugmentsAndExtends.ts | 17 ++--------------- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 31c9acd8fb136..aa5d10c475c8f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20018,7 +20018,7 @@ namespace ts { const augmentsTags = getAllJSDocTagsOfKind(classLike, SyntaxKind.JSDocAugmentsTag); Debug.assert(augmentsTags.length > 0); if (augmentsTags.length > 1) { - error(augmentsTags[1], Diagnostics.The_total_number_of_augments_and_extends_tags_allowed_for_a_single_class_declaration_is_at_most_1); + error(augmentsTags[1], Diagnostics.Class_declarations_cannot_have_more_than_one_augments_or_extends_tag); } const name = getIdentifierFromEntityNameExpression(node.class.expression); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 64c4d99d3496a..e0de43a97db6c 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3527,7 +3527,7 @@ "category": "Error", "code": 8024 }, - "The total number of `@augments` and `@extends` tags allowed for a single class declaration is at most 1.": { + "Class declarations cannot have more than one `@augments` or `@extends` tag.": { "category": "Error", "code": 8025 }, diff --git a/tests/cases/fourslash/jsDocAugmentsAndExtends.ts b/tests/cases/fourslash/jsDocAugmentsAndExtends.ts index e76a617a3cff9..10f33260268ac 100644 --- a/tests/cases/fourslash/jsDocAugmentsAndExtends.ts +++ b/tests/cases/fourslash/jsDocAugmentsAndExtends.ts @@ -10,6 +10,7 @@ //// */ //// class MyStringThing extends Thing { //// constructor() { +//// super(); //// var x = this.mine; //// x/**/; //// } @@ -27,24 +28,10 @@ verify.quickInfoIs("(local var) x: number"); verify.getSemanticDiagnostics( `[ { - "message": "The total number of \`@augments\` and \`@extends\` tags allowed for a single class declaration is at most 1.", + "message": "Class declarations cannot have more than one \`@augments\` or \`@extends\` tag.", "start": 36, "length": 24, "category": "error", "code": 8025 - }, - { - "message": "Constructors for derived classes must contain a \'super\' call.", - "start": 105, - "length": 59, - "category": "error", - "code": 2377 - }, - { - "message": "\'super\' must be called before accessing \'this\' in the constructor of a derived class.", - "start": 137, - "length": 4, - "category": "error", - "code": 17009 } ]`); \ No newline at end of file From 5f3d6e753e0c89419bd3734ccf991c26ba772131 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 9 Oct 2017 14:43:51 -0700 Subject: [PATCH 5/5] update baselines --- tests/baselines/reference/api/tsserverlibrary.d.ts | 2 ++ tests/baselines/reference/api/typescript.d.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index ca8696b11fb8d..5f4ef1dbdfec4 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2870,6 +2870,8 @@ declare namespace ts { function getJSDocReturnType(node: Node): TypeNode | undefined; /** Get all JSDoc tags related to a node, including those on parent nodes. */ function getJSDocTags(node: Node): ReadonlyArray | undefined; + /** Gets all JSDoc tags of a specified kind, or undefined if not present. */ + function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray | undefined; } declare namespace ts { function isNumericLiteral(node: Node): node is NumericLiteral; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 820be44f1f1c4..e608c7ffc3d1e 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2925,6 +2925,8 @@ declare namespace ts { function getJSDocReturnType(node: Node): TypeNode | undefined; /** Get all JSDoc tags related to a node, including those on parent nodes. */ function getJSDocTags(node: Node): ReadonlyArray | undefined; + /** Gets all JSDoc tags of a specified kind, or undefined if not present. */ + function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray | undefined; } declare namespace ts { function isNumericLiteral(node: Node): node is NumericLiteral;