All files / src/abap/5_syntax/expressions value_body.ts

96.26% Statements 103/107
93.61% Branches 44/47
100% Functions 1/1
96.26% Lines 103/107

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 1081x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 186x 186x 186x 186x 186x 58x 58x 128x 128x 128x 186x 9x 9x 128x 128x 186x 35x 35x 34x 34x 35x 127x 127x 186x 59x 59x 59x 55x 55x 55x 55x 1x 1x 1x 1x 54x 54x 55x 59x 126x 126x 186x 7x 7x 186x 19x 19x 126x 186x 84x 84x 84x 84x 84x         84x 84x 66x 66x 84x 84x 4x 5x 5x 5x 4x 84x 59x 59x 84x 42x 42x 1x 1x 1x 42x 84x 126x 186x 9x 9x 126x 186x 34x 34x 126x 186x 1x 1x 186x 186x 1x  
import {ExpressionNode} from "../../nodes";
import * as Expressions from "../../2_statements/expressions";
import {For} from "./for";
import {Source} from "./source";
import {AbstractType} from "../../types/basic/_abstract_type";
import {Let} from "./let";
import {FieldAssignment} from "./field_assignment";
import {AnyType, CharacterType, StringType, TableType, UnknownType, VoidType} from "../../types/basic";
import {CheckSyntaxKey, SyntaxInput, syntaxIssue} from "../_syntax_input";
 
export class ValueBody {
  public static runSyntax(
    node: ExpressionNode | undefined,
    input: SyntaxInput,
    targetType: AbstractType | undefined): AbstractType | undefined {
 
    if (node === undefined) {
      return targetType;
    }
 
    let letScoped = false;
    const letNode = node.findDirectExpression(Expressions.Let);
    if (letNode) {
      letScoped = Let.runSyntax(letNode, input);
    }
 
    let forScopes = 0;
    for (const forNode of node.findDirectExpressions(Expressions.For) || []) {
      const scoped = For.runSyntax(forNode, input);
      if (scoped === true) {
        forScopes++;
      }
    }
 
    const fields = new Set<string>();
    for (const s of node.findDirectExpressions(Expressions.FieldAssignment)) {
      FieldAssignment.runSyntax(s, input, targetType);
 
      if (node.findDirectExpression(Expressions.ValueBodyLine) === undefined) {
        // todo: refine, still needs to be checked when there are table lines
        const fieldname = s.findDirectExpression(Expressions.FieldSub)?.concatTokens().toUpperCase();
        if (fieldname) {
          if (fields.has(fieldname)) {
            const message = "Duplicate field assignment";
            input.issues.push(syntaxIssue(input, s.getFirstToken(), message));
            return VoidType.get(CheckSyntaxKey);
          }
          fields.add(fieldname);
        }
      }
    }
 
    let type: AbstractType | undefined = undefined; // todo, this is only correct if there is a single source in the body
    for (const s of node.findDirectExpressions(Expressions.Source)) {
      type = Source.runSyntax(s, input, type);
    }
    for (const s of node.findDirectExpression(Expressions.ValueBase)?.findDirectExpressions(Expressions.Source) || []) {
      type = Source.runSyntax(s, input, type);
    }
 
    for (const foo of node.findDirectExpressions(Expressions.ValueBodyLine)) {
      if (!(targetType instanceof TableType)
          && !(targetType instanceof UnknownType)
          && !(targetType instanceof AnyType)
          && targetType !== undefined
          && !(targetType instanceof VoidType)) {
        const message = "Value, not a table type";
        input.issues.push(syntaxIssue(input, foo.getFirstToken(), message));
        return VoidType.get(CheckSyntaxKey);
      }
      let rowType: AbstractType | undefined = targetType;
      if (targetType instanceof TableType) {
        rowType = targetType.getRowType();
      }
 
      for (const l of foo.findDirectExpressions(Expressions.ValueBodyLines)) {
        for (const s of l.findDirectExpressions(Expressions.Source)) {
// LINES OF ?? todo, pass type,
          Source.runSyntax(s, input);
        }
      }
      for (const s of foo.findDirectExpressions(Expressions.FieldAssignment)) {
        FieldAssignment.runSyntax(s, input, rowType);
      }
      for (const s of foo.findDirectExpressions(Expressions.Source)) {
        const sourceType = Source.runSyntax(s, input, rowType);
        if (rowType instanceof StringType && sourceType instanceof CharacterType) {
          const message = "VALUE, source type CharacterType not compatible with StringType";
          input.issues.push(syntaxIssue(input, s.getFirstToken(), message));
        }
      }
    }
 
    if (letScoped === true) {
      input.scope.pop(node.getLastToken().getEnd());
    }
 
    for (let i = 0; i < forScopes; i++) {
      input.scope.pop(node.getLastToken().getEnd());
    }
 
    if (targetType?.isGeneric() && type) {
      return type;
    }
    return targetType ? targetType : type;
  }
}