All files / src/abap/5_syntax/statements loop.ts

89.5% Statements 145/162
85.71% Branches 66/77
100% Functions 1/1
89.5% Lines 145/162

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 1621x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 130x 130x 130x 130x 130x 52x 52x 130x 130x 130x 130x 130x 130x     130x 130x 130x 130x 130x           130x 1x 1x 1x 130x 129x 129x 129x 1x 1x 129x 128x 128x 128x 128x     128x 128x 128x 1x 1x 1x 130x 130x 130x 130x 130x 130x 130x 130x 1x 1x 1x 1x 129x 130x 111x 111x 111x 18x 18x 111x 129x 129x 130x 13x 13x 129x 129x 130x 130x 1x 1x 1x 1x 128x 128x 130x 130x 2x 2x 2x 2x 2x 2x 2x 2x         2x 2x 2x 2x 3x 1x 1x 1x 1x 3x 1x 2x 2x 127x 130x 130x 62x 62x 127x 130x 6x     6x 6x 127x 130x 130x 1x 130x 126x 126x 40x 40x 126x 127x 130x     127x 127x 130x 21x 21x 130x 130x 1x
import * as Expressions from "../../2_statements/expressions";
import {ExpressionNode, StatementNode} from "../../nodes";
import {VoidType, TableType, UnknownType, DataReference, AnyType, DataType, ITableKey, TableAccessType} from "../../types/basic";
import {Target} from "../expressions/target";
import {Source} from "../expressions/source";
import {InlineData} from "../expressions/inline_data";
import {InlineFS} from "../expressions/inline_fs";
import {FSTarget} from "../expressions/fstarget";
import {ComponentCond} from "../expressions/component_cond";
import {Dynamic} from "../expressions/dynamic";
import {StatementSyntax} from "../_statement_syntax";
import {LoopGroupBy} from "../expressions/loop_group_by";
import {AbstractType} from "../../types/basic/_abstract_type";
import {SyntaxInput, syntaxIssue} from "../_syntax_input";
import {Release, releaseAtLeast} from "../../../version";
 
export class Loop implements StatementSyntax {
  public runSyntax(node: StatementNode, input: SyntaxInput): void {
    const loopTarget = node.findDirectExpression(Expressions.LoopTarget);
 
    let target = loopTarget?.findDirectExpression(Expressions.Target);
    const targetType = target ? Target.runSyntax(target, input) : undefined;
    if (target === undefined) {
      target = node.findDirectExpression(Expressions.FSTarget);
    }
 
    const write = loopTarget?.findDirectTokenByText("ASSIGNING") !== undefined;
 
    const sources = node.findDirectExpressions(Expressions.Source);
    let firstSource = node.findDirectExpression(Expressions.LoopSource)?.getFirstChild() as ExpressionNode | undefined;
    if (firstSource === undefined) {
      firstSource = sources[0];
    }
    let sourceType = firstSource ? Source.runSyntax(firstSource, input, targetType, write) : undefined;
    let rowType: AbstractType | undefined = undefined;
 
    const concat = node.concatTokens().toUpperCase();
    if (sourceType === undefined) {
      // if its a dynpro table control loop, then dont issue error
      if (concat !== "LOOP.") {
        const message = "No source type determined";
        input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
      }
    } else if (sourceType instanceof UnknownType) {
      const message = "Loop, not a table type, " + sourceType.getError();
      input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
      sourceType = VoidType.get("Loop, not a table type");
    } else if (sourceType instanceof TableType
        && target === undefined
        && sourceType.isWithHeader() === false
        && node.getChildren().length === 4) {
      const message = "Loop, no header line";
      input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
    } else if (!(sourceType instanceof TableType)
        && !(sourceType instanceof AnyType)
        && !(sourceType instanceof DataType)
        && !(sourceType instanceof VoidType)
        && concat.startsWith("LOOP AT GROUP ") === false) {
      const message = "Loop, not a table type";
      input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
    } else if (loopTarget === undefined
        && sourceType instanceof TableType
        && sourceType.isWithHeader() === false) {
      const message = "Loop, no header";
      input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
    }
 
    const targetConcat = loopTarget?.concatTokens().toUpperCase();
    const topType = sourceType; // todo: refactor topType vs sourceType vs rowType
 
    if (topType instanceof TableType
        && topType.getAccessType() === TableAccessType.hashed
        && node.findDirectTokenByText("USING") === undefined
        && (node.findDirectTokenByText("FROM") || node.findDirectTokenByText("TO"))) {
      const message = "Implicit or explicit index operation on hashed table is not possible";
      input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
      return;
    }
 
    if (sourceType instanceof TableType) {
      rowType = sourceType.getRowType();
      sourceType = rowType;
      if (targetConcat?.startsWith("REFERENCE INTO ")) {
        sourceType = new DataReference(sourceType);
      }
    }
 
    const cond = node.findDirectExpression(Expressions.ComponentCond);
    if (cond !== undefined) {
      ComponentCond.runSyntax(cond, input, rowType);
    }
 
    if (targetConcat
        && targetConcat.startsWith("TRANSPORTING ")
        && node.findDirectTokenByText("WHERE") === undefined) {
      const message = "Loop, TRANSPORTING NO FIELDS only with WHERE";
      input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
      return;
    }
 
    if (node.findDirectTokenByText("USING") !== undefined
        && cond !== undefined
        && topType instanceof TableType) {
 
      // https://github.com/abap2xlsx/abap2xlsx/issues/1341
      const keyName = node.findExpressionAfterToken("KEY");
      let key: ITableKey | undefined = undefined;
      if (keyName?.get() instanceof Expressions.SimpleName) {
        // it might be dynamic, in that case we cannot check anything
        key = topType.getOptions().secondary?.find(k => k.name.toUpperCase() === keyName.getFirstToken().getStr().toUpperCase());
        if (key === undefined) {
          const message = "Key " + keyName?.concatTokens() + " not found in table type";
          input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
          return;
        }
 
        if (!releaseAtLeast(input.scope.getRelease(), Release.v740sp02)) {
          const compares = cond.findAllExpressionsRecursive(Expressions.ComponentCompare).map(c => c.concatTokens().toUpperCase());
          for (const keyField of key.keyFields) {
            if (compares.find(c => c === keyField.toUpperCase() + " IS INITIAL") !== undefined) {
              const message = "Loop, key check with IS INITIAL cannot optimized before 7.40 SP02";
              input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
              return;
            }
          }
        }
      }
    }
 
    const inline = target?.findDirectExpression(Expressions.InlineData);
    if (inline) {
      InlineData.runSyntax(inline, input, sourceType);
    }
 
    for (const s of sources) {
      if (s === firstSource) {
        continue;
      }
      Source.runSyntax(s, input);
    }
 
    const inlinefs = target?.findDirectExpression(Expressions.InlineFS);
    if (inlinefs) {
      InlineFS.runSyntax(inlinefs, input, sourceType);
    } else {
      const fstarget = loopTarget?.findDirectExpression(Expressions.FSTarget);
      if (fstarget) {
        FSTarget.runSyntax(fstarget, input, sourceType);
      }
    }
 
    for (const t of node.findDirectExpressions(Expressions.Dynamic)) {
      Dynamic.runSyntax(t, input);
    }
 
    const group = node.findDirectExpression(Expressions.LoopGroupBy);
    if (group) {
      LoopGroupBy.runSyntax(group, input);
    }
 
  }
}