Rewrite optimize imports using JetReference#resolveToDescriptors

#KT-4451 Fixed
This commit is contained in:
Pavel V. Talanov
2014-02-17 15:15:46 +04:00
parent 6f2ed9a5a9
commit f5c8557ff7
2 changed files with 24 additions and 194 deletions
@@ -375,7 +375,7 @@ private fun PsiElement.isInCopiedArea(fileCopiedFrom: JetFile, startOffsets: Int
}
}
private val DeclarationDescriptor.importableFqName: FqName?
public val DeclarationDescriptor.importableFqName: FqName?
get() {
if (this is ConstructorDescriptor) return getContainingDeclaration().importableFqName
val mayBeUnsafe = DescriptorUtils.getFqName(this)
@@ -20,24 +20,25 @@ import com.google.common.collect.Lists;
import com.intellij.lang.ImportOptimizer;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressIndicatorProvider;
import com.intellij.psi.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.ImportPath;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.java.JavaResolverPsiUtils;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.lang.resolve.name.NamePackage;
import org.jetbrains.jet.plugin.codeInsight.CodeInsightPackage;
import org.jetbrains.jet.plugin.references.JetReference;
import java.util.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.jetbrains.jet.plugin.quickfix.ImportInsertHelper.needImport;
@@ -128,196 +129,25 @@ public class JetImportOptimizer implements ImportOptimizer {
}
@Override
public void visitReferenceExpression(@NotNull JetReferenceExpression expression) {
if (PsiTreeUtil.getParentOfType(expression, JetImportDirective.class) == null &&
PsiTreeUtil.getParentOfType(expression, JetPackageDirective.class) == null) {
PsiReference reference = expression.getReference();
if (reference != null) {
List<PsiElement> references = new ArrayList<PsiElement>();
PsiElement resolve = reference.resolve();
if (resolve != null) {
references.add(resolve);
}
if (references.isEmpty() && reference instanceof PsiPolyVariantReference) {
for (ResolveResult resolveResult : ((PsiPolyVariantReference)reference).multiResolve(true)) {
references.add(resolveResult.getElement());
}
}
for (PsiElement psiReference : references) {
FqName fqName = getElementUsageFQName(psiReference);
if (fqName != null) {
usedQualifiedNames.add(fqName);
}
public void visitJetElement(@NotNull JetElement element) {
if (PsiTreeUtil.getParentOfType(element, JetImportDirective.class) != null ||
PsiTreeUtil.getParentOfType(element, JetPackageDirective.class) != null) {
return;
}
PsiReference reference = element.getReference();
if (reference instanceof JetReference) {
Collection<DeclarationDescriptor> referencedDescriptors = ((JetReference) reference).resolveToDescriptors();
for (DeclarationDescriptor descriptor : referencedDescriptors) {
FqName importableFqName = CodeInsightPackage.getImportableFqName(descriptor);
if (importableFqName != null) {
usedQualifiedNames.add(importableFqName);
}
}
}
super.visitReferenceExpression(expression);
}
@Override
public void visitForExpression(@NotNull JetForExpression expression) {
BindingContext context = AnalyzerFacadeWithCache.getContextForElement(expression);
ResolvedCall<FunctionDescriptor> resolvedCall = context.get(BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL, expression.getLoopRange());
addResolvedCallFqName(resolvedCall);
super.visitForExpression(expression);
}
@Override
public void visitMultiDeclaration(@NotNull JetMultiDeclaration declaration) {
BindingContext context = AnalyzerFacadeWithCache.getContextForElement(declaration);
List<JetMultiDeclarationEntry> entries = declaration.getEntries();
for (JetMultiDeclarationEntry entry : entries) {
ResolvedCall<FunctionDescriptor> resolvedCall = context.get(BindingContext.COMPONENT_RESOLVED_CALL, entry);
addResolvedCallFqName(resolvedCall);
}
super.visitMultiDeclaration(declaration);
}
private void addResolvedCallFqName(@Nullable ResolvedCall resolvedCall) {
if (resolvedCall != null) {
CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor();
usedQualifiedNames.add(DescriptorUtils.getFqNameSafe(resultingDescriptor));
}
super.visitJetElement(element);
}
});
return usedQualifiedNames;
}
@Nullable
public static FqName getElementUsageFQName(PsiElement element) {
if (element instanceof JetFile) {
return JetPsiUtil.getFQName((JetFile) element);
}
if (element instanceof JetSimpleNameExpression) {
JetPackageDirective packageDirective = PsiTreeUtil.getParentOfType(element, JetPackageDirective.class);
if (packageDirective != null) {
List<JetSimpleNameExpression> simpleNameExpressions = PsiTreeUtil.getChildrenOfTypeAsList(packageDirective, JetSimpleNameExpression.class);
FqName fqName = null;
for (JetSimpleNameExpression nameExpression : simpleNameExpressions) {
Name referencedName = nameExpression.getReferencedNameAsName();
if (fqName == null) {
fqName = new FqName(referencedName.asString());
} else {
fqName = fqName.child(referencedName);
}
if (nameExpression.equals(element)) {
return fqName;
}
}
}
}
if (element instanceof JetNamedDeclaration) {
return JetPsiUtil.getFQName((JetNamedDeclaration) element);
}
if (element instanceof PsiClass) {
String qualifiedName = ((PsiClass) element).getQualifiedName();
if (qualifiedName != null) {
return new FqName(qualifiedName);
}
}
if (element instanceof PsiField) {
PsiField field = (PsiField) element;
FqName classFQN = getFqNameOfContainingClassForPsiMember(field);
if (classFQN == null) {
return null;
}
return combineClassFqNameWithMemberName(field.getContainingClass(), classFQN, field.getName());
}
// TODO: Still problem with kotlin global properties imported from class files
if (element instanceof PsiMethod) {
PsiMethod method = (PsiMethod) element;
FqName classFQN = getFqNameOfContainingClassForPsiMember(method);
if (classFQN == null) {
return null;
}
if (method.isConstructor()) {
return classFQN;
}
return combineClassFqNameWithMemberName(method.getContainingClass(), classFQN, method.getName());
}
if (element instanceof PsiPackage) {
return new FqName(((PsiPackage) element).getQualifiedName());
}
return null;
}
@Nullable
private static FqName combineClassFqNameWithMemberName(PsiClass containingClass, FqName classFQN, String memberName) {
if (memberName == null) {
return null;
}
if (JavaResolverPsiUtils.isCompiledKotlinPackageClass(containingClass)) {
return classFQN.parent().child(Name.identifier(memberName));
}
else {
return classFQN.child(Name.identifier(memberName));
}
}
@Nullable
private static FqName getFqNameOfContainingClassForPsiMember(PsiMember member) {
PsiClass containingClass = member.getContainingClass();
if (containingClass != null) {
String classFQNStr = containingClass.getQualifiedName();
if (classFQNStr != null) {
return new FqName(classFQNStr);
}
}
return null;
}
private static PsiElement getWithPreviousWhitespaces(PsiElement element) {
PsiElement result = element;
PsiElement siblingIterator = element.getPrevSibling();
while (siblingIterator != null) {
if (siblingIterator.getNode().getElementType() != TokenType.WHITE_SPACE) {
break;
}
else {
result = siblingIterator;
}
siblingIterator = siblingIterator.getPrevSibling();
}
return result;
}
private static PsiElement getWithFollowedWhitespaces(PsiElement element) {
PsiElement result = element;
PsiElement siblingIterator = element.getNextSibling();
while (siblingIterator != null) {
if (siblingIterator.getNode().getElementType() != TokenType.WHITE_SPACE) {
break;
}
else {
result = siblingIterator;
}
siblingIterator = siblingIterator.getNextSibling();
}
return result;
}
}