All Downloads are FREE. Search and download functionalities are using the official Maven repository.

au.com.integradev.delphi.symbol.declaration.VariableNameDeclarationImpl Maven / Gradle / Ivy

The newest version!
/*
 * Sonar Delphi Plugin
 * Copyright (C) 2019 Integrated Application Development
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package au.com.integradev.delphi.symbol.declaration;

import au.com.integradev.delphi.antlr.ast.node.DelphiNodeImpl;
import au.com.integradev.delphi.antlr.ast.node.NameDeclarationNodeImpl;
import au.com.integradev.delphi.symbol.SymbolicNode;
import au.com.integradev.delphi.symbol.resolve.TypeInferrer;
import java.util.List;
import java.util.Objects;
import org.sonar.plugins.communitydelphi.api.ast.ConstStatementNode;
import org.sonar.plugins.communitydelphi.api.ast.FieldSectionNode;
import org.sonar.plugins.communitydelphi.api.ast.ForInStatementNode;
import org.sonar.plugins.communitydelphi.api.ast.ForLoopVarDeclarationNode;
import org.sonar.plugins.communitydelphi.api.ast.ForToStatementNode;
import org.sonar.plugins.communitydelphi.api.ast.NameDeclarationNode;
import org.sonar.plugins.communitydelphi.api.ast.Node;
import org.sonar.plugins.communitydelphi.api.ast.RecordVariantItemNode;
import org.sonar.plugins.communitydelphi.api.ast.TypeNode;
import org.sonar.plugins.communitydelphi.api.ast.VarDeclarationNode;
import org.sonar.plugins.communitydelphi.api.ast.VarStatementNode;
import org.sonar.plugins.communitydelphi.api.ast.Visibility;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.NameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.VariableNameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.scope.DelphiScope;
import org.sonar.plugins.communitydelphi.api.type.IntrinsicType;
import org.sonar.plugins.communitydelphi.api.type.Type;
import org.sonar.plugins.communitydelphi.api.type.Type.ArrayConstructorType;
import org.sonar.plugins.communitydelphi.api.type.TypeFactory;
import org.sonar.plugins.communitydelphi.api.type.TypeSpecializationContext;
import org.sonar.plugins.communitydelphi.api.type.Typed;

public final class VariableNameDeclarationImpl extends NameDeclarationImpl
    implements VariableNameDeclaration {
  private final Type type;
  private final VisibilityType visibility;
  private final Kind kind;
  private int hashCode;

  public VariableNameDeclarationImpl(NameDeclarationNode node) {
    this(new SymbolicNode(node), extractType(node), extractVisibility(node), extractKind(node));
  }

  private VariableNameDeclarationImpl(
      SymbolicNode location, Type type, VisibilityType visibility, Kind kind) {
    super(location);
    this.type = type;
    this.visibility = visibility;
    this.kind = kind;
  }

  public static VariableNameDeclaration result(Type type, DelphiScope scope) {
    return new VariableNameDeclarationImpl(
        SymbolicNode.imaginary("Result", scope), type, VisibilityType.PUBLIC, Kind.RESULT);
  }

  public static VariableNameDeclaration self(Type type, DelphiScope scope) {
    return new VariableNameDeclarationImpl(
        SymbolicNode.imaginary("Self", scope), type, VisibilityType.PUBLIC, Kind.SELF);
  }

  public static VariableNameDeclaration parameter(String name, Type type, DelphiScope scope) {
    return new VariableNameDeclarationImpl(
        SymbolicNode.imaginary(name, scope), type, VisibilityType.PUBLIC, Kind.PARAMETER);
  }

  public static VariableNameDeclaration constant(String name, Type type, DelphiScope scope) {
    return new VariableNameDeclarationImpl(
        SymbolicNode.imaginary(name, scope), type, VisibilityType.PUBLIC, Kind.CONST);
  }

  private static Type extractType(NameDeclarationNode node) {
    var nodeKind = ((NameDeclarationNodeImpl) node).getKind();
    switch (nodeKind) {
      case CONST:
        return constType(node);
      case EXCEPT_ITEM:
      case RECORD_VARIANT_TAG:
        return ((Typed) node.getParent()).getType();
      case PARAMETER:
      case FIELD:
      case VAR:
        return ((Typed) node.getNthParent(2)).getType();
      case INLINE_CONST:
        return inlineConstType(node);
      case INLINE_VAR:
        return inlineVarType(node);
      case LOOP_VAR:
        return loopVarType(node);
      default:
        throw new AssertionError("Unhandled DeclarationKind");
    }
  }

  private static Type constType(NameDeclarationNode node) {
    Type type = ((Typed) node.getParent()).getType();
    if (type.isArrayConstructor()) {
      List elementTypes = ((ArrayConstructorType) type).elementTypes();
      Type elementType = elementTypes.stream().findFirst().orElse(TypeFactory.voidType());
      TypeFactory typeFactory = ((DelphiNodeImpl) node).getTypeFactory();
      if (elementType.isInteger()) {
        elementType = typeFactory.getIntrinsic(IntrinsicType.BYTE);
      } else if (elementType.isChar()) {
        elementType = typeFactory.getIntrinsic(IntrinsicType.ANSICHAR);
      }
      type = typeFactory.set(elementType);
    }
    return type;
  }

  private static Type inlineConstType(NameDeclarationNode node) {
    var constStatement = (ConstStatementNode) node.getParent();
    return getDeclaredTypeWithTypeInferenceFallback(
        ((DelphiNodeImpl) node).getTypeFactory(),
        constStatement.getTypeNode(),
        constStatement.getExpression());
  }

  private static Type inlineVarType(NameDeclarationNode node) {
    var varStatement = (VarStatementNode) node.getNthParent(2);
    return getDeclaredTypeWithTypeInferenceFallback(
        ((DelphiNodeImpl) node).getTypeFactory(),
        varStatement.getTypeNode(),
        varStatement.getExpression());
  }

  private static Type loopVarType(NameDeclarationNode node) {
    ForLoopVarDeclarationNode loopVarDeclaration = (ForLoopVarDeclarationNode) node.getParent();
    Node loop = loopVarDeclaration.getParent();

    Typed typed;
    if (loop instanceof ForToStatementNode) {
      typed = ((ForToStatementNode) loop).getInitializerExpression();
    } else {
      typed = ((ForInStatementNode) loop).getCurrentDeclaration();
    }

    return getDeclaredTypeWithTypeInferenceFallback(
        ((DelphiNodeImpl) node).getTypeFactory(), loopVarDeclaration.getTypeNode(), typed);
  }

  private static Type getDeclaredTypeWithTypeInferenceFallback(
      TypeFactory typeFactory, TypeNode typeNode, Typed typed) {
    if (typeNode != null) {
      return typeNode.getType();
    } else {
      TypeInferrer inferrer = new TypeInferrer(typeFactory);
      return inferrer.infer(typed);
    }
  }

  private static VisibilityType extractVisibility(NameDeclarationNode node) {
    Visibility visibility;
    var nodeKind = ((NameDeclarationNodeImpl) node).getKind();

    switch (nodeKind) {
      case CONST:
        visibility = (Visibility) node.getParent();
        break;
      case FIELD:
        visibility = (Visibility) node.getNthParent(2);
        break;
      default:
        return VisibilityType.PUBLIC;
    }

    return visibility.getVisibility();
  }

  private static Kind extractKind(NameDeclarationNode node) {
    var nodeKind = ((NameDeclarationNodeImpl) node).getKind();
    switch (nodeKind) {
      case CONST:
        return Kind.CONST;
      case EXCEPT_ITEM:
        return Kind.EXCEPT_ITEM;
      case FIELD:
        Node ancestor = node.getNthParent(3);
        if (ancestor instanceof FieldSectionNode
            && ((FieldSectionNode) ancestor).isClassFieldSection()) {
          return Kind.CLASS_VAR;
        }
        if (node.getNthParent(3) instanceof RecordVariantItemNode) {
          return Kind.RECORD_VARIANT_FIELD;
        }
        return Kind.FIELD;
      case INLINE_CONST:
        return Kind.INLINE_CONST;
      case INLINE_VAR:
      case LOOP_VAR:
        return Kind.INLINE_VAR;
      case PARAMETER:
        return Kind.PARAMETER;
      case RECORD_VARIANT_TAG:
        return Kind.RECORD_VARIANT_TAG;
      case VAR:
        if (((VarDeclarationNode) node.getNthParent(2)).isAbsolute()) {
          return Kind.ABSOLUTE_VAR;
        }
        return Kind.VAR;
      default:
        throw new AssertionError("Unhandled DeclarationKind");
    }
  }

  @Override
  public Type getType() {
    return type;
  }

  @Override
  public VisibilityType getVisibility() {
    return visibility;
  }

  @Override
  public boolean isInline() {
    switch (kind) {
      case INLINE_VAR:
      case INLINE_CONST:
        return true;
      default:
        return false;
    }
  }

  @Override
  public boolean isVar() {
    switch (kind) {
      case VAR:
      case INLINE_VAR:
      case ABSOLUTE_VAR:
        return true;
      default:
        return false;
    }
  }

  @Override
  public boolean isConst() {
    switch (kind) {
      case CONST:
      case INLINE_CONST:
        return true;
      default:
        return false;
    }
  }

  @Override
  public boolean isField() {
    switch (kind) {
      case FIELD:
      case RECORD_VARIANT_FIELD:
        return true;
      default:
        return false;
    }
  }

  @Override
  public boolean isClassVar() {
    return kind == Kind.CLASS_VAR;
  }

  @Override
  public boolean isUnion() {
    switch (kind) {
      case ABSOLUTE_VAR:
      case RECORD_VARIANT_FIELD:
        return true;
      default:
        return false;
    }
  }

  @Override
  public boolean isSelf() {
    return kind == Kind.SELF;
  }

  @Override
  public boolean isResult() {
    return kind == Kind.RESULT;
  }

  @Override
  protected NameDeclaration doSpecialization(TypeSpecializationContext context) {
    return new VariableNameDeclarationImpl(node, type.specialize(context), visibility, kind);
  }

  @Override
  public boolean equals(Object other) {
    if (!(other instanceof VariableNameDeclarationImpl)) {
      return false;
    }
    VariableNameDeclarationImpl that = (VariableNameDeclarationImpl) other;
    return getImage().equalsIgnoreCase(that.getImage()) && type.is(that.type) && kind == that.kind;
  }

  @Override
  public int hashCode() {
    if (hashCode == 0) {
      hashCode = Objects.hash(getImage().toLowerCase(), type.getImage(), kind);
    }
    return hashCode;
  }

  @Override
  public String toString() {
    return "Variable: image = '" + getImage();
  }

  private enum Kind {
    VAR,
    CLASS_VAR,
    ABSOLUTE_VAR,
    CONST,
    FIELD,
    RECORD_VARIANT_FIELD,
    RECORD_VARIANT_TAG,
    EXCEPT_ITEM,
    INLINE_CONST,
    INLINE_VAR,
    PARAMETER,
    SELF,
    RESULT
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy