@abaplint/core
Version:
abaplint - Core API
178 lines • 7.94 kB
JavaScript
"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