1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
|
/**
* @fileoverview Enforces always return from a fixer function
* @author 薛定谔的猫<hh_2013@foxmail.com>
*/
'use strict';
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const utils = require('../utils');
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: 'require fixer function to always return a value.',
category: 'Possible Errors',
recommended: true,
},
type: 'problem',
fixable: null,
schema: [],
},
create (context) {
const message = 'Expected fixer function to always return a value.';
let funcInfo = {
upper: null,
codePath: null,
hasReturn: false,
hasYield: false,
shouldCheck: false,
node: null,
};
let contextIdentifiers;
/**
* Checks whether or not the last code path segment is reachable.
* Then reports this function if the segment is reachable.
*
* If the last code path segment is reachable, there are paths which are not
* returned or thrown.
*
* @param {ASTNode} node - A node to check.
* @returns {void}
*/
function checkLastSegment (node) {
if (
funcInfo.shouldCheck &&
funcInfo.codePath.currentSegments.some(segment => segment.reachable) &&
(!node.generator || !funcInfo.hasYield)
) {
context.report({
node,
loc: (node.id || node).loc.start,
message,
});
}
}
return {
Program (node) {
contextIdentifiers = utils.getContextIdentifiers(context, node);
},
// Stacks this function's information.
onCodePathStart (codePath, node) {
const parent = node.parent;
const shouldCheck = node.type === 'FunctionExpression' &&
parent.parent.type === 'ObjectExpression' &&
parent.parent.parent.type === 'CallExpression' &&
contextIdentifiers.has(parent.parent.parent.callee.object) &&
parent.parent.parent.callee.property.name === 'report' &&
utils.getReportInfo(parent.parent.parent.arguments).fix === node;
funcInfo = {
upper: funcInfo,
codePath,
hasYield: false,
hasReturn: false,
shouldCheck,
node,
};
},
// Pops this function's information.
onCodePathEnd () {
funcInfo = funcInfo.upper;
},
// Yield in generators
YieldExpression () {
if (funcInfo.shouldCheck) {
funcInfo.hasYield = true;
}
},
// Checks the return statement is valid.
ReturnStatement (node) {
if (funcInfo.shouldCheck) {
funcInfo.hasReturn = true;
if (!node.argument) {
context.report({
node,
message,
});
}
}
},
// Reports a given function if the last path is reachable.
'FunctionExpression:exit': checkLastSegment,
};
},
};
|