Skip to content

fix getParameterSymbolFromJSDoc #19355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 43 additions & 32 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,34 @@ namespace ts {
return getJSDocCommentsAndTags(node);
}

export function getSourceOfAssignment(node: Node): Node {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to export.

Please annotate potentially undefined return values (although as written this is potentially false).
I would prefer multiple statements with local variables instead of writing it all as one expression that has to be formatted on different lines anyway.

if (isExpressionStatement(node)) {
    const { expression } = node;
    if (isBinaryExpression(expression)) {
        const { operatorToken, right } = node;
        return operatorToken.kind === SyntaxKind.EqualsToken ? right : undefined;
    }
}

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);
Expand All @@ -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
Expand All @@ -1606,13 +1614,16 @@ namespace ts {
return undefined;
}
const name = node.name.escapedText;
const func = getJSDocHost(node);
if (!isFunctionLike(func)) {
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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//// [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) {}

/**
* @param {string} s
*/
var one = function (s) { }, two = function (untyped) { };


//// [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) { };
/**
* @param {string} s
*/
var one = function (s) { }, two = function (untyped) { };
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
=== 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))

/**
* @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))

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
=== 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

/**
* @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

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// @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) {}

/**
* @param {string} s
*/
var one = function (s) { }, two = function (untyped) { };