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

70% Statements 49/70
63.63% Branches 14/22
100% Functions 1/1
70% Lines 49/70

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 701x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 259x 259x         259x 259x 259x 259x 259x 259x 259x       259x 5x 259x         259x 259x 254x 254x 254x 245x 245x 238x 238x 238x 254x 9x 9x 254x   254x 3x 254x           254x 256x 256x 259x         256x 256x 256x 1x
import {ExpressionNode} from "../../nodes";
import {DataReference, GenericObjectReferenceType, ObjectReferenceType, UnknownType, VoidType} from "../../types/basic";
import * as Expressions from "../../2_statements/expressions";
import {AbstractType} from "../../types/basic/_abstract_type";
import {Source} from "./source";
import {TypeUtils} from "../_type_utils";
import {BasicTypes} from "../basic_types";
import {ReferenceType} from "../_reference";
import {CheckSyntaxKey, SyntaxInput, syntaxIssue} from "../_syntax_input";
 
export class Cast {
  public static runSyntax(node: ExpressionNode, input: SyntaxInput, targetType: AbstractType | undefined): AbstractType {
    const sourceNode = node.findDirectExpression(Expressions.Source);
    if (sourceNode === undefined) {
      const message = "Cast, source node not found";
      input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
      return VoidType.get(CheckSyntaxKey);
    }
 
    const sourceType = Source.runSyntax(sourceNode, input);
    let tt: AbstractType | undefined = undefined;
 
    const typeExpression = node.findDirectExpression(Expressions.TypeNameOrInfer);
    const typeName = typeExpression?.concatTokens();
    if (typeName === undefined) {
      const message = "Cast, child TypeNameOrInfer not found";
      input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
      return VoidType.get(CheckSyntaxKey);
    } else if (typeName === "#" && targetType) {
      tt = targetType;
    } else if (typeName === "#") {
      const message = "Cast, todo, infer type";
      input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
      return VoidType.get(CheckSyntaxKey);
    }
 
    if (tt === undefined && typeExpression) {
      const basic = new BasicTypes(input);
      tt = basic.parseType(typeExpression);
      if (tt === undefined || tt instanceof VoidType || tt instanceof UnknownType) {
        const found = input.scope.findObjectDefinition(typeName);
        if (found) {
          tt = new ObjectReferenceType(found, {qualifiedName: typeName});
          input.scope.addReference(typeExpression.getFirstToken(), found, ReferenceType.ObjectOrientedReference, input.filename);
        }
      } else {
        tt = new DataReference(tt, typeName);
      }
      if (tt === undefined && input.scope.getDDIC().inErrorNamespace(typeName) === false) {
        tt = VoidType.get(typeName);
      } else if (typeName.toUpperCase() === "OBJECT") {
        return new GenericObjectReferenceType();
      } else if (tt === undefined) {
        // todo, this should be an UnknownType instead?
        const message = "Type \"" + typeName + "\" not found in scope, Cast";
        input.issues.push(syntaxIssue(input, typeExpression.getFirstToken(), message));
        return VoidType.get(CheckSyntaxKey);
      }
    }
    Source.addIfInferred(node, input, tt);
 
    if (new TypeUtils(input.scope).isCastable(sourceType, tt) === false) {
      const message = "Cast, incompatible types";
      input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
      return VoidType.get(CheckSyntaxKey);
    }
 
    return tt!;
  }
}