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

86.88% Statements 159/183
88.46% Branches 69/78
100% Functions 2/2
86.88% Lines 159/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 1x 1x 1939x 1939x 1939x 1939x 1939x 1939x 217x 217x 217x 217x 217x 217x 2x 2x 2x 2x 2x 2x 217x 1937x 1937x 1937x 1937x 1937x 1939x 800x 800x     800x 800x 1x 1x 1x 800x 251x       251x 251x 251x 6x 6x 6x 8x 8x 6x 6x 6x 6x 1x 1x 6x         6x         6x 799x 55x 55x 55x         548x 6x 493x 251x 10x 10x 251x 487x 236x 46x 1x 1x 1x 1x 45x 46x 31x 31x 236x 101x 190x 19x 19x 7x 2x 7x 2x 2x 7x 89x 20x 20x 13x 3x 13x 2x 2x 13x 20x 800x 800x 1935x 1935x 1935x 1x 1x 1x 1x 1937x 1937x 1937x 1937x 1937x     1937x 1937x 1937x 79x 1937x 1858x 1810x 1858x 48x 2x 48x 46x 46x 46x     46x 46x 25x 25x 46x 17x 17x 17x 21x 4x 4x 4x 4x 46x       1x 1x
import {ExpressionNode} from "../../nodes";
import {AbstractType} from "../../types/basic/_abstract_type";
import {INode} from "../../nodes/_inode";
import * as Expressions from "../../2_statements/expressions";
import {Dash, DashW, InstanceArrow} from "../../1_lexer/tokens";
import {StructureType, ObjectReferenceType, VoidType, DataReference, TableType, UnknownType, GenericObjectReferenceType, CharacterType, HexType} from "../../types/basic";
import {ComponentName} from "./component_name";
import {AttributeName} from "./attribute_name";
import {ReferenceType} from "../_reference";
import {FieldOffset} from "./field_offset";
import {FieldLength} from "./field_length";
import {TableExpression} from "./table_expression";
import {Dereference as DereferenceExpression} from "../../2_statements/expressions";
import {Dereference} from "./dereference";
import {SourceFieldSymbol} from "./source_field_symbol";
import {SourceField} from "./source_field";
import {CheckSyntaxKey, SyntaxInput, syntaxIssue} from "../_syntax_input";
 
export class FieldChain {
 
  public runSyntax(
    node: ExpressionNode,
    input: SyntaxInput,
    refType?: ReferenceType | ReferenceType[] | undefined): AbstractType | undefined {
 
    if (node.getFirstChild()?.get() instanceof Expressions.SourceField
        && node.findDirectExpression(Expressions.ComponentName)) {
      // workaround for names with dashes, eg. "sy-repid"
      const concat = node.concatTokens();
      const offset = node.findDirectExpression(Expressions.FieldOffset)?.concatTokens() || "";
      const length = node.findDirectExpression(Expressions.FieldLength)?.concatTokens() || "";
      const found = input.scope.findVariable(concat.replace(offset, "").replace(length, ""));
      if (found) {
        if (refType) {
          input.scope.addReference(node.getFirstToken(), found, refType, input.filename);
        }
        // this is not completely correct, but will work, dashes in names is a mess anyhow
        return found.getType();
      }
    }
 
    let context: AbstractType | undefined = undefined;
    const children = node.getChildren();
    context = this.findTop(children[0], input, refType);
 
    for (let i = 1; i < children.length; i++) {
      const current = children[i];
      if (current === undefined) {
        break;
      }
 
      if (current.get() instanceof DashW) {
        const message = "Ending with dash";
        input.issues.push(syntaxIssue(input, current.getFirstToken(), message));
        return new VoidType(CheckSyntaxKey);
      } else if (current.get() instanceof Dash) {
        if (context instanceof UnknownType) {
          const message = "Not a structure, type unknown, FieldChain";
          input.issues.push(syntaxIssue(input, current.getFirstToken(), message));
          return new VoidType(CheckSyntaxKey);
        } else if (!(context instanceof StructureType)
            && !(context instanceof TableType && context.isWithHeader())
            && !(context instanceof VoidType)) {
          if (context instanceof TableType && context.isWithHeader() === false) {
            let contextName = "";
            for (let j = 0; j < i; j++) {
              contextName += children[j].concatTokens();
            }
            if (input.scope.isAllowHeaderUse(contextName)) {
              // FOR ALL ENTRIES workaround
              context = context.getRowType();
              if (!(context instanceof StructureType) && !(context instanceof VoidType)) {
                context = new StructureType([{name: "TABLE_LINE", type: context}]);
              }
            } else {
              const message = "Table without header, cannot access fields, " + contextName;
              input.issues.push(syntaxIssue(input, current.getFirstToken(), message));
              return new VoidType(CheckSyntaxKey);
            }
          } else {
            const message = "Not a structure, FieldChain";
            input.issues.push(syntaxIssue(input, current.getFirstToken(), message));
            return new VoidType(CheckSyntaxKey);
          }
        }
      } else if (current.get() instanceof InstanceArrow) {
        if (!(context instanceof ObjectReferenceType)
            && !(context instanceof DataReference)
            && !(context instanceof VoidType)) {
          const message = "Not an object reference, field chain";
          input.issues.push(syntaxIssue(input, current.getFirstToken(), message));
          return new VoidType(CheckSyntaxKey);
        }
      } else if (current.get() instanceof DereferenceExpression) {
        context = new Dereference().runSyntax(current, context, input);
      } else if (current.get() instanceof Expressions.ComponentName) {
        if (context instanceof TableType && context.isWithHeader()) {
          context = context.getRowType();
        }
        context = new ComponentName().runSyntax(context, current, input);
      } else if (current instanceof ExpressionNode
          && current.get() instanceof Expressions.TableExpression) {
        if (!(context instanceof TableType) && !(context instanceof VoidType)) {
          const message = "Table expression, expected table";
          input.issues.push(syntaxIssue(input, current.getFirstToken(), message));
          return new VoidType(CheckSyntaxKey);
        }
        new TableExpression().runSyntax(current, input);
        if (!(context instanceof VoidType)) {
          context = context.getRowType();
        }
      } else if (current.get() instanceof Expressions.AttributeName) {
        context = new AttributeName().runSyntax(context, current, input, refType);
      } else if (current.get() instanceof Expressions.FieldOffset && current instanceof ExpressionNode) {
        const offset = new FieldOffset().runSyntax(current, input);
        if (offset) {
          if (context instanceof CharacterType) {
            context = new CharacterType(context.getLength() - offset);
          } else if (context instanceof HexType) {
            context = new HexType(context.getLength() - offset);
          }
        }
      } else if (current.get() instanceof Expressions.FieldLength && current instanceof ExpressionNode) {
        const length = new FieldLength().runSyntax(current, input);
        if (length) {
          if (context instanceof CharacterType) {
            context = new CharacterType(length);
          } else if (context instanceof HexType) {
            context = new HexType(length);
          }
        }
      }
 
    }
 
    return context;
  }
 
  ////////////////
 
  private findTop(
    node: INode | undefined,
    input: SyntaxInput,
    type: ReferenceType | ReferenceType[] | undefined): AbstractType | undefined {
 
    if (node === undefined) {
      return undefined;
    }
 
    if (node instanceof ExpressionNode
        && node.get() instanceof Expressions.SourceFieldSymbol) {
      return new SourceFieldSymbol().runSyntax(node, input);
    } else if (node instanceof ExpressionNode
        && node.get() instanceof Expressions.SourceField) {
      return new SourceField().runSyntax(node, input, type);
    } else if (node instanceof ExpressionNode
        && node.get() instanceof Expressions.Field) {
      return new SourceField().runSyntax(node, input, type);
    } else if (node.get() instanceof Expressions.ClassName) {
      const classTok = node.getFirstToken();
      const classNam = classTok.getStr();
      if (classNam.toUpperCase() === "OBJECT") {
        return new GenericObjectReferenceType();
      }
      const found = input.scope.existsObject(classNam);
      if (found?.id) {
        input.scope.addReference(classTok, found.id, ReferenceType.ObjectOrientedReference, input.filename);
        return new ObjectReferenceType(found.id);
      } else if (input.scope.getDDIC().inErrorNamespace(classNam) === false) {
        input.scope.addReference(classTok, undefined,
                                 ReferenceType.ObjectOrientedVoidReference, input.filename, {ooName: classNam.toUpperCase()});
        return new VoidType(classNam);
      } else {
        const message = "Unknown class " + classNam;
        input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
        return new VoidType(CheckSyntaxKey);
      }
    }

    return undefined;
  }
 
}