123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- 'use strict'
- const getDocsUrl = require('./lib/get-docs-url')
- function isFunctionWithBlockStatement(node) {
- if (node.type === 'FunctionExpression') {
- return true
- }
- if (node.type === 'ArrowFunctionExpression') {
- return node.body.type === 'BlockStatement'
- }
- return false
- }
- function isThenCallExpression(node) {
- return (
- node.type === 'CallExpression' &&
- node.callee.type === 'MemberExpression' &&
- node.callee.property.name === 'then'
- )
- }
- function isFirstArgument(node) {
- return (
- node.parent && node.parent.arguments && node.parent.arguments[0] === node
- )
- }
- function isInlineThenFunctionExpression(node) {
- return (
- isFunctionWithBlockStatement(node) &&
- isThenCallExpression(node.parent) &&
- isFirstArgument(node)
- )
- }
- function hasParentReturnStatement(node) {
- // istanbul ignore else -- not reachable given not checking `Program`
- if (node && node.parent && node.parent.type) {
- // if the parent is a then, and we haven't returned anything, fail
- if (isThenCallExpression(node.parent)) {
- return false
- }
- if (node.parent.type === 'ReturnStatement') {
- return true
- }
- return hasParentReturnStatement(node.parent)
- }
- // istanbul ignore next -- not reachable given not checking `Program`
- return false
- }
- function peek(arr) {
- return arr[arr.length - 1]
- }
- module.exports = {
- meta: {
- type: 'problem',
- docs: {
- url: getDocsUrl('always-return'),
- },
- },
- create(context) {
- // funcInfoStack is a stack representing the stack of currently executing
- // functions
- // funcInfoStack[i].branchIDStack is a stack representing the currently
- // executing branches ("codePathSegment"s) within the given function
- // funcInfoStack[i].branchInfoMap is an object representing information
- // about all branches within the given function
- // funcInfoStack[i].branchInfoMap[j].good is a boolean representing whether
- // the given branch explicitly `return`s or `throw`s. It starts as `false`
- // for every branch and is updated to `true` if a `return` or `throw`
- // statement is found
- // funcInfoStack[i].branchInfoMap[j].loc is a eslint SourceLocation object
- // for the given branch
- // example:
- // funcInfoStack = [ { branchIDStack: [ 's1_1' ],
- // branchInfoMap:
- // { s1_1:
- // { good: false,
- // loc: <loc> } } },
- // { branchIDStack: ['s2_1', 's2_4'],
- // branchInfoMap:
- // { s2_1:
- // { good: false,
- // loc: <loc> },
- // s2_2:
- // { good: true,
- // loc: <loc> },
- // s2_4:
- // { good: false,
- // loc: <loc> } } } ]
- const funcInfoStack = []
- function markCurrentBranchAsGood() {
- const funcInfo = peek(funcInfoStack)
- const currentBranchID = peek(funcInfo.branchIDStack)
- if (funcInfo.branchInfoMap[currentBranchID]) {
- funcInfo.branchInfoMap[currentBranchID].good = true
- }
- // else unreachable code
- }
- return {
- ReturnStatement: markCurrentBranchAsGood,
- ThrowStatement: markCurrentBranchAsGood,
- onCodePathSegmentStart(segment, node) {
- const funcInfo = peek(funcInfoStack)
- funcInfo.branchIDStack.push(segment.id)
- funcInfo.branchInfoMap[segment.id] = { good: false, node }
- },
- onCodePathSegmentEnd() {
- const funcInfo = peek(funcInfoStack)
- funcInfo.branchIDStack.pop()
- },
- onCodePathStart() {
- funcInfoStack.push({
- branchIDStack: [],
- branchInfoMap: {},
- })
- },
- onCodePathEnd(path, node) {
- const funcInfo = funcInfoStack.pop()
- if (!isInlineThenFunctionExpression(node)) {
- return
- }
- path.finalSegments.forEach((segment) => {
- const id = segment.id
- const branch = funcInfo.branchInfoMap[id]
- if (!branch.good) {
- if (hasParentReturnStatement(branch.node)) {
- return
- }
- context.report({
- message: 'Each then() should return a value or throw',
- node: branch.node,
- })
- }
- })
- },
- }
- },
- }
|