support arrow function expressions (#555)

This commit is contained in:
Jeff Williams 2015-08-20 11:01:08 -07:00
parent ebee2014ad
commit 5a58bdf5a5
8 changed files with 90 additions and 13 deletions

View File

@ -22,6 +22,7 @@ var acceptsLeadingComments = (function() {
// these nodes always accept leading comments
var commentable = [
Syntax.ArrowFunctionExpression,
Syntax.AssignmentExpression,
Syntax.CallExpression,
Syntax.ClassDeclaration,
@ -46,17 +47,20 @@ var acceptsLeadingComments = (function() {
// these nodes accept leading comments if they have specific types of parent nodes
// like: function foo(/** @type {string} */ bar) {}
accepts[Syntax.Identifier] = [
Syntax.ArrowFunctionExpression,
Syntax.CatchClause,
Syntax.FunctionDeclaration,
Syntax.FunctionExpression
];
// like: function foo(/** @type {string} */ bar='baz') {}
accepts[Syntax.AssignmentPattern] = [
Syntax.ArrowFunctionExpression,
Syntax.FunctionDeclaration,
Syntax.FunctionExpression
];
// like: function foo(/** @type {string} */ ...bar) {}
accepts[Syntax.RestElement] = [
Syntax.ArrowFunctionExpression,
Syntax.FunctionDeclaration,
Syntax.FunctionExpression
];

View File

@ -33,7 +33,7 @@ var isFunction = exports.isFunction = function(node) {
}
return type === Syntax.FunctionDeclaration || type === Syntax.FunctionExpression ||
type === Syntax.MethodDefinition;
type === Syntax.MethodDefinition || type === Syntax.ArrowFunctionExpression;
};
/**
@ -160,6 +160,9 @@ var nodeToValue = exports.nodeToValue = function(node) {
str = 'exports.' + nodeToValue(node.exported);
break;
case Syntax.ArrowFunctionExpression:
// falls through
case Syntax.FunctionDeclaration:
// falls through
@ -295,6 +298,14 @@ var getInfo = exports.getInfo = function(node) {
var info = {};
switch (node.type) {
// like the function in: "var foo = () => {}"
case Syntax.ArrowFunctionExpression:
info.node = node;
info.name = '';
info.type = info.node.type;
info.paramnames = getParamNames(node);
break;
// like: "foo = 'bar'" (after declaring foo)
// like: "MyClass.prototype.myMethod = function() {}" (after declaring MyClass)
case Syntax.AssignmentExpression:

View File

@ -282,8 +282,9 @@ Parser.prototype.addDocletRef = function(e) {
}
// keep references to undocumented anonymous functions, too, as they might have scoped vars
else if (
(node.type === Syntax.FunctionDeclaration || node.type === Syntax.FunctionExpression) &&
!this._getDocletById(node.nodeId) ) {
(node.type === Syntax.FunctionDeclaration || node.type === Syntax.FunctionExpression ||
node.type === Syntax.ArrowFunctionExpression) &&
!this._getDocletById(node.nodeId) ) {
fakeDoclet = {
longname: jsdoc.name.LONGNAMES.ANONYMOUS,
meta: {
@ -343,7 +344,8 @@ Parser.prototype.astnodeToMemberof = function(node) {
var type = node.type;
if ( (type === Syntax.FunctionDeclaration || type === Syntax.FunctionExpression ||
type === Syntax.VariableDeclarator) && node.enclosingScope ) {
type === Syntax.ArrowFunctionExpression || type === Syntax.VariableDeclarator) &&
node.enclosingScope ) {
doclet = this._getDocletById(node.enclosingScope.nodeId);
if (!doclet) {

View File

@ -566,6 +566,10 @@ Visitor.prototype.makeSymbolFoundEvent = function(node, parser, filename) {
break;
// like: var foo = () => {};
case Syntax.ArrowFunctionExpression:
// falls through
// like: function foo() {}
case Syntax.FunctionDeclaration:
// falls through
@ -671,13 +675,6 @@ Visitor.prototype.makeSymbolFoundEvent = function(node, parser, filename) {
break;
// log a warning for ES 2015 nodes that are not currently handled
case Syntax.ArrowFunctionExpression:
logger.warn('JSDoc does not currently handle %s nodes. Source file: %s, line %s',
node.type, filename, (node.loc && node.loc.start) ? node.loc.start.line : '??');
break;
default:
// ignore
}

View File

@ -21,7 +21,8 @@ var Syntax = require('jsdoc/src/syntax').Syntax;
function isScopeNode(node) {
// TODO: handle blocks with "let" declarations
return node && typeof node === 'object' && (node.type === Syntax.CatchClause ||
node.type === Syntax.FunctionDeclaration || node.type === Syntax.FunctionExpression);
node.type === Syntax.FunctionDeclaration || node.type === Syntax.FunctionExpression ||
node.type === Syntax.ArrowFunctionExpression);
}
// TODO: docs
@ -66,7 +67,6 @@ walkers[Syntax.ArrowFunctionExpression] = function(node, parent, state, cb) {
var i;
var l;
// used for function declarations, so we include it here
if (node.id) {
cb(node.id, node, state);
}

11
test/fixtures/arrowfunction.js vendored Normal file
View File

@ -0,0 +1,11 @@
/**
* Increment a number by 1.
*
* @param {number} n - The number to increment.
*/
var increment = n => n + 1;
/**
* Print a value to the console.
*/
var print = (/** @type {*} */ val) => console.log(val);

View File

@ -0,0 +1,24 @@
'use strict';
if (jasmine.jsParser !== 'rhino') {
describe('arrow functions', function() {
var docSet = jasmine.getDocSetFromFile('test/fixtures/arrowfunction.js');
var increment = docSet.getByLongname('increment')[0];
var print = docSet.getByLongname('print')[0];
it('should use the correct name and longname', function() {
expect(increment).toBeDefined();
expect(increment.name).toBe('increment');
});
it('should allow function parameters to be documented', function() {
expect(increment.params.length).toBe(1);
expect(increment.params[0].name).toBe('n');
});
it('should support inline comments on parameters', function() {
expect(print.params.length).toBe(1);
expect(print.params[0].type.names[0]).toBe('*');
});
});
}

View File

@ -14,6 +14,7 @@ describe('jsdoc/src/astNode', function() {
// create the AST nodes we'll be testing
var arrayExpression = parse('[,]').body[0].expression;
var arrowFunctionExpression = parse('var foo = () => {};').body[0].declarations[0].init;
var assignmentExpression = parse('foo = 1;').body[0].expression;
var binaryExpression = parse('foo & foo;').body[0].expression;
var functionDeclaration1 = parse('function foo() {}').body[0];
@ -28,6 +29,7 @@ describe('jsdoc/src/astNode', function() {
var memberExpression = parse('foo.bar;').body[0].expression;
var memberExpressionComputed1 = parse('foo["bar"];').body[0].expression;
var memberExpressionComputed2 = parse('foo[\'bar\'];').body[0].expression;
var methodDefinition = parse('class Foo { bar() {} }').body[0].body.body[0];
var propertyGet = parse('var foo = { get bar() {} };').body[0].declarations[0].init
.properties[0];
var propertyInit = parse('var foo = { bar: {} };').body[0].declarations[0].init.properties[0];
@ -65,6 +67,10 @@ describe('jsdoc/src/astNode', function() {
expect(typeof astNode.isAssignment).toBe('function');
});
it('should export an isFunction method', function() {
expect(typeof astNode.isFunction).toBe('function');
});
it('should export an isScope method', function() {
expect(typeof astNode.isScope).toBe('function');
});
@ -489,6 +495,28 @@ describe('jsdoc/src/astNode', function() {
});
});
describe('isFunction', function() {
it('should recognize function declarations as functions', function() {
expect( astNode.isFunction(functionDeclaration1) ).toBe(true);
});
it('should recognize function expressions as functions', function() {
expect( astNode.isFunction(functionExpression1) ).toBe(true);
});
it('should recognize method definitions as functions', function() {
expect( astNode.isFunction(methodDefinition) ).toBe(true);
});
it('should recognize arrow function expressions as functions', function() {
expect( astNode.isFunction(arrowFunctionExpression) ).toBe(true);
});
it('should recognize non-functions', function() {
expect( astNode.isFunction(arrayExpression) ).toBe(false);
});
});
describe('isScope', function() {
it('should return false for undefined values', function() {
expect( astNode.isScope() ).toBe(false);