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 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 24226x 1x 12114x 12114x 12114x 12114x 12114x 82505x 82505x 82505x 82505x 82505x 82505x 82505x 82505x 82505x 82505x 82505x 82505x 82505x 82505x 82505x 82505x 12114x 12114x 26x 26x 12114x 12114x 14411x 14411x 12114x 12114x 256x 256x 12114x 12114x 304x 304x 304x 304x 12x 12x 292x 292x 304x 1681x 8x 8x 1681x 284x 284x 284x 284x 284x 284x 284x 284x 284x 284x 284x 284x 284x 284x 284x 284x 284x 284x 284x 284x 304x 11x 5x 5x 5x 5x 11x 284x 284x 284x 284x 13x 13x 1x 1x 1x 1x 1x 1x 1x 1x 1x 13x 284x 284x 284x 284x 284x 3x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 3x 284x 284x 284x 284x 284x 284x 284x 13x 13x 7x 7x 7x 7x 7x 7x 7x 7x 7x 13x 284x 284x 304x 10x 11x 11x 3x 3x 8x 11x 2x 2x 6x 6x 6x 6x 10x 284x 284x 284x 284x 284x 9x 6x 6x 6x 6x 6x 9x 284x 284x 284x 284x 12114x 12114x 8x 1x 1x 7x 8x 8x 8x 8x 12114x 12114x | import {Issue} from "../issue";
import * as Structures from "../abap/3_structures/structures";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {StructureNode} from "../abap/nodes";
import {IRuleMetadata, RuleTag} from "./_irule";
import {ABAPFile} from "../abap/abap_file";
import {Comment, Unknown} from "../abap/2_statements/statements/_statement";
import {StatementNode} from "../abap/nodes/statement_node";
export class EmptyStructureConf extends BasicRuleConfig {
/** Checks for empty LOOP blocks */
public loop: boolean = true;
/** Allow empty LOOP if subrc is checked after the loop */
public loopAllowIfSubrc: boolean = true;
/** Checks for empty IF blocks */
public if: boolean = true;
/** Checks for empty WHILE blocks */
public while: boolean = true;
/** Checks for empty CASE blocks */
public case: boolean = true;
/** Checks for empty SELECT blockss */
public select: boolean = true;
/** Checks for empty DO blocks */
public do: boolean = true;
/** Checks for empty AT blocks */
public at: boolean = true;
/** Checks for empty TRY blocks */
public try: boolean = true;
/** Checks for empty WHEN blocks */
public when: boolean = true;
/** Checks for empty CATCH blocks, unless the CATCH has pragma ##NO_HANDLER or pseudo comment "#EC NO_HANDLER */
public catch: boolean = false;
// todo, other category containing ELSE
}
export class EmptyStructure extends ABAPRule {
private conf = new EmptyStructureConf();
public getMetadata(): IRuleMetadata {
return {
key: "empty_structure",
title: "Find empty blocks",
shortDescription: `Checks that the code does not contain empty blocks.`,
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#no-empty-if-branches`,
tags: [RuleTag.Styleguide, RuleTag.SingleFile],
badExample: `IF foo = bar.
ENDIF.
DO 2 TIMES.
ENDDO.`,
goodExample: `LOOP AT itab WHERE qty = 0 OR date > sy-datum.
ENDLOOP.
result = xsdbool( sy-subrc = 0 ).`,
};
}
private getDescription(name: string): string {
return "Empty block, add code: " + name;
}
public getConfig() {
return this.conf;
}
public setConfig(conf: EmptyStructureConf) {
this.conf = conf;
}
public runParsed(file: ABAPFile) {
const issues: Issue[] = [];
const stru = file.getStructure();
if (stru === undefined) {
return [];
}
const statements = file.getStatements();
for (const statement of statements) {
if (statement.get() instanceof Unknown) {
return []; // contains parser errors
}
}
const candidates: StructureNode[] = [];
if (this.getConfig().while === true) {
candidates.push(...stru.findAllStructuresRecursive(Structures.While));
}
if (this.getConfig().case === true) {
candidates.push(...stru.findAllStructuresRecursive(Structures.Case));
}
if (this.getConfig().select === true) {
candidates.push(...stru.findAllStructuresRecursive(Structures.Select));
}
if (this.getConfig().do === true) {
candidates.push(...stru.findAllStructuresRecursive(Structures.Do));
}
if (this.getConfig().at === true) {
candidates.push(...stru.findAllStructuresRecursive(Structures.At));
candidates.push(...stru.findAllStructuresRecursive(Structures.AtFirst));
candidates.push(...stru.findAllStructuresRecursive(Structures.AtLast));
}
for (const l of candidates) {
if (l.getChildren().length === 2) {
const token = l.getFirstToken();
const issue = Issue.atToken(file, token, this.getDescription(l.get().constructor.name), this.getMetadata().key, this.conf.severity);
issues.push(issue);
}
}
if (this.getConfig().try === true) {
const tries = stru.findAllStructuresRecursive(Structures.Try);
for (const t of tries) {
const normal = t.findDirectStructure(Structures.Body);
if (normal === undefined) {
const token = t.getFirstToken();
const issue = Issue.atToken(
file,
token,
this.getDescription(t.get().constructor.name),
this.getMetadata().key,
this.conf.severity);
issues.push(issue);
}
}
}
if (this.getConfig().loop === true) {
const loops = stru.findAllStructuresRecursive(Structures.Loop);
for (const loop of loops) {
if (loop.getChildren().length === 2) {
const endloopStatement = loop.getLastChild();
const endloopIndex = statements.findIndex((s) => s === endloopStatement);
const afterEndloop = statements[endloopIndex + 1];
if (afterEndloop !== undefined && afterEndloop.concatTokens().toUpperCase().includes("SY-SUBRC")) {
continue;
}
const token = loop.getFirstToken();
const issue = Issue.atToken(
file, token, this.getDescription(loop.get().constructor.name), this.getMetadata().key, this.conf.severity);
issues.push(issue);
}
}
}
if (this.getConfig().if === true) {
const tries = stru.findAllStructuresRecursive(Structures.If)
.concat(stru.findAllStructuresRecursive(Structures.Else))
.concat(stru.findAllStructuresRecursive(Structures.ElseIf));
for (const t of tries) {
const normal = t.findDirectStructure(Structures.Body);
if (normal === undefined) {
const token = t.getFirstToken();
const issue = Issue.atToken(
file,
token,
this.getDescription(t.get().constructor.name),
this.getMetadata().key,
this.conf.severity);
issues.push(issue);
}
}
}
if (this.getConfig().catch === true) {
for (const c of stru.findAllStructuresRecursive(Structures.Catch)) {
// a comment in the block does not count, the extended check (SLIN) reports it too
if (c.findDirectStructure(Structures.Body) !== undefined) {
continue;
}
const catchStatement = c.getFirstStatement();
if (catchStatement === undefined || this.isNoHandler(catchStatement, statements)) {
continue;
}
const token = c.getFirstToken();
const issue = Issue.atToken(file, token, this.getDescription(c.get().constructor.name), this.getMetadata().key, this.conf.severity);
issues.push(issue);
}
}
if (this.getConfig().when === true) {
const tries = stru.findAllStructuresRecursive(Structures.When);
for (const t of tries) {
if (t.getChildren().length === 1) {
const token = t.getFirstToken();
const message = this.getDescription(t.get().constructor.name);
const issue = Issue.atToken(file, token, message, this.getMetadata().key, this.conf.severity);
issues.push(issue);
}
}
}
return issues;
}
private isNoHandler(catchStatement: StatementNode, statements: readonly StatementNode[]): boolean {
if (catchStatement.getPragmas().some(p => p.getStr().toUpperCase() === "##NO_HANDLER")) {
return true;
}
const next = statements[statements.indexOf(catchStatement) + 1];
return next?.get() instanceof Comment
&& next.getStart().getRow() === catchStatement.getEnd().getRow()
&& next.concatTokens().toUpperCase().replace(/\s/g, "").includes("\"#ECNO_HANDLER");
}
} |