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 12x 12x         12x 12x 12x 12x 12x 12x 12x       12x 2x 12x         12x 12x 10x 10x 10x 8x 8x 4x 4x 4x 10x 2x 2x 10x   10x 1x 10x           10x 11x 11x 12x         11x 11x 11x 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 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 new VoidType(CheckSyntaxKey);
    }
 
    const sourceType = new 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 new VoidType(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 new VoidType(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 = new VoidType(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 new VoidType(CheckSyntaxKey);
      }
    }
    new 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 new VoidType(CheckSyntaxKey);
    }
 
    return tt!;
  }
}