@maniascript/mslint
Version:
ManiaScript linter
116 lines (115 loc) • 4.05 kB
JavaScript
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();
}
}
};
}
};