private static boolean processSuperTypes(
     @NotNull PsiClass aClass,
     @NotNull PsiScopeProcessor processor,
     @Nullable Set<PsiClass> visited,
     PsiElement last,
     @NotNull PsiElement place,
     @NotNull ResolveState state,
     boolean isRaw,
     @NotNull PsiElementFactory factory,
     @NotNull LanguageLevel languageLevel) {
   boolean resolved = false;
   for (final PsiClassType superType : aClass.getSuperTypes()) {
     final PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics();
     PsiClass superClass = superTypeResolveResult.getElement();
     if (superClass == null) continue;
     PsiSubstitutor finalSubstitutor =
         obtainFinalSubstitutor(
             superClass,
             superTypeResolveResult.getSubstitutor(),
             aClass,
             state.get(PsiSubstitutor.KEY),
             factory,
             languageLevel);
     if (aClass instanceof PsiTypeParameter
         && PsiUtil.isRawSubstitutor(superClass, finalSubstitutor)) {
       finalSubstitutor = PsiSubstitutor.EMPTY;
     }
     if (!processDeclarationsInClass(
         superClass,
         processor,
         state.put(PsiSubstitutor.KEY, finalSubstitutor),
         visited,
         last,
         place,
         isRaw)) {
       resolved = true;
     }
   }
   return !resolved;
 }
  public PsiElement resolve() {
    final String varName = getName();
    if (varName == null) {
      return null;
    }

    BashVarProcessor processor = new BashVarProcessor(this, true);
    if (!BashPsiUtils.varResolveTreeWalkUp(
        processor, this, getContainingFile(), ResolveState.initial())) {
      return processor.getBestResult(false, this);
    }

    return null;
  }
  private PsiElement internalResolve() {
    final String referencedName = getReferencedName();
    if (referencedName == null) {
      return null;
    }

    final BashFunctionProcessor processor = new BashFunctionProcessor(referencedName);

    boolean walkOn =
        PsiTreeUtil.treeWalkUp(processor, this, getContainingFile(), ResolveState.initial());
    if (!walkOn) {
      return processor.hasResults() ? processor.getBestResult(true, this) : null;
    }

    return null;
  }
  @org.jetbrains.annotations.NotNull
  public Object[] getVariants() {
    List<Object> variants = Lists.newArrayList();

    BashFunctionVariantsProcessor processor = new BashFunctionVariantsProcessor();
    PsiTreeUtil.treeWalkUp(processor, this, this.getContainingFile(), ResolveState.initial());

    variants.addAll(processor.getFunctionDefs());

    if (BashProjectSettings.storedSettings(getProject()).isAutocompleteBuiltinCommands()) {
      variants.addAll(LanguageBuiltins.commands);
    }

    if (BashProjectSettings.storedSettings(getProject()).isSupportBash4()) {
      variants.addAll(LanguageBuiltins.commands_v4);
    }

    return variants.toArray();
  }
  public static boolean processDeclarationsInClass(
      @NotNull PsiClass aClass,
      @NotNull final PsiScopeProcessor processor,
      @NotNull ResolveState state,
      @Nullable Set<PsiClass> visited,
      PsiElement last,
      @NotNull PsiElement place,
      boolean isRaw) {
    if (last instanceof PsiTypeParameterList || last instanceof PsiModifierList) {
      return true; // TypeParameterList and ModifierList do not see our declarations
    }
    if (visited != null && visited.contains(aClass)) return true;

    PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
    isRaw = isRaw || PsiUtil.isRawSubstitutor(aClass, substitutor);

    ParameterizedCachedValue<MembersMap, PsiClass> cache =
        getValues(aClass); // aClass.getUserData(MAP_IN_CLASS_KEY);
    boolean upToDate = cache.hasUpToDateValue();
    LanguageLevel languageLevel = PsiUtil.getLanguageLevel(place);
    if (
    /*true || */ upToDate) {
      final NameHint nameHint = processor.getHint(NameHint.KEY);
      if (nameHint != null) {
        String name = nameHint.getName(state);
        return processCachedMembersByName(
            aClass,
            processor,
            state,
            visited,
            last,
            place,
            isRaw,
            substitutor,
            cache.getValue(aClass),
            name,
            languageLevel);
      }
    }
    return processDeclarationsInClassNotCached(
        aClass, processor, state, visited, last, place, isRaw, languageLevel);
  }
  @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;
  }
  @NotNull
  public Object[] getVariants() {
    List<Object> variants = Lists.newArrayList();

    // collect the previously declared variables
    final BashVarVariantsProcessor processor = new BashVarVariantsProcessor(this);
    PsiTreeUtil.treeWalkUp(processor, this, this.getContainingFile(), ResolveState.initial());

    variants.addAll(createPsiItems(processor.getVariables()));

    if (BashProjectSettings.storedSettings(getProject()).isAutocompleteBuiltinVars()) {
      variants.addAll(createItems(LanguageBuiltins.bashShellVars, BashIcons.BASH_VAR_ICON));
      variants.addAll(createItems(LanguageBuiltins.bourneShellVars, BashIcons.BOURNE_VAR_ICON));
    }

    if (BashProjectSettings.storedSettings(getProject()).isAutcompleteGlobalVars()) {
      variants.addAll(
          createItems(
              BashProjectSettings.storedSettings(getProject()).getGlobalVariables(),
              BashIcons.GLOBAL_VAR_ICON));
    }

    return variants.toArray();
  }
  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);
  }
  private static boolean processCachedMembersByName(
      @NotNull PsiClass aClass,
      @NotNull PsiScopeProcessor processor,
      @NotNull ResolveState state,
      @Nullable Set<PsiClass> visited,
      PsiElement last,
      @NotNull PsiElement place,
      boolean isRaw,
      @NotNull PsiSubstitutor substitutor,
      @NotNull MembersMap value,
      String name,
      @NotNull LanguageLevel languageLevel) {
    final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);

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

    if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.FIELD)) {
      final PsiField fieldByName = aClass.findFieldByName(name, false);
      if (fieldByName != null) {
        processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
        if (!processor.execute(fieldByName, state)) return false;
      } else {
        final Map<String, List<Pair<PsiMember, PsiSubstitutor>>> allFieldsMap =
            value.get(MemberType.FIELD);

        final List<Pair<PsiMember, PsiSubstitutor>> list = allFieldsMap.get(name);
        if (list != null) {
          for (final Pair<PsiMember, PsiSubstitutor> candidate : list) {
            PsiMember candidateField = candidate.getFirst();
            PsiSubstitutor finalSubstitutor =
                obtainFinalSubstitutor(
                    candidateField.getContainingClass(),
                    candidate.getSecond(),
                    aClass,
                    substitutor,
                    factory,
                    languageLevel);

            processor.handleEvent(
                PsiScopeProcessor.Event.SET_DECLARATION_HOLDER,
                candidateField.getContainingClass());
            if (!processor.execute(candidateField, state.put(PsiSubstitutor.KEY, finalSubstitutor)))
              return false;
          }
        }
      }
    }
    if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) {
      if (last != null && last.getParent() == aClass) {
        if (last instanceof PsiClass) {
          if (!processor.execute(last, state)) return false;
        }
        // Parameters
        final PsiTypeParameterList list = aClass.getTypeParameterList();
        if (list != null && !list.processDeclarations(processor, state, last, place)) return false;
      }
      if (!(last instanceof PsiReferenceList)) {
        final PsiClass classByName = aClass.findInnerClassByName(name, false);
        if (classByName != null) {
          processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
          if (!processor.execute(classByName, state)) return false;
        } else {
          Map<String, List<Pair<PsiMember, PsiSubstitutor>>> allClassesMap =
              value.get(MemberType.CLASS);

          List<Pair<PsiMember, PsiSubstitutor>> list = allClassesMap.get(name);
          if (list != null) {
            for (final Pair<PsiMember, PsiSubstitutor> candidate : list) {
              PsiMember inner = candidate.getFirst();
              PsiClass containingClass = inner.getContainingClass();
              if (containingClass != null) {
                PsiSubstitutor finalSubstitutor =
                    obtainFinalSubstitutor(
                        containingClass,
                        candidate.getSecond(),
                        aClass,
                        substitutor,
                        factory,
                        languageLevel);
                processor.handleEvent(
                    PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
                if (!processor.execute(inner, state.put(PsiSubstitutor.KEY, finalSubstitutor)))
                  return false;
              }
            }
          }
        }
      }
    }
    if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) {
      if (processor instanceof MethodResolverProcessor) {
        final MethodResolverProcessor methodResolverProcessor = (MethodResolverProcessor) processor;
        if (methodResolverProcessor.isConstructor()) {
          final PsiMethod[] constructors = aClass.getConstructors();
          methodResolverProcessor.handleEvent(
              PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
          for (PsiMethod constructor : constructors) {
            if (!methodResolverProcessor.execute(constructor, state)) return false;
          }
          return true;
        }
      }
      Map<String, List<Pair<PsiMember, PsiSubstitutor>>> allMethodsMap =
          value.get(MemberType.METHOD);
      List<Pair<PsiMember, PsiSubstitutor>> list = allMethodsMap.get(name);
      if (list != null) {
        for (final Pair<PsiMember, PsiSubstitutor> candidate : list) {
          ProgressIndicatorProvider.checkCanceled();
          PsiMethod candidateMethod = (PsiMethod) candidate.getFirst();
          if (processor instanceof MethodResolverProcessor) {
            if (candidateMethod.isConstructor()
                != ((MethodResolverProcessor) processor).isConstructor()) continue;
          }
          final PsiClass containingClass = candidateMethod.getContainingClass();
          if (visited != null && visited.contains(candidateMethod.getContainingClass())) {
            continue;
          }

          PsiSubstitutor finalSubstitutor =
              obtainFinalSubstitutor(
                  containingClass,
                  candidate.getSecond(),
                  aClass,
                  substitutor,
                  factory,
                  languageLevel);
          finalSubstitutor = checkRaw(isRaw, factory, candidateMethod, finalSubstitutor);
          processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
          if (!processor.execute(candidateMethod, state.put(PsiSubstitutor.KEY, finalSubstitutor)))
            return false;
        }

        if (visited != null) {
          for (Pair<PsiMember, PsiSubstitutor> aList : list) {
            visited.add(aList.getFirst().getContainingClass());
          }
        }
      }
    }
    return true;
  }