From ecdc4b363e75ad98de253dfdf858ff62d627b337 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 19 Oct 2017 09:12:49 -0700 Subject: [PATCH 1/4] Test:jsdoc @param errors on vardecls/assignments --- ...ramOnVariableDeclaredFunctionExpression.js | 26 +++++++++++++++++++ ...VariableDeclaredFunctionExpression.symbols | 23 ++++++++++++++++ ...OnVariableDeclaredFunctionExpression.types | 26 +++++++++++++++++++ ...ramOnVariableDeclaredFunctionExpression.ts | 15 +++++++++++ 4 files changed, 90 insertions(+) create mode 100644 tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.js create mode 100644 tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.symbols create mode 100644 tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.types create mode 100644 tests/cases/conformance/jsdoc/checkJsdocParamOnVariableDeclaredFunctionExpression.ts diff --git a/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.js b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.js new file mode 100644 index 0000000000000..312e62b843567 --- /dev/null +++ b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.js @@ -0,0 +1,26 @@ +//// [0.js] +// @ts-check +/** + * @param {number=} n + * @param {string} [s] + */ +var x = function foo(n, s) {} +var y; +/** + * @param {boolean!} b + */ +y = function bar(b) {} + + +//// [0.js] +// @ts-check +/** + * @param {number=} n + * @param {string} [s] + */ +var x = function foo(n, s) { }; +var y; +/** + * @param {boolean!} b + */ +y = function bar(b) { }; diff --git a/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.symbols b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.symbols new file mode 100644 index 0000000000000..26bf26e1a4a4b --- /dev/null +++ b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.symbols @@ -0,0 +1,23 @@ +=== tests/cases/conformance/jsdoc/0.js === +// @ts-check +/** + * @param {number=} n + * @param {string} [s] + */ +var x = function foo(n, s) {} +>x : Symbol(x, Decl(0.js, 5, 3)) +>foo : Symbol(foo, Decl(0.js, 5, 7)) +>n : Symbol(n, Decl(0.js, 5, 21)) +>s : Symbol(s, Decl(0.js, 5, 23)) + +var y; +>y : Symbol(y, Decl(0.js, 6, 3)) + +/** + * @param {boolean!} b + */ +y = function bar(b) {} +>y : Symbol(y, Decl(0.js, 6, 3)) +>bar : Symbol(bar, Decl(0.js, 10, 3)) +>b : Symbol(b, Decl(0.js, 10, 17)) + diff --git a/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.types b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.types new file mode 100644 index 0000000000000..f9537219e20bf --- /dev/null +++ b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.types @@ -0,0 +1,26 @@ +=== tests/cases/conformance/jsdoc/0.js === +// @ts-check +/** + * @param {number=} n + * @param {string} [s] + */ +var x = function foo(n, s) {} +>x : (n?: number, s?: string) => void +>function foo(n, s) {} : (n?: number, s?: string) => void +>foo : (n?: number, s?: string) => void +>n : number +>s : string + +var y; +>y : any + +/** + * @param {boolean!} b + */ +y = function bar(b) {} +>y = function bar(b) {} : (b: boolean) => void +>y : any +>function bar(b) {} : (b: boolean) => void +>bar : (b: boolean) => void +>b : boolean + diff --git a/tests/cases/conformance/jsdoc/checkJsdocParamOnVariableDeclaredFunctionExpression.ts b/tests/cases/conformance/jsdoc/checkJsdocParamOnVariableDeclaredFunctionExpression.ts new file mode 100644 index 0000000000000..7443357337aa5 --- /dev/null +++ b/tests/cases/conformance/jsdoc/checkJsdocParamOnVariableDeclaredFunctionExpression.ts @@ -0,0 +1,15 @@ +// @allowJS: true +// @suppressOutputPathCheck: true + +// @filename: 0.js +// @ts-check +/** + * @param {number=} n + * @param {string} [s] + */ +var x = function foo(n, s) {} +var y; +/** + * @param {boolean!} b + */ +y = function bar(b) {} From c2bbfafcbe744ad3617727edf0ebcc84dfe3fc1e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 19 Oct 2017 09:13:31 -0700 Subject: [PATCH 2/4] Fix getParameterSymbolFromJSDoc --- src/compiler/utilities.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 980a8cc61c47e..5bc8e78f74990 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1606,8 +1606,19 @@ namespace ts { return undefined; } const name = node.name.escapedText; - const func = getJSDocHost(node); - if (!isFunctionLike(func)) { + const decl = getJSDocHost(node); + let func: FunctionLike; + if (isExpressionStatement(decl) && isBinaryExpression(decl.expression) && isFunctionLike(decl.expression.right)) { + func = decl.expression.right; + } + else if (isVariableStatement(decl) && decl.declarationList.declarations.length === 1 && isVariableDeclaration(decl.declarationList.declarations[0]) + && isFunctionLike(decl.declarationList.declarations[0].initializer)) { + func = decl.declarationList.declarations[0].initializer as FunctionLike; + } + else if (isFunctionLike(decl)) { + func = decl; + } + else { return undefined; } const parameter = find(func.parameters, p => From 97a6f14ca28c1c41d7bdcfcd471aca34898a8341 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 19 Oct 2017 14:12:56 -0700 Subject: [PATCH 3/4] Consolidate jsdoc node getters They are now used both in getJSDocCommentsAndTagsWorker and in geParameterSymbolFromJSDoc. --- src/compiler/utilities.ts | 86 +++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5bc8e78f74990..b29bea18e4183 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1538,6 +1538,34 @@ namespace ts { return getJSDocCommentsAndTags(node); } + export function getSourceOfAssignment(node: Node): Node { + return isExpressionStatement(node) && + node.expression && isBinaryExpression(node.expression) && + node.expression.operatorToken.kind === SyntaxKind.EqualsToken && + node.expression.right; + } + + export function getSingleInitializerOfVariableStatement(node: Node, child?: Node): Node { + return isVariableStatement(node) && + node.declarationList.declarations.length > 0 && + (!child || node.declarationList.declarations[0].initializer === child) && + node.declarationList.declarations[0].initializer; + } + + export function getSingleVariableOfVariableStatement(node: Node, child?: Node): Node { + return isVariableStatement(node) && + node.declarationList.declarations.length > 0 && + (!child || node.declarationList.declarations[0] === child) && + node.declarationList.declarations[0]; + } + + export function getNestedModuleDeclaration(node: Node): Node { + return node.kind === SyntaxKind.ModuleDeclaration && + (node as ModuleDeclaration).body && + (node as ModuleDeclaration).body.kind === SyntaxKind.ModuleDeclaration && + (node as ModuleDeclaration).body; + } + export function getJSDocCommentsAndTags(node: Node): (JSDoc | JSDocTag)[] { let result: (JSDoc | JSDocTag)[] | undefined; getJSDocCommentsAndTagsWorker(node); @@ -1551,35 +1579,15 @@ namespace ts { // * @returns {number} // */ // var x = function(name) { return name.length; } - const isInitializerOfVariableDeclarationInStatement = - isVariableLike(parent) && - parent.initializer === node && - parent.parent.parent.kind === SyntaxKind.VariableStatement; - const isVariableOfVariableDeclarationStatement = isVariableLike(node) && - parent.parent.kind === SyntaxKind.VariableStatement; - const variableStatementNode = - isInitializerOfVariableDeclarationInStatement ? parent.parent.parent : - isVariableOfVariableDeclarationStatement ? parent.parent : - undefined; - if (variableStatementNode) { - getJSDocCommentsAndTagsWorker(variableStatementNode); + if (parent && (parent.kind === SyntaxKind.PropertyAssignment || getNestedModuleDeclaration(parent))) { + getJSDocCommentsAndTagsWorker(parent); } - - // Also recognize when the node is the RHS of an assignment expression - const isSourceOfAssignmentExpressionStatement = - parent && parent.parent && - parent.kind === SyntaxKind.BinaryExpression && - (parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken && - parent.parent.kind === SyntaxKind.ExpressionStatement; - if (isSourceOfAssignmentExpressionStatement) { + if (parent && parent.parent && + (getSingleVariableOfVariableStatement(parent.parent, node) || getSourceOfAssignment(parent.parent))) { getJSDocCommentsAndTagsWorker(parent.parent); } - - const isModuleDeclaration = node.kind === SyntaxKind.ModuleDeclaration && - parent && parent.kind === SyntaxKind.ModuleDeclaration; - const isPropertyAssignmentExpression = parent && parent.kind === SyntaxKind.PropertyAssignment; - if (isModuleDeclaration || isPropertyAssignmentExpression) { - getJSDocCommentsAndTagsWorker(parent); + if (parent && parent.parent && parent.parent.parent && getSingleInitializerOfVariableStatement(parent.parent.parent, node)) { + getJSDocCommentsAndTagsWorker(parent.parent.parent); } // Pull parameter comments from declaring function as well @@ -1606,24 +1614,16 @@ namespace ts { return undefined; } const name = node.name.escapedText; - const decl = getJSDocHost(node); - let func: FunctionLike; - if (isExpressionStatement(decl) && isBinaryExpression(decl.expression) && isFunctionLike(decl.expression.right)) { - func = decl.expression.right; - } - else if (isVariableStatement(decl) && decl.declarationList.declarations.length === 1 && isVariableDeclaration(decl.declarationList.declarations[0]) - && isFunctionLike(decl.declarationList.declarations[0].initializer)) { - func = decl.declarationList.declarations[0].initializer as FunctionLike; - } - else if (isFunctionLike(decl)) { - func = decl; - } - else { - return undefined; + const host = getJSDocHost(node); + const decl = getSourceOfAssignment(host) || + getSingleInitializerOfVariableStatement(host) || + getSingleVariableOfVariableStatement(host) || + getNestedModuleDeclaration(host) || + host; + if (decl && isFunctionLike(decl)) { + const parameter = find(decl.parameters, p => p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name); + return parameter && parameter.symbol; } - const parameter = find(func.parameters, p => - p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name); - return parameter && parameter.symbol; } export function getJSDocHost(node: JSDocTag): HasJSDoc { From 8cc2af59b1d098ea00adc1a24bba2570e6cf838b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 19 Oct 2017 16:22:05 -0700 Subject: [PATCH 4/4] More tests for getParameterSymbolFromJSDoc --- ...kJsdocParamOnVariableDeclaredFunctionExpression.js | 9 +++++++++ ...cParamOnVariableDeclaredFunctionExpression.symbols | 9 +++++++++ ...docParamOnVariableDeclaredFunctionExpression.types | 11 +++++++++++ ...kJsdocParamOnVariableDeclaredFunctionExpression.ts | 5 +++++ 4 files changed, 34 insertions(+) diff --git a/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.js b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.js index 312e62b843567..59d2fe5b14b96 100644 --- a/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.js +++ b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.js @@ -10,6 +10,11 @@ var y; * @param {boolean!} b */ y = function bar(b) {} + +/** + * @param {string} s + */ +var one = function (s) { }, two = function (untyped) { }; //// [0.js] @@ -24,3 +29,7 @@ var y; * @param {boolean!} b */ y = function bar(b) { }; +/** + * @param {string} s + */ +var one = function (s) { }, two = function (untyped) { }; diff --git a/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.symbols b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.symbols index 26bf26e1a4a4b..82e92a5cea128 100644 --- a/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.symbols +++ b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.symbols @@ -21,3 +21,12 @@ y = function bar(b) {} >bar : Symbol(bar, Decl(0.js, 10, 3)) >b : Symbol(b, Decl(0.js, 10, 17)) +/** + * @param {string} s + */ +var one = function (s) { }, two = function (untyped) { }; +>one : Symbol(one, Decl(0.js, 15, 3)) +>s : Symbol(s, Decl(0.js, 15, 20)) +>two : Symbol(two, Decl(0.js, 15, 27)) +>untyped : Symbol(untyped, Decl(0.js, 15, 44)) + diff --git a/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.types b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.types index f9537219e20bf..fcb3c344ca92a 100644 --- a/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.types +++ b/tests/baselines/reference/checkJsdocParamOnVariableDeclaredFunctionExpression.types @@ -24,3 +24,14 @@ y = function bar(b) {} >bar : (b: boolean) => void >b : boolean +/** + * @param {string} s + */ +var one = function (s) { }, two = function (untyped) { }; +>one : (s: string) => void +>function (s) { } : (s: string) => void +>s : string +>two : (untyped: any) => void +>function (untyped) { } : (untyped: any) => void +>untyped : any + diff --git a/tests/cases/conformance/jsdoc/checkJsdocParamOnVariableDeclaredFunctionExpression.ts b/tests/cases/conformance/jsdoc/checkJsdocParamOnVariableDeclaredFunctionExpression.ts index 7443357337aa5..09ea30c0cc013 100644 --- a/tests/cases/conformance/jsdoc/checkJsdocParamOnVariableDeclaredFunctionExpression.ts +++ b/tests/cases/conformance/jsdoc/checkJsdocParamOnVariableDeclaredFunctionExpression.ts @@ -13,3 +13,8 @@ var y; * @param {boolean!} b */ y = function bar(b) {} + +/** + * @param {string} s + */ +var one = function (s) { }, two = function (untyped) { };