Skip to content
This repository has been archived by the owner on Mar 23, 2024. It is now read-only.

Commit

Permalink
New rule: requireMatchingFunctionName
Browse files Browse the repository at this point in the history
Requires function names to match member and property names

Example of member name mismatch:

```js
var test = {};
test.foo = function bar() {};
```

Example of property name mismatch:

```js
var test = {foo: function bar() {}};
```

Fixes #846
Closes #850
  • Loading branch information
xaka authored and markelog committed Jun 12, 2015
1 parent 059d53a commit bbd7337
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/config/configuration.js
Expand Up @@ -742,6 +742,8 @@ Configuration.prototype.registerDefaultRules = function() {
this.registerRule(require('../rules/validate-newline-after-array-elements'));

this.registerRule(require('../rules/disallow-not-operators-in-conditionals'));

this.registerRule(require('../rules/require-matching-function-name'));
};

/**
Expand Down
131 changes: 131 additions & 0 deletions lib/rules/require-matching-function-name.js
@@ -0,0 +1,131 @@
/**
* Requires function names to match member and property names.
*
* It doesn't affect anonymous functions nor functions assigned to members or
* properties named with a reserved word.
*
* Type: `Boolean`
*
* Value: `true`
*
* #### Example
*
* ```js
* "requireMatchingFunctionName": true
* ```
*
* ##### Valid
*
* ```js
* var test = {};
* test.foo = function foo() {};
* ```
*
* ```js
* var test = {};
* test['foo'] = function foo() {};
* ```
*
* ```js
* var test = {foo: function foo() {}};
* ```
*
* ##### Invalid
*
* ```js
* var test = {};
* test.foo = function bar() {};
* ```
*
* ```js
* var test = {};
* test['foo'] = function bar() {};
* ```
*
* ```js
* var test = {foo: function bar() {}};
* ```
*/

var assert = require('assert');
var utils = require('../../lib/utils');

module.exports = function() {};

module.exports.prototype = {
configure: function(requireMatchingFunctionName) {
assert(
requireMatchingFunctionName === true,
'requireMatchingFunctionName option requires true value or should be removed'
);
},

getOptionName: function() {
return 'requireMatchingFunctionName';
},

check: function(file, errors) {
file.iterateNodesByType(['FunctionExpression'], function(node) {
switch (node.parentNode.type) {
// object.foo = function bar() {}
// object['foo'] = function bar() {}
case 'AssignmentExpression':
checkForMember(node.parentNode, errors);
break;

// object = {foo: function bar() {}}
case 'Property':
checkForProperty(node.parentNode, errors);
break;
}
});
}
};

function checkForMember(assignment, errors) {
// We don't care about anonymous functions as
// those should be enforced by separate rule
if (isAnonymousFunction(assignment.right)) {
return;
}

// Relax a bit when reserved word is detected
if (isReservedName(assignment.left.property.name)) {
return;
}

if (assignment.left.property.name !== assignment.right.id.name) {
errors.add(
'Function name does not match member name',
assignment.loc.start
);
}
}

function checkForProperty(property, errors) {
// We don't care about anonymous functions as
// those should be enforced by separate rule
if (isAnonymousFunction(property.value)) {
return;
}

// Relax a bit when reserved word is detected
if (isReservedName(property.key.name)) {
return;
}

if (property.key.name !== property.value.id.name) {
errors.add(
'Function name does not match property name',
property.loc.start
);
}
}

function isAnonymousFunction(node) {
return !node.id;
}

function isReservedName(name) {
return utils.isEs3Keyword(name) || utils.isEs3FutureReservedWord(name);
}
72 changes: 72 additions & 0 deletions test/rules/require-matching-function-name.js
@@ -0,0 +1,72 @@
var Checker = require('../../lib/checker');
var assert = require('assert');

describe('rules/require-matching-function-name', function() {
var checker;

beforeEach(function() {
checker = new Checker();
checker.registerDefaultRules();
});

describe('option value true', function() {
beforeEach(function() {
checker.configure({ requireMatchingFunctionName: true });
});

// TODO:
// - assigning function to variable (both names should match)
// - assigning function declaration to member
// - assigning function declaration to property
// For both cases we should track identifier back, check whether
// it's a function and if so, compare the names

it('should report function name mismatch when assigning to member', function() {
assertErrorForMemberNameMismatch('var test = {}; test.foo = function bar() {};');
});

it('should report function name mismatch when assigning to member via ["..."]', function() {
assertErrorForMemberNameMismatch('var test = {}; test["foo"] = function bar() {};');
});

it('should NOT report function name mismatch when assigning anonymous to member', function() {
assertNoErrors('var test = {}; test.foo = function() {};');
});

it('should NOT report function name mismatch when member name is reserved word', function() {
assertNoErrors('var test = {}; test.delete = function $delete() {};');
});

it('should report function name mismatch when assigning to property', function() {
assertErrorForPropertyNameMismatch('var test = {foo: function bar() {}};');
});

it('should NOT report function name mismatch when assigning anonymous to property', function() {
assertNoErrors('var test = {foo: function() {}};');
});

it('should NOT report function name mismatch when property name is reserved word', function() {
assertNoErrors('var test = {delete: function $delete() {}};');
});

function assertErrorForMemberNameMismatch(js) {
assertError(js, 'Function name does not match member name');
}

function assertErrorForPropertyNameMismatch(js) {
assertError(js, 'Function name does not match property name');
}

function assertError(js, message) {
var errors = checker.checkString(js).getErrorList();
assert(errors.length);
assert.equal(errors[0].rule, 'requireMatchingFunctionName');
assert.equal(errors[0].message, message);
}

function assertNoErrors(js) {
var errors = checker.checkString(js).getErrorList();
assert.equal(errors.length, 0);
}
});
});

0 comments on commit bbd7337

Please sign in to comment.