@NotNull
 private static MultiMap<String, GoImportSpec> collectImportsWithSameString(
     @NotNull Collection<GoImportSpec> importsWithSameName) {
   MultiMap<String, GoImportSpec> importsWithSameString = MultiMap.create();
   for (PsiElement duplicateCandidate : importsWithSameName) {
     GoImportSpec importSpec = getImportSpec(duplicateCandidate);
     if (importSpec != null) {
       importsWithSameString.putValue(importSpec.getPath(), importSpec);
     }
   }
   return importsWithSameString;
 }
    @NotNull
    private ExistingImportData cachedImportData(
        @NotNull GoNamedElement element, @Nullable ExistingImportData existingValue) {
      if (existingValue != null) return existingValue;

      GoFile declarationFile = element.getContainingFile();
      String importPath = declarationFile.getImportPath();
      GoImportSpec existingImport = myImportedPackages.get(importPath);

      boolean exists = existingImport != null;
      boolean isDot = exists && existingImport.isDot();
      String alias = existingImport != null ? existingImport.getAlias() : null;
      return new ExistingImportData(exists, isDot, alias, importPath);
    }
 @NotNull
 public static Set<PsiElement> findRedundantImportIdentifiers(
     @NotNull MultiMap<String, GoImportSpec> importMap) {
   Set<PsiElement> importIdentifiersToDelete = ContainerUtil.newLinkedHashSet();
   for (PsiElement importEntry : importMap.values()) {
     GoImportSpec importSpec = getImportSpec(importEntry);
     if (importSpec != null) {
       String localPackageName = importSpec.getLocalPackageName();
       if (!StringUtil.isEmpty(localPackageName)) {
         if (Comparing.equal(importSpec.getAlias(), localPackageName)) {
           importIdentifiersToDelete.add(importSpec.getIdentifier());
         }
       }
     }
   }
   return importIdentifiersToDelete;
 }
  @NotNull
  private static Set<String> sortMatching(
      @NotNull PrefixMatcher matcher, @NotNull Collection<String> names, @NotNull GoFile file) {
    ProgressManager.checkCanceled();
    String prefix = matcher.getPrefix();
    if (prefix.isEmpty()) return ContainerUtil.newLinkedHashSet(names);

    Set<String> packagesWithAliases = ContainerUtil.newHashSet();
    for (Map.Entry<String, Collection<GoImportSpec>> entry : file.getImportMap().entrySet()) {
      for (GoImportSpec spec : entry.getValue()) {
        String alias = spec.getAlias();
        if (spec.isDot() || alias != null) {
          packagesWithAliases.add(entry.getKey());
          break;
        }
      }
    }

    List<String> sorted = ContainerUtil.newArrayList();
    for (String name : names) {
      if (matcher.prefixMatches(name) || packagesWithAliases.contains(substringBefore(name, '.'))) {
        sorted.add(name);
      }
    }

    ProgressManager.checkCanceled();
    Collections.sort(sorted, String.CASE_INSENSITIVE_ORDER);
    ProgressManager.checkCanceled();

    LinkedHashSet<String> result = new LinkedHashSet<String>();
    for (String name : sorted) {
      if (matcher.isStartMatch(name)) {
        result.add(name);
      }
    }

    ProgressManager.checkCanceled();

    result.addAll(sorted);
    return result;
  }
 private static boolean hasImportUsers(@NotNull GoImportSpec spec) {
   //noinspection SynchronizationOnLocalVariableOrMethodParameter
   synchronized (spec) {
     List<PsiElement> list = spec.getUserData(GoReferenceBase.IMPORT_USERS);
     if (list != null) {
       for (PsiElement e : list) {
         if (e.isValid()) {
           return true;
         }
         ProgressManager.checkCanceled();
       }
     }
   }
   return false;
 }
  public static MultiMap<String, GoImportSpec> filterUnusedImports(
      @NotNull PsiFile file, @NotNull MultiMap<String, GoImportSpec> importMap) {
    MultiMap<String, GoImportSpec> result = MultiMap.create();
    result.putAllValues(importMap);
    result.remove("_"); // imports for side effects are always used

    Collection<GoImportSpec> implicitImports = ContainerUtil.newArrayList(result.get("."));
    for (GoImportSpec importEntry : implicitImports) {
      GoImportSpec spec = getImportSpec(importEntry);
      if (spec != null && spec.isDot() && hasImportUsers(spec)) {
        result.remove(".", importEntry);
      }
    }

    file.accept(
        new GoRecursiveVisitor() {
          @Override
          public void visitTypeReferenceExpression(@NotNull GoTypeReferenceExpression o) {
            GoTypeReferenceExpression lastQualifier = o.getQualifier();
            if (lastQualifier != null) {
              GoTypeReferenceExpression previousQualifier;
              while ((previousQualifier = lastQualifier.getQualifier()) != null) {
                lastQualifier = previousQualifier;
              }
              markAsUsed(lastQualifier.getIdentifier(), lastQualifier.getReference());
            }
          }

          @Override
          public void visitReferenceExpression(@NotNull GoReferenceExpression o) {
            GoReferenceExpression lastQualifier = o.getQualifier();
            if (lastQualifier != null) {
              GoReferenceExpression previousQualifier;
              while ((previousQualifier = lastQualifier.getQualifier()) != null) {
                lastQualifier = previousQualifier;
              }
              markAsUsed(lastQualifier.getIdentifier(), lastQualifier.getReference());
            }
          }

          private void markAsUsed(@NotNull PsiElement qualifier, @NotNull PsiReference reference) {
            String qualifierText = qualifier.getText();
            if (!result.containsKey(qualifierText)) {
              // already marked
              return;
            }
            PsiElement resolve = reference.resolve();
            if (!(resolve instanceof PsiDirectory
                || resolve instanceof GoImportSpec
                || resolve instanceof PsiDirectoryContainer)) {
              return;
            }
            Collection<String> qualifiersToDelete = ContainerUtil.newHashSet();
            for (GoImportSpec spec : result.get(qualifierText)) {
              for (Map.Entry<String, Collection<GoImportSpec>> entry : result.entrySet()) {
                for (GoImportSpec importSpec : entry.getValue()) {
                  if (importSpec == spec) {
                    qualifiersToDelete.add(entry.getKey());
                  }
                }
              }
            }
            for (String qualifierToDelete : qualifiersToDelete) {
              result.remove(qualifierToDelete);
            }
          }
        });
    return result;
  }
  public static MultiMap<String, GoImportSpec> filterUnusedImports(
      @NotNull PsiFile file, @NotNull final MultiMap<String, GoImportSpec> importMap) {
    final MultiMap<String, GoImportSpec> result = MultiMap.create();
    result.putAllValues(importMap);
    result.remove("_"); // imports for side effects are always used

    Collection<GoImportSpec> implicitImports = ContainerUtil.newArrayList(result.get("."));
    for (GoImportSpec importEntry : implicitImports) {
      GoImportSpec spec = getImportSpec(importEntry);
      if (spec != null && spec.isDot()) {
        List<? extends PsiElement> list = spec.getUserData(GoReference.IMPORT_USERS);
        if (list != null) {
          for (PsiElement e : list) {
            if (e.isValid()) {
              result.remove(".", importEntry);
            }
          }
        }
      }
    }

    file.accept(
        new GoRecursiveVisitor() {
          @Override
          public void visitTypeReferenceExpression(@NotNull GoTypeReferenceExpression o) {
            GoTypeReferenceExpression lastQualifier = o.getQualifier();
            if (lastQualifier != null) {
              GoTypeReferenceExpression previousQualifier;
              while ((previousQualifier = lastQualifier.getQualifier()) != null) {
                lastQualifier = previousQualifier;
              }
              markAsUsed(lastQualifier.getIdentifier());
            }
          }

          @Override
          public void visitReferenceExpression(@NotNull GoReferenceExpression o) {
            GoReferenceExpression lastQualifier = o.getQualifier();
            if (lastQualifier != null) {
              GoReferenceExpression previousQualifier;
              while ((previousQualifier = lastQualifier.getQualifier()) != null) {
                lastQualifier = previousQualifier;
              }
              markAsUsed(lastQualifier.getIdentifier());
            }
          }

          private void markAsUsed(@NotNull PsiElement qualifier) {
            Collection<String> qualifiersToDelete = ContainerUtil.newHashSet();
            for (GoImportSpec spec : result.get(qualifier.getText())) {
              for (Map.Entry<String, Collection<GoImportSpec>> entry : result.entrySet()) {
                for (GoImportSpec importSpec : entry.getValue()) {
                  if (importSpec == spec) {
                    qualifiersToDelete.add(entry.getKey());
                  }
                }
              }
            }
            for (String qualifierToDelete : qualifiersToDelete) {
              result.remove(qualifierToDelete);
            }
          }
        });
    return result;
  }