@Nullable
 private static PsiType getTypeFromMapAccess(@NotNull GrReferenceExpression ref) {
   // map access
   GrExpression qualifier = ref.getQualifierExpression();
   if (qualifier instanceof GrReferenceExpression) {
     if (((GrReferenceExpression) qualifier).resolve() instanceof PsiClass) return null;
   }
   if (qualifier != null) {
     PsiType qType = qualifier.getType();
     if (qType instanceof PsiClassType) {
       PsiClassType.ClassResolveResult qResult = ((PsiClassType) qType).resolveGenerics();
       PsiClass clazz = qResult.getElement();
       if (clazz != null) {
         PsiClass mapClass =
             JavaPsiFacade.getInstance(ref.getProject())
                 .findClass(CommonClassNames.JAVA_UTIL_MAP, ref.getResolveScope());
         if (mapClass != null && mapClass.getTypeParameters().length == 2) {
           PsiSubstitutor substitutor =
               TypeConversionUtil.getClassSubstitutor(mapClass, clazz, qResult.getSubstitutor());
           if (substitutor != null) {
             PsiType substituted = substitutor.substitute(mapClass.getTypeParameters()[1]);
             if (substituted != null) {
               return PsiImplUtil.normalizeWildcardTypeByPosition(substituted, ref);
             }
           }
         }
       }
     }
   }
   return null;
 }
 @Nullable
 private static PsiType getTypeFromClassRef(@NotNull GrReferenceExpressionImpl ref) {
   if ("class".equals(ref.getReferenceName())) {
     return TypesUtil.createJavaLangClassType(
         PsiImplUtil.getQualifierType(ref), ref.getProject(), ref.getResolveScope());
   }
   return null;
 }
 @NotNull
 private MethodResolverProcessor createMethodProcessor(
     boolean allVariants,
     @Nullable String name,
     final boolean byShape,
     @Nullable GrExpression upToArgument) {
   final PsiType[] argTypes = PsiUtil.getArgumentTypes(this, false, upToArgument, byShape);
   if (byShape && argTypes != null) {
     for (int i = 0; i < argTypes.length; i++) {
       argTypes[i] = TypeConversionUtil.erasure(argTypes[i]);
     }
   }
   PsiType qualifierType = PsiImplUtil.getQualifierType(this);
   return new MethodResolverProcessor(
       name, this, false, qualifierType, argTypes, getTypeArguments(), allVariants, byShape);
 }
 @NotNull
 private static GroovyResolveResult[] collapseReflectedMethods(GroovyResolveResult[] candidates) {
   Set<GrMethod> visited = ContainerUtil.newHashSet();
   List<GroovyResolveResult> collapsed = ContainerUtil.newArrayList();
   for (GroovyResolveResult result : candidates) {
     PsiElement element = result.getElement();
     if (element instanceof GrReflectedMethod) {
       GrMethod baseMethod = ((GrReflectedMethod) element).getBaseMethod();
       if (visited.add(baseMethod)) {
         collapsed.add(
             PsiImplUtil.reflectedToBase(result, baseMethod, (GrReflectedMethod) element));
       }
     } else {
       collapsed.add(result);
     }
   }
   return collapsed.toArray(new GroovyResolveResult[collapsed.size()]);
 }
 public GrExpression replaceWithExpression(
     @NotNull GrExpression newExpr, boolean removeUnnecessaryParentheses) {
   return PsiImplUtil.replaceExpression(this, newExpr, removeUnnecessaryParentheses);
 }
  @Nullable
  private PsiType getNominalTypeInner(@Nullable PsiElement resolved) {
    if (resolved == null && !"class".equals(getReferenceName())) {
      resolved = resolve();
    }

    if (resolved instanceof PsiClass) {
      final PsiElementFactory factory = JavaPsiFacade.getInstance(getProject()).getElementFactory();
      if (PsiUtil.isInstanceThisRef(this)) {
        final PsiClassType categoryType = GdkMethodUtil.getCategoryType((PsiClass) resolved);
        if (categoryType != null) {
          return categoryType;
        } else {
          return factory.createType((PsiClass) resolved);
        }
      } else if (PsiUtil.isSuperReference(this)) {
        PsiClass contextClass = PsiUtil.getContextClass(this);
        if (GrTraitUtil.isTrait(contextClass)) {
          PsiClassType[] extendsTypes = contextClass.getExtendsListTypes();
          PsiClassType[] implementsTypes = contextClass.getImplementsListTypes();

          PsiClassType[] superTypes =
              ArrayUtil.mergeArrays(implementsTypes, extendsTypes, PsiClassType.ARRAY_FACTORY);

          return PsiIntersectionType.createIntersection(ArrayUtil.reverseArray(superTypes));
        }
        return factory.createType((PsiClass) resolved);
      }
      if (getParent() instanceof GrReferenceExpression) {
        return factory.createType((PsiClass) resolved);
      } else {
        return TypesUtil.createJavaLangClassType(
            factory.createType((PsiClass) resolved), getProject(), getResolveScope());
      }
    }

    if (resolved instanceof GrVariable) {
      return ((GrVariable) resolved).getDeclaredType();
    }

    if (resolved instanceof PsiVariable) {
      return ((PsiVariable) resolved).getType();
    }

    if (resolved instanceof PsiMethod) {
      PsiMethod method = (PsiMethod) resolved;
      if (PropertyUtil.isSimplePropertySetter(method)
          && !method.getName().equals(getReferenceName())) {
        return method.getParameterList().getParameters()[0].getType();
      }

      // 'class' property with explicit generic
      PsiClass containingClass = method.getContainingClass();
      if (containingClass != null
          && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())
          && "getClass".equals(method.getName())) {
        return TypesUtil.createJavaLangClassType(
            PsiImplUtil.getQualifierType(this), getProject(), getResolveScope());
      }

      return PsiUtil.getSmartReturnType(method);
    }

    if (resolved == null) {
      final PsiType fromClassRef = getTypeFromClassRef(this);
      if (fromClassRef != null) {
        return fromClassRef;
      }

      final PsiType fromMapAccess = getTypeFromMapAccess(this);
      if (fromMapAccess != null) {
        return fromMapAccess;
      }

      final PsiType fromSpreadOperator = getTypeFromSpreadOperator(this);
      if (fromSpreadOperator != null) {
        return fromSpreadOperator;
      }
    }

    return null;
  }
  /**
   * 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;

    GrReferenceResolveRunner resolveRunner = new GrReferenceResolveRunner(this);

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

    if (!allVariants) { // search for local variables
      for (GroovyResolveResult candidate : propertyCandidates) {
        final PsiElement element = candidate.getElement();
        if (element instanceof GrVariable
            && !(element instanceof GrField || element instanceof GrBindingVariable)) {
          return propertyCandidates;
        }
      }
    }

    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(ClassHint.RESOLVE_CONTEXT, result.getCurrentFileResolveContext())
                .put(SpreadState.SPREAD_STATE, result.getSpreadState());
        PsiElement element = result.getElement();
        assert element != null;
        methodResolver.execute(element, 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);

    filterOutBindings(allCandidates);

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

    if (!allCandidates.isEmpty()) {
      return allCandidates.toArray(new GroovyResolveResult[allCandidates.size()]);
    }
    return GroovyResolveResult.EMPTY_ARRAY;
  }
  @NotNull
  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) {
      GroovyResolveResult[] results = GrThisReferenceResolver.resolveThisExpression(this);
      if (results != null) {
        return results;
      }
    } else if (nameType == GroovyTokenTypes.kSUPER) {
      GroovyResolveResult[] results = GrSuperReferenceResolver.resolveSuperExpression(this);
      if (results != null) {
        return results;
      }
    }

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

    GroovyResolveResult[] classCandidates = null;

    GrReferenceResolveRunner resolveRunner = new GrReferenceResolveRunner(this);

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

    if (hasAt()) {
      return fieldCandidates;
    }

    boolean canBeClassOrPackage = ResolveUtil.canBeClassOrPackage(this);

    if (canBeClassOrPackage && findClassOrPackageAtFirst()) {
      ResolverProcessor classProcessor = new ClassResolverProcessor(name, this, kinds);
      resolveRunner.resolveImpl(classProcessor);
      classCandidates = classProcessor.getCandidates();
      if (classCandidates.length > 0 && containsPackage(classCandidates)) {
        final PsiElement firstNonReferenceExprParent =
            PsiTreeUtil.skipParentsOfType(this, GrReferenceExpressionImpl.class);
        final GrReferenceExpressionImpl topRef =
            (GrReferenceExpressionImpl)
                PsiTreeUtil.findFirstParent(
                    this,
                    new Condition<PsiElement>() {
                      @Override
                      public boolean value(PsiElement parent) {
                        return parent.getParent() == firstNonReferenceExprParent
                            && parent instanceof GrReferenceExpressionImpl;
                      }
                    });
        if (topRef != null) {
          final String fqn = topRef.getTextSkipWhiteSpaceAndComments();
          if (JavaPsiFacade.getInstance(getProject()).findClass(fqn, getResolveScope()) != null) {
            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 && PsiUtil.getContextClass(this) == containingClass)
          return fieldCandidates;
      } else if (!(element instanceof GrBindingVariable)) {
        return fieldCandidates;
      }
    }

    if (classCandidates != null && classCandidates.length > 0) return classCandidates;

    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,
              PsiImplUtil.getQualifierType(this),
              getTypeArguments());
      resolveRunner.resolveImpl(accessorResolver);
      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);
      }
    }

    final ArrayList<GroovyResolveResult> fieldList = ContainerUtil.newArrayList(fieldCandidates);
    filterOutBindings(fieldList);
    if (!fieldList.isEmpty()) {
      return fieldList.toArray(new GroovyResolveResult[fieldList.size()]);
    }

    if (classCandidates == null && canBeClassOrPackage) {
      ResolverProcessor classProcessor = new ClassResolverProcessor(name, this, kinds);
      resolveRunner.resolveImpl(classProcessor);
      classCandidates = classProcessor.getCandidates();
    }

    if (classCandidates != null && classCandidates.length > 0) return classCandidates;
    if (!accessorResults.isEmpty()) return new GroovyResolveResult[] {accessorResults.get(0)};
    return GroovyResolveResult.EMPTY_ARRAY;
  }