@NotNull
  private GroovyResolveResult[] doPolyResolve(boolean incompleteCode, boolean genericsMatter) {
    String name = getReferenceName();
    if (name == null) return GroovyResolveResult.EMPTY_ARRAY;

    if (incompleteCode) {
      ResolverProcessor processor = CompletionProcessor.createRefSameNameProcessor(this, name);
      GrReferenceResolveUtil.resolveImpl(processor, this);
      GroovyResolveResult[] propertyCandidates = processor.getCandidates();
      if (propertyCandidates.length > 0) return propertyCandidates;
    }

    try {
      ResolveProfiler.start();
      switch (getKind()) {
        case METHOD_OR_PROPERTY:
          return resolveMethodOrProperty(false, null, genericsMatter);
        case TYPE_OR_PROPERTY:
          return resolveTypeOrProperty();
        case METHOD_OR_PROPERTY_OR_TYPE:
          GroovyResolveResult[] results = resolveMethodOrProperty(false, null, genericsMatter);
          if (results.length == 0) results = resolveTypeOrProperty();
          return results;
        default:
          return GroovyResolveResult.EMPTY_ARRAY;
      }
    } finally {
      final long time = ResolveProfiler.finish();
      ResolveProfiler.write("ref " + getText() + " " + hashCode() + " : " + time);
    }
  }
  private void processMethods(final MethodResolverProcessor methodResolver) {
    GrReferenceResolveUtil.resolveImpl(methodResolver, this);
    if (methodResolver.hasApplicableCandidates()) {
      return;
    }

    // Search in ClosureMissingMethodContributor
    if (!isQualified() && getContext() instanceof GrMethodCall) {
      for (PsiElement e = this.getContext(); e != null; e = e.getContext()) {
        if (e instanceof GrClosableBlock) {
          ResolveState state = ResolveState.initial().put(ResolverProcessor.RESOLVE_CONTEXT, e);
          for (ClosureMissingMethodContributor contributor :
              ClosureMissingMethodContributor.EP_NAME.getExtensions()) {
            if (!contributor.processMembers((GrClosableBlock) e, methodResolver, this, state)) {
              return;
            }
          }
        }
      }
    }
  }
  /**
   * priority: inside class C: local variable, c.method, c.property, c.getter in other places: local
   * variable, c.method, c.getter, c.property
   */
  @NotNull
  private GroovyResolveResult[] resolveMethodOrProperty(
      boolean allVariants, @Nullable GrExpression upToArgument, boolean genericsMatter) {
    final String name = getReferenceName();
    if (name == null) return GroovyResolveResult.EMPTY_ARRAY;

    PropertyResolverProcessor propertyResolver = new PropertyResolverProcessor(name, this);
    GrReferenceResolveUtil.resolveImpl(propertyResolver, this);
    final GroovyResolveResult[] propertyCandidates = propertyResolver.getCandidates();

    if (!allVariants) { // search for local variables
      for (GroovyResolveResult candidate : propertyCandidates) {
        if (candidate.getElement() instanceof GrVariable
            && !(candidate.getElement() instanceof GrField)) {
          return propertyResolver.getCandidates();
        }
      }
    }

    final Pair<Boolean, GroovyResolveResult[]> shapeResults =
        resolveByShape(allVariants, upToArgument);
    if (!genericsMatter && !allVariants && shapeResults.first) {
      assertAllAreValid(shapeResults.second);
      return shapeResults.second;
    }

    MethodResolverProcessor methodResolver = null;
    if (genericsMatter) {
      methodResolver = createMethodProcessor(allVariants, name, false, upToArgument);

      for (GroovyResolveResult result : shapeResults.second) {
        final ResolveState state =
            ResolveState.initial()
                .put(PsiSubstitutor.KEY, result.getSubstitutor())
                .put(ResolverProcessor.RESOLVE_CONTEXT, result.getCurrentFileResolveContext())
                .put(SpreadState.SPREAD_STATE, result.getSpreadState());
        methodResolver.execute(result.getElement(), state);
      }

      if (!allVariants && methodResolver.hasApplicableCandidates()) {
        return methodResolver.getCandidates();
      }
    }

    // search for fields inside its class
    if (!allVariants) {
      for (GroovyResolveResult candidate : propertyCandidates) {
        final PsiElement element = candidate.getElement();
        if (element instanceof GrField) {
          final PsiClass containingClass = ((PsiField) element).getContainingClass();
          if (containingClass != null && PsiTreeUtil.isContextAncestor(containingClass, this, true))
            return propertyCandidates;
        }
      }
    }

    List<GroovyResolveResult> allCandidates = new ArrayList<GroovyResolveResult>();
    ContainerUtil.addAll(allCandidates, propertyCandidates);
    ContainerUtil.addAll(
        allCandidates, genericsMatter ? methodResolver.getCandidates() : shapeResults.second);

    // search for getters
    for (String getterName : GroovyPropertyUtils.suggestGettersName(name)) {
      AccessorResolverProcessor getterResolver =
          new AccessorResolverProcessor(
              getterName,
              name,
              this,
              true,
              genericsMatter,
              GrReferenceResolveUtil.getQualifierType(this),
              getTypeArguments());
      GrReferenceResolveUtil.resolveImpl(getterResolver, this);
      final GroovyResolveResult[] candidates =
          getterResolver.getCandidates(); // can be only one candidate
      if (!allVariants && candidates.length == 1) {
        return candidates;
      }
      ContainerUtil.addAll(allCandidates, candidates);
    }

    if (allCandidates.size() > 0) {
      return allCandidates.toArray(new GroovyResolveResult[allCandidates.size()]);
    }
    return GroovyResolveResult.EMPTY_ARRAY;
  }
  private GroovyResolveResult[] resolveTypeOrPropertyInner() {
    PsiElement nameElement = getReferenceNameElement();
    String name = getReferenceName();

    if (name == null || nameElement == null) return GroovyResolveResult.EMPTY_ARRAY;

    IElementType nameType = nameElement.getNode().getElementType();
    if (nameType == GroovyTokenTypes.kTHIS) {
      ArrayList<GroovyResolveResult> results = new ArrayList<GroovyResolveResult>();
      if (GrReferenceResolveUtil.resolveThisExpression(this, results)) {
        return results.toArray(new GroovyResolveResult[results.size()]);
      }
    } else if (nameType == GroovyTokenTypes.kSUPER) {
      ArrayList<GroovyResolveResult> results = new ArrayList<GroovyResolveResult>();
      if (GrReferenceResolveUtil.resolveSuperExpression(this, results)) {
        return results.toArray(new GroovyResolveResult[results.size()]);
      }
    }

    EnumSet<ClassHint.ResolveKind> kinds =
        getParent() instanceof GrReferenceExpression
            ? ResolverProcessor.RESOLVE_KINDS_CLASS_PACKAGE
            : ResolverProcessor.RESOLVE_KINDS_CLASS;

    GroovyResolveResult[] classCandidates = null;

    ResolverProcessor processor = new PropertyResolverProcessor(name, this);
    GrReferenceResolveUtil.resolveImpl(processor, this);
    final GroovyResolveResult[] fieldCandidates = processor.getCandidates();

    if (hasAt()) {
      return fieldCandidates;
    }

    boolean canBeClassOrPackage = ResolveUtil.canBeClassOrPackage(this);

    if (canBeClassOrPackage && findClassOrPackageAtFirst()) {
      boolean preferVar = containsLocalVar(fieldCandidates);
      if (!preferVar) {
        ResolverProcessor classProcessor = new ClassResolverProcessor(name, this, kinds);
        GrReferenceResolveUtil.resolveImpl(classProcessor, this);
        classCandidates = classProcessor.getCandidates();
        if (classCandidates.length > 0) return classCandidates;
      }
    }

    // if reference expression is in class we need to return field instead of accessor method
    for (GroovyResolveResult candidate : fieldCandidates) {
      final PsiElement element = candidate.getElement();
      if (element instanceof PsiField) {
        final PsiClass containingClass = ((PsiField) element).getContainingClass();
        if (containingClass != null && PsiTreeUtil.isContextAncestor(containingClass, this, true))
          return fieldCandidates;
      } else {
        return fieldCandidates;
      }
    }

    final boolean isLValue = PsiUtil.isLValue(this);
    String[] accessorNames =
        isLValue
            ? GroovyPropertyUtils.suggestSettersName(name)
            : GroovyPropertyUtils.suggestGettersName(name);
    List<GroovyResolveResult> accessorResults = new ArrayList<GroovyResolveResult>();
    for (String accessorName : accessorNames) {
      AccessorResolverProcessor accessorResolver =
          new AccessorResolverProcessor(
              accessorName,
              name,
              this,
              !isLValue,
              false,
              GrReferenceResolveUtil.getQualifierType(this),
              getTypeArguments());
      GrReferenceResolveUtil.resolveImpl(accessorResolver, this);
      final GroovyResolveResult[] candidates = accessorResolver.getCandidates();

      // can be only one correct candidate or some incorrect
      if (candidates.length == 1 && candidates[0].isStaticsOK() && candidates[0].isAccessible()) {
        return candidates;
      } else {
        ContainerUtil.addAll(accessorResults, candidates);
      }
    }
    if (fieldCandidates.length > 0) return fieldCandidates;
    if (classCandidates == null && canBeClassOrPackage) {
      ResolverProcessor classProcessor = new ClassResolverProcessor(name, this, kinds);
      GrReferenceResolveUtil.resolveImpl(classProcessor, this);
      classCandidates = classProcessor.getCandidates();
    }
    if (classCandidates != null && classCandidates.length > 0) return classCandidates;
    if (accessorResults.size() > 0) return new GroovyResolveResult[] {accessorResults.get(0)};
    return GroovyResolveResult.EMPTY_ARRAY;
  }