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

87.43% Statements 160/183
81.92% Branches 68/83
100% Functions 2/2
87.43% Lines 160/183

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 1831x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1429x 1429x 1429x 178x 178x 178x 2x 2x 2x 178x 1427x 1427x 1427x 1429x     1427x 1427x 1429x 57x 57x 57x 57x 1370x 1370x 1429x 1699x 1699x 1356x 1356x 343x 343x 1699x 146x   146x 1x 146x 145x 145x 145x         1699x 19x 19x 19x 13x 13x 13x 13x 197x 4x         4x 4x 3x 3x 178x 145x 174x 3x 3x 3x 3x         3x 2x 2x 29x 26x 3x         3x 3x 3x 3x 26x 9x 9x 9x 1699x 1356x 1356x 1429x 5x 2x 2x 2x 2x 3x 3x 1354x 1354x 1429x 6x 2x 2x 2x 2x 4x 4x 1352x 1352x 1352x 1x 1x 1x 1x 1427x     1427x 1427x 1427x 1427x 1427x 1427x 1291x 1291x 1234x 1234x 1291x 2x 2x 2x 2x 2x 1291x 1427x 3x 3x 2x 2x 3x 1x 1x 1x     136x 2x 2x 1x 1x 1x 1x 1x 1x 131x 131x 131x 1x
import * as Expressions from "../../2_statements/expressions";
import {ExpressionNode} from "../../nodes";
import {AbstractType} from "../../types/basic/_abstract_type";
import {UnknownType} from "../../types/basic/unknown_type";
import {INode} from "../../nodes/_inode";
import {Dash, InstanceArrow} from "../../1_lexer/tokens";
import {StructureType, ObjectReferenceType, VoidType, DataReference, TableType, XStringType, StringType} from "../../types/basic";
import {ComponentName} from "./component_name";
import {AttributeName} from "./attribute_name";
import {FieldOffset} from "./field_offset";
import {ReferenceType} from "../_reference";
import {TableExpression} from "./table_expression";
import {Dereference} from "../../2_statements/expressions";
import {FieldLength} from "./field_length";
import {Cast} from "./cast";
import {CheckSyntaxKey, SyntaxInput, syntaxIssue} from "../_syntax_input";
 
export class Target {
  public static runSyntax(node: ExpressionNode, input: SyntaxInput): AbstractType | undefined {
 
    const concat = node.concatTokens();
    if (concat.includes("-")) {
      // workaround for names with dashes
      const found = input.scope.findVariable(concat);
      if (found) {
        input.scope.addReference(node.getFirstToken(), found, ReferenceType.DataWriteReference, input.filename);
        return found.getType();
      }
    }
 
    const children = node.getChildren();
    const first = children[0];
    if (first === undefined || !(first instanceof ExpressionNode)) {
      return undefined;
    }
 
    let context = this.findTop(first, input);
    if (context === undefined) {
      const message = `"${first.getFirstToken().getStr()}" not found, Target`;
      input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
      return VoidType.get(CheckSyntaxKey);
    }
 
    let currentIndex = 1;
    while (currentIndex <= children.length) {
      const current = children[currentIndex];
      if (current === undefined) {
        break;
      }
      currentIndex++;
 
      if (current.get() instanceof Dash) {
        if (context instanceof UnknownType) {
          return VoidType.get(CheckSyntaxKey);
        } else if (context instanceof TableType && context.isWithHeader() && context.getRowType() instanceof UnknownType) {
          return VoidType.get(CheckSyntaxKey);
        } else if (!(context instanceof StructureType)
            && !(context instanceof TableType && context.isWithHeader() && context.getRowType() instanceof StructureType)
            && !(context instanceof TableType && context.isWithHeader() && context.getRowType() instanceof VoidType)
            && !(context instanceof VoidType)) {
          const message = "Not a structure, target, " + context?.constructor.name + ", " + current.concatTokens();
          input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
          return VoidType.get(CheckSyntaxKey);
        }
      } else if (current.get() instanceof InstanceArrow) {
        if (!(context instanceof ObjectReferenceType)
            && !(context instanceof DataReference)
            && !(context instanceof VoidType)) {
          const message = "Not an object reference, target";
          input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
          return VoidType.get(CheckSyntaxKey);
        }
      } else if (current.get() instanceof Dereference) {
        if (!(context instanceof DataReference) && !(context instanceof VoidType)) {
          const message = "Not an object reference, target";
          input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
          return VoidType.get(CheckSyntaxKey);
        }
 
        if (!(context instanceof VoidType)) {
          context = context.getType();
        }
      } else if (current.get() instanceof Expressions.ComponentName) {
        context = ComponentName.runSyntax(context, current, input);
      } else if (current.get() instanceof Expressions.TableBody) {
        if (!(context instanceof TableType)
            && !(context instanceof VoidType)
            && !(context instanceof UnknownType)
            && !(context instanceof UnknownType)) {
          const message = "Not a internal table, \"[]\"";
          input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
          return VoidType.get(CheckSyntaxKey);
        }
        if (context instanceof TableType && context.isWithHeader()) {
          context = new TableType(context.getRowType(), {...context.getOptions(), withHeader: false});
        }
      } else if (current instanceof ExpressionNode
          && current.get() instanceof Expressions.TableExpression) {
        if (!(context instanceof TableType) && !(context instanceof VoidType) && !(context instanceof UnknownType)) {
          const message = "Table expression, expected table";
          input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
          return VoidType.get(CheckSyntaxKey);
        }
        TableExpression.runSyntax(current, input, context);
        if (!(context instanceof VoidType) && !(context instanceof UnknownType)) {
          context = context.getRowType();
        }
      } else if (current.get() instanceof Expressions.AttributeName) {
        const type = children.length === 0 ? ReferenceType.DataWriteReference : ReferenceType.DataReadReference;
        context = AttributeName.runSyntax(context, current, input, type);
      }
    }
 
    const offset = node.findDirectExpression(Expressions.FieldOffset);
    if (offset) {
      if (context instanceof XStringType || context instanceof StringType) {
        const message = "xstring/string offset/length in writer position not possible";
        input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
        return VoidType.get(CheckSyntaxKey);
      }
      FieldOffset.runSyntax(offset, input);
    }
 
    const length = node.findDirectExpression(Expressions.FieldLength);
    if (length) {
      if (context instanceof XStringType || context instanceof StringType) {
        const message = "xstring/string offset/length in writer position not possible";
        input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
        return VoidType.get(CheckSyntaxKey);
      }
      FieldLength.runSyntax(length, input);
    }
 
    return context;
  }
 
/////////////////////////////////
 
  private static findTop(node: INode | undefined, input: SyntaxInput): AbstractType | undefined {
    if (node === undefined) {
      return undefined;
    }
 
    const token = node.getFirstToken();
    const name = token.getStr();
 
    if (node.get() instanceof Expressions.TargetField
        || node.get() instanceof Expressions.TargetFieldSymbol) {
      const found = input.scope.findVariable(name);
      if (found) {
        input.scope.addReference(token, found, ReferenceType.DataWriteReference, input.filename);
      }
      if (name.includes("~")) {
        const idef = input.scope.findInterfaceDefinition(name.split("~")[0]);
        if (idef) {
          input.scope.addReference(token, idef, ReferenceType.ObjectOrientedReference, input.filename);
        }
      }
      return found?.getType();
    } else if (node.get() instanceof Expressions.ClassName) {
      const found = input.scope.findObjectDefinition(name);
      if (found) {
        input.scope.addReference(token, found, ReferenceType.ObjectOrientedReference, input.filename);
        return new ObjectReferenceType(found);
      } else if (input.scope.getDDIC().inErrorNamespace(name) === false) {
        input.scope.addReference(token, undefined, ReferenceType.ObjectOrientedVoidReference, input.filename, {ooName: name, ooType: "CLAS"});
        return VoidType.get(name);
      } else {
        return new UnknownType(name + " unknown, Target");
      }
    } else if (node.get() instanceof Expressions.Cast && node instanceof ExpressionNode) {
      const ret = Cast.runSyntax(node, input, undefined);
      if (ret instanceof UnknownType) {
        const message = "CAST, uknown type";
        input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
        return VoidType.get(CheckSyntaxKey);
      }
      return ret;
    }
 
    return new UnknownType("unknown target type");
  }
}