UNPKG

@maniascript/mslint

Version:
116 lines (115 loc) 4.05 kB
import {} from '../linter/rule.js'; import { BlockStatement, Kind, Node } from '@maniascript/parser'; const DEFAULT_MAXIMUM = 8; export const maxDepth = { meta: { id: 'max-depth', description: 'Prevent nested code blocks to exceed a maximum depth', recommended: true, settings: { maximum: DEFAULT_MAXIMUM } }, create(context) { const maximum = typeof context.settings['maximum'] === 'number' ? context.settings['maximum'] : DEFAULT_MAXIMUM; const stack = []; function canPushBlock(blockStatement) { return (blockStatement.parent !== undefined && (blockStatement.parent.kind !== Kind.Main && blockStatement.parent.kind !== Kind.FunctionDeclaration && blockStatement.parent.kind !== Kind.ForeachStatement && blockStatement.parent.kind !== Kind.ForStatement && blockStatement.parent.kind !== Kind.WhileStatement && blockStatement.parent.kind !== Kind.MeanwhileStatement && blockStatement.parent.kind !== Kind.SwitchCase && blockStatement.parent.kind !== Kind.SwitchtypeCase && blockStatement.parent.kind !== Kind.ConditionalBranch)); } function enterFunction() { stack.push(0); } function exitFunction() { stack.pop(); } function pushBlock(node) { const depth = ++stack[stack.length - 1]; if (depth > maximum) { context.report(node, `Blocks are nested too deeply. Depth ${depth.toString()} while maximum allowed is ${maximum.toString()}.`); } } function popBlock() { stack[stack.length - 1] -= 1; } return { 'Main:enter': () => { enterFunction(); }, 'Main:exit': () => { exitFunction(); }, 'FunctionDeclaration:enter': () => { enterFunction(); }, 'FunctionDeclaration:exit': () => { exitFunction(); }, 'LabelDeclaration:enter': () => { enterFunction(); }, 'LabelDeclaration:exit': () => { exitFunction(); }, 'ForeachStatement:enter': (node) => { pushBlock(node); }, 'ForeachStatement:exit': () => { popBlock(); }, 'ForStatement:enter': (node) => { pushBlock(node); }, 'ForStatement:exit': () => { popBlock(); }, 'WhileStatement:enter': (node) => { pushBlock(node); }, 'WhileStatement:exit': () => { popBlock(); }, 'MeanwhileStatement:enter': (node) => { pushBlock(node); }, 'MeanwhileStatement:exit': () => { popBlock(); }, 'SwitchStatement:enter': (node) => { pushBlock(node); }, 'SwitchStatement:exit': () => { popBlock(); }, 'SwitchtypeStatement:enter': (node) => { pushBlock(node); }, 'SwitchtypeStatement:exit': () => { popBlock(); }, 'ConditionalStatement:enter': (node) => { pushBlock(node); }, 'ConditionalStatement:exit': () => { popBlock(); }, 'BlockStatement:enter': (node) => { if (node instanceof BlockStatement && canPushBlock(node)) { pushBlock(node); } }, 'BlockStatement:exit': (node) => { if (node instanceof BlockStatement && canPushBlock(node)) { popBlock(); } } }; } };