From 995f746c92f5824e6959f6382ab9e2ff6cf87999 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 12 Nov 2018 14:39:02 -0800 Subject: [PATCH] Add implicit-any error on JSDocFunctionType with no return type (#28487) --- src/compiler/checker.ts | 12 ++++- src/compiler/diagnosticMessages.json | 4 ++ src/compiler/utilities.ts | 7 ++- .../reference/checkJsdocTypeTag1.errors.txt | 47 +++++++++++++++++++ .../reference/checkJsdocTypeTag2.errors.txt | 5 +- ...docParameterParsingInfiniteLoop.errors.txt | 5 +- ...ocParseDotDotDotInJSDocFunction.errors.txt | 18 +++++++ 7 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/checkJsdocTypeTag1.errors.txt create mode 100644 tests/baselines/reference/jsdocParseDotDotDotInJSDocFunction.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 17bb5e193e5..d0160144a09 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13534,6 +13534,9 @@ namespace ts { case SyntaxKind.BindingElement: diagnostic = Diagnostics.Binding_element_0_implicitly_has_an_1_type; break; + case SyntaxKind.JSDocFunctionType: + error(declaration, Diagnostics.Function_type_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString); + return; case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: @@ -24401,6 +24404,13 @@ namespace ts { } } + function checkJSDocFunctionType(node: JSDocFunctionType): void { + if (produceDiagnostics && !node.type && !isJSDocConstructSignature(node)) { + reportImplicitAny(node, anyType); + } + checkSignatureDeclaration(node); + } + function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void { const classLike = getJSDocHost(node); if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) { @@ -27404,7 +27414,7 @@ namespace ts { case SyntaxKind.JSDocParameterTag: return checkJSDocParameterTag(node as JSDocParameterTag); case SyntaxKind.JSDocFunctionType: - checkSignatureDeclaration(node as JSDocFunctionType); + checkJSDocFunctionType(node as JSDocFunctionType); // falls through case SyntaxKind.JSDocNonNullableType: case SyntaxKind.JSDocNullableType: diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index bfa8108ef02..4da9f11047b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3979,6 +3979,10 @@ "category": "Error", "code": 7013 }, + "Function type, which lacks return-type annotation, implicitly has an '{0}' return type.": { + "category": "Error", + "code": 7014 + }, "Element implicitly has an 'any' type because index expression is not of type 'number'.": { "category": "Error", "code": 7015 diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f0d43be624a..29212e6fa63 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2091,10 +2091,9 @@ namespace ts { } export function isJSDocConstructSignature(node: Node) { - return node.kind === SyntaxKind.JSDocFunctionType && - (node as JSDocFunctionType).parameters.length > 0 && - (node as JSDocFunctionType).parameters[0].name && - ((node as JSDocFunctionType).parameters[0].name as Identifier).escapedText === "new"; + const param = isJSDocFunctionType(node) ? firstOrUndefined(node.parameters) : undefined; + const name = tryCast(param && param.name, isIdentifier); + return !!name && name.escapedText === "new"; } export function isJSDocTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag { diff --git a/tests/baselines/reference/checkJsdocTypeTag1.errors.txt b/tests/baselines/reference/checkJsdocTypeTag1.errors.txt new file mode 100644 index 00000000000..ee915f6aea3 --- /dev/null +++ b/tests/baselines/reference/checkJsdocTypeTag1.errors.txt @@ -0,0 +1,47 @@ +tests/cases/conformance/jsdoc/0.js(24,12): error TS7014: Function type, which lacks return-type annotation, implicitly has an 'any' return type. + + +==== tests/cases/conformance/jsdoc/0.js (1 errors) ==== + // @ts-check + /** @type {String} */ + var S = "hello world"; + + /** @type {number} */ + var n = 10; + + /** @type {*} */ + var anyT = 2; + anyT = "hello"; + + /** @type {?} */ + var anyT1 = 2; + anyT1 = "hi"; + + /** @type {Function} */ + const x = (a) => a + 1; + x(1); + + /** @type {function} */ + const y = (a) => a + 1; + y(1); + + /** @type {function (number)} */ + ~~~~~~~~~~~~~~~~~ +!!! error TS7014: Function type, which lacks return-type annotation, implicitly has an 'any' return type. + const x1 = (a) => a + 1; + x1(0); + + /** @type {function (number): number} */ + const x2 = (a) => a + 1; + x2(0); + + /** + * @type {object} + */ + var props = {}; + + /** + * @type {Object} + */ + var props = {}; + \ No newline at end of file diff --git a/tests/baselines/reference/checkJsdocTypeTag2.errors.txt b/tests/baselines/reference/checkJsdocTypeTag2.errors.txt index 1f0feacd675..2f57ec7f542 100644 --- a/tests/baselines/reference/checkJsdocTypeTag2.errors.txt +++ b/tests/baselines/reference/checkJsdocTypeTag2.errors.txt @@ -1,12 +1,13 @@ tests/cases/conformance/jsdoc/0.js(3,5): error TS2322: Type 'true' is not assignable to type 'string'. tests/cases/conformance/jsdoc/0.js(6,5): error TS2322: Type '"hello"' is not assignable to type 'number'. +tests/cases/conformance/jsdoc/0.js(8,12): error TS7014: Function type, which lacks return-type annotation, implicitly has an 'any' return type. tests/cases/conformance/jsdoc/0.js(10,4): error TS2345: Argument of type '"string"' is not assignable to parameter of type 'number'. tests/cases/conformance/jsdoc/0.js(17,1): error TS2322: Type 'number' is not assignable to type 'string'. tests/cases/conformance/jsdoc/0.js(20,21): error TS2339: Property 'concat' does not exist on type 'number'. tests/cases/conformance/jsdoc/0.js(24,19): error TS2322: Type 'number' is not assignable to type 'string'. -==== tests/cases/conformance/jsdoc/0.js (6 errors) ==== +==== tests/cases/conformance/jsdoc/0.js (7 errors) ==== // @ts-check /** @type {String} */ var S = true; @@ -19,6 +20,8 @@ tests/cases/conformance/jsdoc/0.js(24,19): error TS2322: Type 'number' is not as !!! error TS2322: Type '"hello"' is not assignable to type 'number'. /** @type {function (number)} */ + ~~~~~~~~~~~~~~~~~ +!!! error TS7014: Function type, which lacks return-type annotation, implicitly has an 'any' return type. const x1 = (a) => a + 1; x1("string"); ~~~~~~~~ diff --git a/tests/baselines/reference/jsdocParameterParsingInfiniteLoop.errors.txt b/tests/baselines/reference/jsdocParameterParsingInfiniteLoop.errors.txt index 71286e23bdb..e5bc7502d25 100644 --- a/tests/baselines/reference/jsdocParameterParsingInfiniteLoop.errors.txt +++ b/tests/baselines/reference/jsdocParameterParsingInfiniteLoop.errors.txt @@ -1,11 +1,14 @@ +tests/cases/compiler/example.js(3,11): error TS7014: Function type, which lacks return-type annotation, implicitly has an 'any' return type. tests/cases/compiler/example.js(3,20): error TS1110: Type expected. tests/cases/compiler/example.js(3,21): error TS2304: Cannot find name 'foo'. -==== tests/cases/compiler/example.js (2 errors) ==== +==== tests/cases/compiler/example.js (3 errors) ==== // @ts-check /** * @type {function(@foo)} + ~~~~~~~~~~~~~~ +!!! error TS7014: Function type, which lacks return-type annotation, implicitly has an 'any' return type. ~ !!! error TS1110: Type expected. ~~~ diff --git a/tests/baselines/reference/jsdocParseDotDotDotInJSDocFunction.errors.txt b/tests/baselines/reference/jsdocParseDotDotDotInJSDocFunction.errors.txt new file mode 100644 index 00000000000..034dabe778a --- /dev/null +++ b/tests/baselines/reference/jsdocParseDotDotDotInJSDocFunction.errors.txt @@ -0,0 +1,18 @@ +tests/cases/conformance/jsdoc/a.js(2,13): error TS7014: Function type, which lacks return-type annotation, implicitly has an 'any' return type. + + +==== tests/cases/conformance/jsdoc/a.js (1 errors) ==== + // from bcryptjs + /** @param {function(...[*])} callback */ + ~~~~~~~~~~~~~~~~ +!!! error TS7014: Function type, which lacks return-type annotation, implicitly has an 'any' return type. + function g(callback) { + callback([1], [2], [3]) + } + + /** + * @type {!function(...number):string} + * @inner + */ + var stringFromCharCode = String.fromCharCode; + \ No newline at end of file