UNPKG

@abaplint/core

Version:
178 lines 7.94 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DefinitionsTop = exports.DefinitionsTopConf = void 0; const issue_1 = require("../issue"); const _statement_1 = require("../abap/2_statements/statements/_statement"); const Statements = require("../abap/2_statements/statements"); const Structures = require("../abap/3_structures/structures"); const _abap_rule_1 = require("./_abap_rule"); const _basic_rule_config_1 = require("./_basic_rule_config"); const _irule_1 = require("./_irule"); const edit_helper_1 = require("../edit_helper"); const nodes_1 = require("../abap/nodes"); class DefinitionsTopConf extends _basic_rule_config_1.BasicRuleConfig { } exports.DefinitionsTopConf = DefinitionsTopConf; // todo, use enum instead? // const ANY = 1; const DEFINITION = 2; const AFTER = 3; const IGNORE = 4; class DefinitionsTop extends _abap_rule_1.ABAPRule { constructor() { super(...arguments); this.conf = new DefinitionsTopConf(); } getMetadata() { return { key: "definitions_top", title: "Place definitions in top of routine", shortDescription: `Checks that definitions are placed at the beginning of METHODs, FORMs and FUNCTIONs.`, extendedInformation: `https://docs.abapopenchecks.org/checks/17/`, tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Quickfix], badExample: `FROM foo. WRITE 'hello'. DATA int TYPE i. ENDFORM.`, goodExample: `FROM foo. DATA int TYPE i. WRITE 'hello'. ENDFORM.`, }; } getMessage() { return "Reorder definitions to top of routine"; } getConfig() { return this.conf; } setConfig(conf) { this.conf = conf; } runParsed(file) { var _a; const issues = []; const structure = file.getStructure(); if (structure === undefined) { return []; } const containsUnknown = file.getStatements().some(s => s.get() instanceof _statement_1.Unknown); if (containsUnknown === true) { return []; } const routines = structure.findAllStructuresMulti([Structures.Form, Structures.Method, Structures.FunctionModule]); for (const r of routines) { // one fix per routine this.fixed = false; this.mode = DEFINITION; this.moveTo = (_a = r.getFirstStatement()) === null || _a === void 0 ? void 0 : _a.getLastToken().getEnd(); const found = this.walk(r, file); if (found) { issues.push(found); } } return issues; } ////////////////// walk(r, file) { var _a, _b, _c, _d, _e, _f; let previous = undefined; for (const c of r.getChildren()) { const get = c.get(); if (c instanceof nodes_1.StatementNode) { if (get instanceof _statement_1.Comment) { continue; } else if (get instanceof Statements.FunctionModule) { continue; } else if (get instanceof Statements.Form) { continue; } else if (get instanceof Statements.MethodImplementation) { continue; } } if (c instanceof nodes_1.StructureNode && (get instanceof Structures.Data || get instanceof Structures.Types || get instanceof Structures.Constants || get instanceof Structures.Statics)) { if (this.mode === AFTER) { // These are chained structured statements let fix = undefined; if (((_b = (_a = c.getLastChild()) === null || _a === void 0 ? void 0 : _a.getLastChild()) === null || _b === void 0 ? void 0 : _b.getFirstToken().getStr()) === "." && !(previous instanceof nodes_1.StructureNode) && this.moveTo) { // this is not perfect, but will work for now const start = (_d = (_c = c.getFirstChild()) === null || _c === void 0 ? void 0 : _c.getFirstChild()) === null || _d === void 0 ? void 0 : _d.getFirstToken().getStart(); const end = (_f = (_e = c.getLastChild()) === null || _e === void 0 ? void 0 : _e.getLastChild()) === null || _f === void 0 ? void 0 : _f.getLastToken().getEnd(); if (start && end) { let concat = c.concatTokens(); concat = concat.replace(/,/g, ".\n"); const fix1 = edit_helper_1.EditHelper.deleteRange(file, start, end); const fix2 = edit_helper_1.EditHelper.insertAt(file, this.moveTo, "\n" + concat); fix = edit_helper_1.EditHelper.merge(fix1, fix2); } } // no quick fixes for these, its difficult? return issue_1.Issue.atStatement(file, c.getFirstStatement(), this.getMessage(), this.getMetadata().key, this.conf.severity, fix); } else { this.moveTo = c.getLastToken().getEnd(); } } else if (c instanceof nodes_1.StatementNode && (get instanceof Statements.Data || get instanceof Statements.Type || get instanceof Statements.Constant || (get instanceof Statements.Move && c.concatTokens().toUpperCase().startsWith("DATA(")) || get instanceof Statements.Static || get instanceof Statements.FieldSymbol)) { if (this.mode === AFTER) { // only one fix per routine, as it reorders a lot if (!(get instanceof Statements.Move && c.concatTokens().toUpperCase().startsWith("DATA("))) { let fix = undefined; if (this.fixed === false && this.moveTo) { fix = this.buildFix(file, c, this.moveTo); this.fixed = true; } return issue_1.Issue.atStatement(file, c, this.getMessage(), this.getMetadata().key, this.conf.severity, fix); } } else { this.moveTo = c.getLastToken().getEnd(); } } else if (c instanceof nodes_1.StructureNode && get instanceof Structures.Define) { this.mode = IGNORE; return undefined; } else if (c instanceof nodes_1.StatementNode && get instanceof _statement_1.Unknown) { this.mode = IGNORE; return undefined; } else if (c instanceof nodes_1.StatementNode && this.mode === DEFINITION) { this.mode = AFTER; } else if (c instanceof nodes_1.StructureNode) { const found = this.walk(c, file); if (found) { return found; } } previous = c; } return undefined; } buildFix(file, statement, at) { let concat = statement.concatTokens(); concat = concat.replace(/,$/, "."); const fix1 = edit_helper_1.EditHelper.deleteStatement(file, statement); const indentation = " ".repeat(statement.getFirstToken().getCol() - 1); const fix2 = edit_helper_1.EditHelper.insertAt(file, at, "\n" + indentation + concat); return edit_helper_1.EditHelper.merge(fix1, fix2); } } exports.DefinitionsTop = DefinitionsTop; //# sourceMappingURL=definitions_top.js.map