All files / src/abap/5_syntax/structures cleanup.ts

97.1% Statements 67/69
96% Branches 24/25
100% Functions 3/3
97.1% Lines 67/69

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 701x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 17x     17x 17x 17x 17x 17x 1x 1x 78x 110x 62x 62x 62x 62x 62x 1x 1x 62x 110x 48x 48x 8x 8x 8x 48x 110x 78x 1x 1x 48x 48x 3x 48x 21x 45x 3x 24x 1x 21x 1x 1x 19x 19x 1x  
import * as Statements from "../../2_statements/statements";
import * as Structures from "../../3_structures/structures";
import {StatementNode, StructureNode} from "../../nodes";
import {IStructure} from "../../3_structures/structures/_structure";
import {SyntaxInput, syntaxIssue} from "../_syntax_input";
 
type StructureType = new () => IStructure;
 
const LOOPS: StructureType[] = [
  Structures.Loop,
  Structures.LoopAtScreen,
  Structures.LoopExtract,
  Structures.Do,
  Structures.While,
  Structures.Select,
  Structures.With,
  Structures.Provide,
];
 
// a CLEANUP block cannot contain statements that statically leave it: RETURN, and EXIT, CONTINUE or CHECK
// that do not belong to a loop inside the block, these end a loop outside the TRY or the processing block
export class Cleanup {
  public static runSyntax(node: StructureNode, input: SyntaxInput): void {
    if (input.scope.getOpenABAP()) {
      return;
    }
    const body = node.findDirectStructure(Structures.Body);
    if (body !== undefined) {
      this.walk(body, false, input);
    }
  }
 
  private static walk(node: StructureNode, inLoop: boolean, input: SyntaxInput): void {
    for (const child of node.getChildren()) {
      if (child instanceof StructureNode) {
        const stru = child.get();
        // a nested CLEANUP block is checked on its own, macro definitions and test injections do not run here
        if (stru instanceof Structures.Cleanup
            || stru instanceof Structures.Define
            || stru instanceof Structures.TestInjection) {
          continue;
        }
        this.walk(child, inLoop || LOOPS.some(l => stru instanceof l), input);
      } else if (child instanceof StatementNode) {
        const name = this.leavingStatement(child, inLoop);
        if (name !== undefined) {
          const message = `${name} cannot leave a CLEANUP block`;
          input.issues.push(syntaxIssue(input, child.getFirstToken(), message));
        }
      }
    }
  }
 
  private static leavingStatement(node: StatementNode, inLoop: boolean): string | undefined {
    const s = node.get();
    if (s instanceof Statements.Return) {
      return "RETURN";
    } else if (inLoop === true) {
      return undefined;
    } else if (s instanceof Statements.Exit && node.findDirectTokenByText("FROM") === undefined) {
      return "EXIT";
    } else if (s instanceof Statements.Continue) {
      return "CONTINUE";
    } else if (s instanceof Statements.Check) {
      return "CHECK";
    }
    return undefined;
  }
}