Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 11668x 11668x 11668x 11668x 11668x 11668x 11668x 11668x 11668x 34848x 34848x 34848x 34848x 34848x 34848x 34848x 34848x 34848x 34848x 34848x 34848x 34848x 34848x 34848x 34848x 11668x 11668x 12x 12x 11668x 11668x 11154x 11154x 11668x 11668x 239x 239x 11668x 11668x 277x 277x 277x 277x 13x 13x 264x 264x 277x 8x 8x 256x 256x 277x 82x 82x 82x 82x 82x 82x 82x 82x 12x 12x 82x 256x 256x 256x 11668x 11668x 11668x 11668x 223x 223x 223x 380x 380x 380x 228x 228x 1x 228x 24x 227x 57x 57x 228x 298x 298x 380x 152x 152x 380x 10x 3x 3x 3x 3x 3x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 3x 3x 10x 7x 7x 380x 288x 146x 146x 146x 146x 288x 35x 10x 10x 9x 9x 9x 9x 9x 9x 9x 35x 25x 25x 288x 1x 1x 253x 252x 81x 252x 141x 141x 31x 31x 141x 254x 254x 254x 179x 179x 179x 11668x 11668x 9x 9x 9x 9x 9x 9x 9x 9x 9x 11668x | import {Issue} from "../issue";
import {Comment, Unknown} from "../abap/2_statements/statements/_statement";
import * as Statements from "../abap/2_statements/statements";
import * as Structures from "../abap/3_structures/structures";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {IRuleMetadata, RuleTag} from "./_irule";
import {ABAPFile} from "../abap/abap_file";
import {EditHelper, IEdit} from "../edit_helper";
import {StructureNode, StatementNode} from "../abap/nodes";
import {Position} from "../position";
export class DefinitionsTopConf extends BasicRuleConfig {
}
// todo, use enum instead?
// const ANY = 1;
const DEFINITION = 2;
const AFTER = 3;
const IGNORE = 4;
export class DefinitionsTop extends ABAPRule {
private conf = new DefinitionsTopConf();
private mode: number;
private fixed: boolean;
private moveTo: Position | undefined;
public getMetadata(): IRuleMetadata {
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: [RuleTag.SingleFile, RuleTag.Quickfix],
badExample: `FORM foo.
WRITE 'hello'.
DATA int TYPE i.
ENDFORM.`,
goodExample: `FORM foo.
DATA int TYPE i.
WRITE 'hello'.
ENDFORM.`,
};
}
private getMessage(): string {
return "Reorder definitions to top of routine";
}
public getConfig() {
return this.conf;
}
public setConfig(conf: DefinitionsTopConf) {
this.conf = conf;
}
public runParsed(file: ABAPFile) {
const issues: Issue[] = [];
const structure = file.getStructure();
if (structure === undefined) {
return [];
}
const containsUnknown = file.getStatements().some(s => s.get() instanceof 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 = r.getFirstStatement()?.getLastToken().getEnd();
const found = this.walk(r, file);
if (found) {
issues.push(found);
}
}
return issues;
}
//////////////////
private walk(r: StructureNode, file: ABAPFile): Issue | undefined {
let previous: StatementNode | StructureNode | undefined = undefined;
for (const c of r.getChildren()) {
const get = c.get();
if (c instanceof StatementNode) {
if (get instanceof 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 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 (c.getLastChild()?.getLastChild()?.getFirstToken().getStr() === "."
&& !(previous instanceof StructureNode)
&& this.moveTo) {
// this is not perfect, but will work for now
const start = c.getFirstChild()?.getFirstChild()?.getFirstToken().getStart();
const end = c.getLastChild()?.getLastChild()?.getLastToken().getEnd();
if (start && end) {
let concat = c.concatTokens();
concat = concat.replace(/,/g, ".\n");
const fix1 = EditHelper.deleteRange(file, start, end);
const fix2 = EditHelper.insertAt(file, this.moveTo, "\n" + concat);
fix = EditHelper.merge(fix1, fix2);
}
}
// no quick fixes for these, its difficult?
return Issue.atStatement(file, c.getFirstStatement()!, this.getMessage(), this.getMetadata().key, this.conf.severity, fix);
} else {
this.moveTo = c.getLastToken().getEnd();
}
} else if (c instanceof 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.atStatement(file, c, this.getMessage(), this.getMetadata().key, this.conf.severity, fix);
}
} else {
this.moveTo = c.getLastToken().getEnd();
}
} else if (c instanceof StructureNode && get instanceof Structures.Define) {
this.mode = IGNORE;
return undefined;
} else if (c instanceof StatementNode && get instanceof Unknown) {
this.mode = IGNORE;
return undefined;
} else if (c instanceof StatementNode && this.mode === DEFINITION) {
this.mode = AFTER;
} else if (c instanceof StructureNode) {
const found = this.walk(c, file);
if (found) {
return found;
}
}
previous = c;
}
return undefined;
}
private buildFix(file: ABAPFile, statement: StatementNode, at: Position): IEdit {
let concat = statement.concatTokens();
concat = concat.replace(/,$/, ".");
const fix1 = EditHelper.deleteStatement(file, statement);
const indentation = " ".repeat(statement.getFirstToken().getCol() - 1);
const fix2 = EditHelper.insertAt(file, at, "\n" + indentation + concat);
return EditHelper.merge(fix1, fix2);
}
}
|