@NotNull
  private static MembersMap buildAllMaps(@NotNull PsiClass psiClass) {
    final List<Pair<PsiMember, PsiSubstitutor>> classes =
        new ArrayList<Pair<PsiMember, PsiSubstitutor>>();
    final List<Pair<PsiMember, PsiSubstitutor>> fields =
        new ArrayList<Pair<PsiMember, PsiSubstitutor>>();
    final List<Pair<PsiMember, PsiSubstitutor>> methods =
        new ArrayList<Pair<PsiMember, PsiSubstitutor>>();

    FilterScopeProcessor<MethodCandidateInfo> processor =
        new FilterScopeProcessor<MethodCandidateInfo>(
            new OrFilter(
                ElementClassFilter.METHOD, ElementClassFilter.FIELD, ElementClassFilter.CLASS)) {
          @Override
          protected void add(PsiElement element, PsiSubstitutor substitutor) {
            if (element instanceof PsiMethod) {
              methods.add(Pair.create((PsiMember) element, substitutor));
            } else if (element instanceof PsiField) {
              fields.add(Pair.create((PsiMember) element, substitutor));
            } else if (element instanceof PsiClass) {
              classes.add(Pair.create((PsiMember) element, substitutor));
            }
          }
        };
    processDeclarationsInClassNotCached(
        psiClass,
        processor,
        ResolveState.initial(),
        null,
        null,
        psiClass,
        false,
        PsiUtil.getLanguageLevel(psiClass));

    MembersMap result = new MembersMap(MemberType.class);
    result.put(MemberType.CLASS, generateMapByList(classes));
    result.put(MemberType.METHOD, generateMapByList(methods));
    result.put(MemberType.FIELD, generateMapByList(fields));
    return result;
  }
  private static boolean processDeclarationsInClassNotCached(
      @NotNull PsiClass aClass,
      @NotNull PsiScopeProcessor processor,
      @NotNull ResolveState state,
      @Nullable Set<PsiClass> visited,
      PsiElement last,
      @NotNull PsiElement place,
      boolean isRaw,
      @NotNull LanguageLevel languageLevel) {
    if (visited == null) visited = new THashSet<PsiClass>();
    if (!visited.add(aClass)) return true;
    processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
    final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
    final NameHint nameHint = processor.getHint(NameHint.KEY);

    if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.FIELD)) {
      if (nameHint != null) {
        final PsiField fieldByName = aClass.findFieldByName(nameHint.getName(state), false);
        if (fieldByName != null && !processor.execute(fieldByName, state)) return false;
      } else {
        final PsiField[] fields = aClass.getFields();
        for (final PsiField field : fields) {
          if (!processor.execute(field, state)) return false;
        }
      }
    }

    PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory();

    if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) {
      PsiSubstitutor baseSubstitutor = state.get(PsiSubstitutor.KEY);
      final PsiMethod[] methods =
          nameHint != null
              ? aClass.findMethodsByName(nameHint.getName(state), false)
              : aClass.getMethods();
      for (final PsiMethod method : methods) {
        PsiSubstitutor finalSubstitutor = checkRaw(isRaw, factory, method, baseSubstitutor);
        ResolveState methodState =
            finalSubstitutor == baseSubstitutor
                ? state
                : state.put(PsiSubstitutor.KEY, finalSubstitutor);
        if (!processor.execute(method, methodState)) return false;
      }
    }

    if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) {
      if (last != null && last.getParent() == aClass) {
        // Parameters
        final PsiTypeParameterList list = aClass.getTypeParameterList();
        if (list != null
            && !list.processDeclarations(processor, ResolveState.initial(), last, place))
          return false;
      }

      if (!(last instanceof PsiReferenceList) && !(last instanceof PsiModifierList)) {
        // Inners
        if (nameHint != null) {
          final PsiClass inner = aClass.findInnerClassByName(nameHint.getName(state), false);
          if (inner != null) {
            if (!processor.execute(inner, state)) return false;
          }
        } else {
          final PsiClass[] inners = aClass.getInnerClasses();
          for (final PsiClass inner : inners) {
            if (!processor.execute(inner, state)) return false;
          }
        }
      }
    }

    return last instanceof PsiReferenceList
        || processSuperTypes(
            aClass, processor, visited, last, place, state, isRaw, factory, languageLevel);
  }
  /**
   * 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;
  }