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

au.com.integradev.delphi.symbol.resolve.NameResolutionHelper 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.resolve;

import static org.sonar.plugins.communitydelphi.api.type.TypeFactory.unknownType;

import au.com.integradev.delphi.antlr.ast.node.TypeNodeImpl;
import au.com.integradev.delphi.symbol.SearchMode;
import au.com.integradev.delphi.symbol.declaration.NameDeclarationImpl;
import au.com.integradev.delphi.symbol.occurrence.NameOccurrenceImpl;
import au.com.integradev.delphi.symbol.scope.RoutineScopeImpl;
import au.com.integradev.delphi.type.generic.TypeParameterTypeImpl;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.sonar.plugins.communitydelphi.api.ast.AnonymousMethodNode;
import org.sonar.plugins.communitydelphi.api.ast.ArrayIndicesNode;
import org.sonar.plugins.communitydelphi.api.ast.ArrayTypeNode;
import org.sonar.plugins.communitydelphi.api.ast.AssignmentStatementNode;
import org.sonar.plugins.communitydelphi.api.ast.AttributeListNode;
import org.sonar.plugins.communitydelphi.api.ast.AttributeNode;
import org.sonar.plugins.communitydelphi.api.ast.DelphiNode;
import org.sonar.plugins.communitydelphi.api.ast.ExpressionNode;
import org.sonar.plugins.communitydelphi.api.ast.FormalParameterListNode;
import org.sonar.plugins.communitydelphi.api.ast.FormalParameterNode;
import org.sonar.plugins.communitydelphi.api.ast.GenericArgumentsNode;
import org.sonar.plugins.communitydelphi.api.ast.HelperTypeNode;
import org.sonar.plugins.communitydelphi.api.ast.MethodResolutionClauseNode;
import org.sonar.plugins.communitydelphi.api.ast.NameReferenceNode;
import org.sonar.plugins.communitydelphi.api.ast.Node;
import org.sonar.plugins.communitydelphi.api.ast.PrimaryExpressionNode;
import org.sonar.plugins.communitydelphi.api.ast.PropertyImplementsSpecifierNode;
import org.sonar.plugins.communitydelphi.api.ast.PropertyNode;
import org.sonar.plugins.communitydelphi.api.ast.PropertyReadSpecifierNode;
import org.sonar.plugins.communitydelphi.api.ast.PropertyWriteSpecifierNode;
import org.sonar.plugins.communitydelphi.api.ast.RecordExpressionItemNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineDeclarationNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineImplementationNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineParametersNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineReturnTypeNode;
import org.sonar.plugins.communitydelphi.api.ast.StructTypeNode;
import org.sonar.plugins.communitydelphi.api.ast.SubRangeTypeNode;
import org.sonar.plugins.communitydelphi.api.ast.TypeDeclarationNode;
import org.sonar.plugins.communitydelphi.api.ast.TypeNode;
import org.sonar.plugins.communitydelphi.api.ast.TypeReferenceNode;
import org.sonar.plugins.communitydelphi.api.ast.UnaryExpressionNode;
import org.sonar.plugins.communitydelphi.api.operator.UnaryOperator;
import org.sonar.plugins.communitydelphi.api.symbol.Invocable;
import org.sonar.plugins.communitydelphi.api.symbol.NameOccurrence;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.GenerifiableDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.NameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.PropertyNameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineNameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.TypeNameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.TypedDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.scope.DelphiScope;
import org.sonar.plugins.communitydelphi.api.symbol.scope.RoutineScope;
import org.sonar.plugins.communitydelphi.api.type.Type;
import org.sonar.plugins.communitydelphi.api.type.Type.AliasType;
import org.sonar.plugins.communitydelphi.api.type.Type.ProceduralType;
import org.sonar.plugins.communitydelphi.api.type.Type.ScopedType;
import org.sonar.plugins.communitydelphi.api.type.Type.TypeParameterType;
import org.sonar.plugins.communitydelphi.api.type.TypeFactory;

public class NameResolutionHelper {
  private final TypeFactory typeFactory;
  private SearchMode searchMode;

  public NameResolutionHelper(TypeFactory typeFactory) {
    this(typeFactory, SearchMode.DEFAULT);
  }

  public NameResolutionHelper(TypeFactory typeFactory, SearchMode searchMode) {
    this.typeFactory = typeFactory;
    this.searchMode = searchMode;
  }

  private NameResolver createNameResolver() {
    return new NameResolver(typeFactory, searchMode);
  }

  public void resolve(TypeDeclarationNode typeDeclaration) {
    AttributeListNode attributeListNode = typeDeclaration.getAttributeList();
    if (attributeListNode != null) {
      attributeListNode.getAttributes().forEach(this::resolve);
    }

    typeDeclaration.getTypeNode().getParentTypeNodes().forEach(this::resolve);
    resolve(typeDeclaration.getTypeNode());
  }

  public void resolve(TypeNode type) {
    ((TypeNodeImpl) type).clearCachedType();

    List nodes = new ArrayList<>();
    nodes.add(type);

    if (type instanceof SubRangeTypeNode) {
      SubRangeTypeNode subrange = (SubRangeTypeNode) type;
      resolve(subrange.getLowExpression());
      resolve(subrange.getHighExpression());
      return;
    }

    if (type instanceof ArrayTypeNode) {
      ArrayTypeNode array = (ArrayTypeNode) type;
      resolve(array.getElementTypeNode());
      ArrayIndicesNode arrayIndices = array.getArrayIndices();
      if (arrayIndices != null) {
        arrayIndices.getTypeNodes().forEach(this::resolve);
      }
      return;
    }

    if (!(type instanceof StructTypeNode)) {
      nodes.addAll(type.findDescendantsOfType(TypeNode.class));
      nodes.addAll(type.findChildrenOfType(PrimaryExpressionNode.class));
    }

    if (type instanceof HelperTypeNode) {
      nodes.add(((HelperTypeNode) type).getFor());
    }

    for (DelphiNode node : nodes) {
      node.findChildrenOfType(NameReferenceNode.class).forEach(this::resolve);
    }
  }

  public void resolve(NameReferenceNode reference) {
    NameResolver resolver = createNameResolver();
    resolver.readNameReference(reference);
    resolver.addToSymbolTable();
  }

  public void resolve(AttributeNode attribute) {
    NameResolver resolver = createNameResolver();
    resolver.readAttribute(attribute);
    resolver.addToSymbolTable();
  }

  public void resolve(PrimaryExpressionNode expression) {
    NameResolver resolver = createNameResolver();
    resolver.readPrimaryExpression(expression);

    if (handleRoutineReference(expression, resolver)
        || handleAddressOf(expression, resolver)
        || handlePascalReturn(expression, resolver)) {
      return;
    }

    if (!resolver.isExplicitInvocation()) {
      resolver.disambiguateImplicitEmptyArgumentList();
    }

    resolver.addToSymbolTable();
  }

  public void resolve(MethodResolutionClauseNode resolutionClause) {
    NameResolver interfaceMethodResolver = createNameResolver();
    interfaceMethodResolver.readNameReference(resolutionClause.getInterfaceMethodNameNode());
    List interfaceMethods =
        interfaceMethodResolver.getDeclarations().stream()
            .filter(RoutineNameDeclaration.class::isInstance)
            .map(RoutineNameDeclaration.class::cast)
            .filter(method -> method.getTypeDeclaration() != null)
            .sorted(
                (left, right) -> {
                  Type leftType = left.getTypeDeclaration().getType();
                  TypeNameDeclaration typeDecl = right.getTypeDeclaration();
                  Type rightType = (typeDecl == null) ? unknownType() : typeDecl.getType();

                  if (leftType.is(rightType)) {
                    return left.getNode().getBeginLine() - right.getNode().getBeginLine();
                  } else if (leftType.isDescendantOf(rightType)) {
                    return 1;
                  } else {
                    return -1;
                  }
                })
            .collect(Collectors.toList());

    NameResolver concreteMethodResolver = createNameResolver();
    concreteMethodResolver.readNameReference(resolutionClause.getImplementationMethodNameNode());
    List implementationMethods =
        concreteMethodResolver.getDeclarations().stream()
            .filter(RoutineNameDeclaration.class::isInstance)
            .map(RoutineNameDeclaration.class::cast)
            .collect(Collectors.toList());

    Set interfaceDeclarations = interfaceMethodResolver.getDeclarations();
    Set concreteDeclarations = concreteMethodResolver.getDeclarations();

    interfaceDeclarations.clear();
    concreteDeclarations.clear();

    for (RoutineNameDeclaration interfaceCandidate : interfaceMethods) {
      boolean matched = false;

      for (RoutineNameDeclaration concreteCandidate : implementationMethods) {
        if (interfaceCandidate.getParameters().equals(concreteCandidate.getParameters())) {
          interfaceDeclarations.add(interfaceCandidate);
          concreteDeclarations.add(concreteCandidate);
          matched = true;
          break;
        }
      }

      if (matched) {
        break;
      }
    }

    interfaceMethodResolver.addToSymbolTable();
    concreteMethodResolver.addToSymbolTable();
  }

  public void resolve(PropertyNode property) {
    resolve(property.getParameterListNode());

    TypeNode type = property.getTypeNode();
    if (type != null) {
      resolve(type);
    }

    PropertyReadSpecifierNode read = property.getReadSpecifier();
    if (read != null) {
      NameResolver readResolver = createNameResolver();
      readResolver.readPrimaryExpression(read.getExpression());
      readResolver.disambiguateParameters(property.getParameterTypes());
      readResolver.addToSymbolTable();
    }

    PropertyWriteSpecifierNode write = property.getWriteSpecifier();
    if (write != null) {
      List parameterTypes = new ArrayList<>(property.getParameterTypes());
      parameterTypes.add(property.getType());
      NameResolver writeResolver = createNameResolver();
      writeResolver.readPrimaryExpression(write.getExpression());
      writeResolver.disambiguateParameters(parameterTypes);
      writeResolver.addToSymbolTable();
    }

    PropertyImplementsSpecifierNode impl = property.getImplementsSpecifier();
    if (impl != null) {
      impl.getTypeReferences().stream().map(TypeReferenceNode::getNameNode).forEach(this::resolve);
    }
  }

  public void resolve(RoutineDeclarationNode routine) {
    resolveRoutine(routine);
  }

  public void resolve(RoutineImplementationNode routine) {
    NameResolver resolver = createNameResolver();
    resolver.readRoutineNameInterfaceReference(routine.getNameReferenceNode());

    RoutineScope routineScope = routine.getScope().getEnclosingScope(RoutineScope.class);
    ((RoutineScopeImpl) routineScope).setTypeScope(findTypeScope(resolver));
    resolveRoutine(routine);

    if (!isBareInterfaceRoutineReference(routine, resolver)) {
      resolver.disambiguateParameters(routine.getParameterTypes());
    }

    if (routine.isOperator()) {
      resolver.disambiguateReturnType(routine.getReturnType());
    }

    resolver.disambiguateIsClassInvocable(routine.isClassMethod());
    resolver.addToSymbolTable();

    completeTypeParameterReferences(routine);
  }

  private static boolean isBareInterfaceRoutineReference(
      RoutineNode routine, NameResolver resolver) {
    return routine.getRoutineHeading().getRoutineParametersNode() == null
        && resolver.getDeclarations().size() == 1;
  }

  private void resolveRoutine(RoutineNode routine) {
    SearchMode previousSearchMode = searchMode;
    try {
      searchMode = SearchMode.ROUTINE_HEADING;
      resolve(routine.getRoutineHeading().getRoutineParametersNode());
      resolve(routine.getRoutineHeading().getRoutineReturnType());
    } finally {
      searchMode = previousSearchMode;
    }
  }

  public void resolve(@Nullable RoutineParametersNode parameters) {
    if (parameters != null) {
      resolve(parameters.getFormalParametersList());
    }
  }

  public void resolve(@Nullable RoutineReturnTypeNode returnType) {
    if (returnType != null) {
      resolve(returnType.getTypeNode());
    }
  }

  public void resolve(@Nullable FormalParameterListNode parameterList) {
    if (parameterList != null) {
      parameterList
          .findChildrenOfType(FormalParameterNode.class)
          .forEach(
              parameter -> {
                TypeNode type = parameter.getTypeNode();
                if (type != null) {
                  resolve(type);
                }
              });
    }
  }

  public RoutineNameDeclaration findMethodMember(
      DelphiNode node, Type type, String name, List parameters) {
    return findInvocableMember(node, type, name, parameters, RoutineNameDeclaration.class);
  }

  public PropertyNameDeclaration findPropertyMember(
      DelphiNode node, Type type, String name, List parameters) {
    return findInvocableMember(node, type, name, parameters, PropertyNameDeclaration.class);
  }

  private  T findInvocableMember(
      DelphiNode node, Type type, String name, List parameters, Class declarationType) {
    NameResolver resolver = memberResolver(node, type, name);
    resolver.disambiguateParameters(parameters);
    NameDeclaration resolved = resolver.addResolvedDeclaration();
    if (declarationType.isInstance(resolved)) {
      return declarationType.cast(resolved);
    }
    return null;
  }

  private NameResolver memberResolver(DelphiNode node, Type type, String name) {
    NameOccurrenceImpl implicitOccurrence = new NameOccurrenceImpl(node, name);
    NameResolver resolver = createNameResolver();
    resolver.updateType(type);
    resolver.addName(implicitOccurrence);
    resolver.searchForDeclaration(implicitOccurrence);
    return resolver;
  }

  public void resolve(ExpressionNode expression) {
    if (expression instanceof PrimaryExpressionNode) {
      resolve((PrimaryExpressionNode) expression);
      return;
    }
    resolveSubExpressions(expression);
  }

  public void resolveSubExpressions(ExpressionNode expression) {
    if (expression instanceof AnonymousMethodNode) {
      return;
    }

    for (var descendant : expression.findDescendantsOfType(PrimaryExpressionNode.class)) {
      resolve(descendant);
    }
  }

  @Nullable
  private static DelphiScope findTypeScope(NameResolver resolver) {
    DelphiScope typeScope = null;
    for (NameDeclaration declaration : resolver.getResolvedDeclarations()) {
      if (declaration instanceof TypeNameDeclaration) {
        Type type = ((TypeNameDeclaration) declaration).getType();
        if (type instanceof ScopedType) {
          typeScope = ((ScopedType) type).typeScope();
        }
      }
    }
    return typeScope;
  }

  private static boolean handleAddressOf(PrimaryExpressionNode expression, NameResolver resolver) {
    Node parent = expression.getParent();

    if (parent instanceof UnaryExpressionNode) {
      UnaryExpressionNode unary = (UnaryExpressionNode) parent;
      if (unary.getOperator() == UnaryOperator.ADDRESS) {
        if (!resolver.isExplicitInvocation() && resolver.getApproximateType().isRoutine()) {
          NameResolver clone = new NameResolver(resolver);
          clone.disambiguateAddressOfRoutineReference();
          clone.addToSymbolTable();
        } else {
          resolver.addToSymbolTable();
        }
        return true;
      }
    }

    return false;
  }

  private static boolean handleRoutineReference(
      PrimaryExpressionNode expression, NameResolver resolver) {
    DelphiNode parent = expression.getParent();

    if (parent instanceof UnaryExpressionNode) {
      UnaryExpressionNode unary = (UnaryExpressionNode) parent;
      if (unary.getOperator() == UnaryOperator.ADDRESS) {
        parent = parent.getParent();
      }
    }

    if (parent instanceof AssignmentStatementNode) {
      ExpressionNode assignee = ((AssignmentStatementNode) parent).getAssignee();
      if (expression == assignee) {
        return false;
      }

      if (assignee.getType().isProcedural()) {
        NameResolver clone = new NameResolver(resolver);
        clone.disambiguateRoutineReference((ProceduralType) assignee.getType());
        if (!clone.getDeclarations().isEmpty()) {
          clone.addToSymbolTable();
          return true;
        }
      }
    } else if (parent instanceof RecordExpressionItemNode) {
      resolver.addToSymbolTable();
      return true;
    }

    return false;
  }

  private static boolean handlePascalReturn(
      PrimaryExpressionNode expression, NameResolver resolver) {
    if (expression.getChildren().size() != 1) {
      return false;
    }

    if (!resolver.getDeclarations().stream().allMatch(RoutineNameDeclaration.class::isInstance)) {
      return false;
    }

    Node parent = expression.getParent();
    if (!(parent instanceof AssignmentStatementNode)) {
      return false;
    }

    ExpressionNode assignee = ((AssignmentStatementNode) parent).getAssignee();
    if (expression != assignee) {
      return false;
    }

    Node child = expression.getChild(0);
    if (!(child instanceof NameReferenceNode)) {
      return false;
    }

    NameReferenceNode reference = (NameReferenceNode) child;
    if (reference.nextName() != null) {
      return false;
    }

    String routineReference = reference.getImage();
    DelphiNode node = expression;

    while ((node = node.getFirstParentOfType(RoutineImplementationNode.class)) != null) {
      RoutineNode routine = (RoutineNode) node;
      if (routine.simpleName().equalsIgnoreCase(routineReference)) {
        RoutineNameDeclaration routineDeclaration = routine.getRoutineNameDeclaration();
        resolver.getDeclarations().removeIf(declaration -> declaration != routineDeclaration);
        resolver.addToSymbolTable();
        return true;
      }
    }

    return false;
  }

  private static void completeTypeParameterReferences(RoutineImplementationNode routine) {
    NameReferenceNode reference = routine.getNameReferenceNode().getLastName();
    NameDeclaration declaration = reference.getNameDeclaration();
    if (!(declaration instanceof GenerifiableDeclaration)) {
      return;
    }

    GenericArgumentsNode genericArguments = reference.getGenericArguments();
    if (genericArguments == null) {
      return;
    }

    GenerifiableDeclaration generifiable = (GenerifiableDeclaration) declaration;
    List typeParameters = generifiable.getTypeParameters();
    List typeArguments =
        genericArguments.findChildrenOfType(TypeReferenceNode.class);

    for (int i = 0; i < typeParameters.size(); ++i) {
      TypedDeclaration parameterDeclaration = typeParameters.get(i);
      TypeReferenceNode parameterReference = typeArguments.get(i);

      NameOccurrence occurrence = parameterReference.getNameNode().getNameOccurrence();
      if (occurrence != null) {
        ((NameDeclarationImpl) parameterDeclaration)
            .setForwardDeclaration(occurrence.getNameDeclaration());
      }

      TypeParameterType parameterType = (TypeParameterType) parameterDeclaration.getType();
      Type argumentType = parameterReference.getType();

      while (argumentType.isWeakAlias()) {
        argumentType = ((AliasType) argumentType).aliasedType();
      }

      if (!argumentType.isAlias() && argumentType.isTypeParameter()) {
        ((TypeParameterTypeImpl) argumentType).setFullType(parameterType);
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy