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

87.22% Statements 116/133
71.43% Branches 40/56
100% Functions 2/2
87.22% Lines 116/133

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 1331x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 306x 306x 306x 21x 21x 21x 1x 1x 1x 21x 305x 305x 305x 306x     305x 305x 306x 26x 26x 279x 306x 46x 46x     46x 46x 15x   15x 15x 15x 15x     46x 2x 2x 2x     31x 15x 29x 3x 3x 3x 3x     3x 2x 2x 14x 11x 1x     1x 1x 1x 1x 11x 5x 5x 5x 46x 278x 278x 306x 1x 1x 277x 277x 277x 1x 1x 1x 1x 305x     305x 305x 305x 305x 305x 305x 269x 269x 243x 243x 269x 2x 2x 2x 2x 2x 269x 305x 3x 3x 2x 2x 3x 1x 1x 1x     3x 33x 33x 33x 1x
import * as Expressions from "../../2_statements/expressions";
import {ExpressionNode} from "../../nodes";
import {CurrentScope} from "../_current_scope";
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} 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";
 
export class Target {
  public runSyntax(node: ExpressionNode, scope: CurrentScope, filename: string): AbstractType | undefined {
 
    const concat = node.concatTokens();
    if (concat.includes("-")) {
      // workaround for names with dashes
      const found = scope.findVariable(concat);
      if (found) {
        scope.addReference(node.getFirstToken(), found, ReferenceType.DataWriteReference, filename);
        return found.getType();
      }
    }
 
    const children = node.getChildren().slice();
    const first = children.shift();
    if (first === undefined || !(first instanceof ExpressionNode)) {
      return undefined;
    }
 
    let context = this.findTop(first, scope, filename);
    if (context === undefined) {
      throw new Error(`"${first.getFirstToken().getStr()}" not found, Target`);
    }
 
    while (children.length > 0) {
      const current = children.shift();
      if (current === undefined) {
        break;
      }
 
      if (current.get() instanceof Dash) {
        if (context instanceof UnknownType) {
          throw new Error("Not a structure, type unknown, target");
        } 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)) {
          throw new Error("Not a structure, target");
        }
      } else if (current.get() instanceof InstanceArrow) {
        if (!(context instanceof ObjectReferenceType)
            && !(context instanceof DataReference)
            && !(context instanceof VoidType)) {
          throw new Error("Not a object reference, target");
        }
      } else if (current.get() instanceof Expressions.ComponentName) {
        context = new ComponentName().runSyntax(context, current);
      } else if (current.get() instanceof Expressions.TableBody) {
        if (!(context instanceof TableType)
            && !(context instanceof VoidType)
            && !(context instanceof UnknownType)
            && !(context instanceof UnknownType)) {
          throw new Error("Not a internal table, \"[]\"");
        }
        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)) {
          throw new Error("Table expression, expected table");
        }
        new TableExpression().runSyntax(current, scope, filename);
        if (!(context instanceof VoidType)) {
          context = context.getRowType();
        }
      } else if (current.get() instanceof Expressions.AttributeName) {
        const type = children.length === 0 ? ReferenceType.DataWriteReference : ReferenceType.DataReadReference;
        context = new AttributeName().runSyntax(context, current, scope, filename, type);
      }
    }
 
    const offset = node.findDirectExpression(Expressions.FieldOffset);
    if (offset) {
      new FieldOffset().runSyntax(offset, scope, filename);
    }
 
    return context;
  }
 
/////////////////////////////////
 
  private findTop(node: INode | undefined, scope: CurrentScope, filename: string): 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 = scope.findVariable(name);
      if (found) {
        scope.addReference(token, found, ReferenceType.DataWriteReference, filename);
      }
      if (name.includes("~")) {
        const idef = scope.findInterfaceDefinition(name.split("~")[0]);
        if (idef) {
          scope.addReference(token, idef, ReferenceType.ObjectOrientedReference, filename);
        }
      }
      return found?.getType();
    } else if (node.get() instanceof Expressions.ClassName) {
      const found = scope.findObjectDefinition(name);
      if (found) {
        scope.addReference(token, found, ReferenceType.ObjectOrientedReference, filename);
        return new ObjectReferenceType(found);
      } else if (scope.getDDIC().inErrorNamespace(name) === false) {
        scope.addReference(token, undefined, ReferenceType.ObjectOrientedVoidReference, filename, {ooName: name, ooType: "CLAS"});
        return new VoidType(name);
      } else {
        return new UnknownType(name + " unknown, Target");
      }
    }
 
    return new UnknownType("unknown target type");
  }
}