Resolve to packages obscured by inaccessible class

This commit is contained in:
Roman Shevchenko
2013-01-30 16:59:54 +04:00
parent 747ba28f0f
commit 24e17b7bc2
7 changed files with 111 additions and 75 deletions
@@ -31,6 +31,7 @@ import com.intellij.psi.impl.DebugUtil;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.impl.source.resolve.ClassResolverProcessor;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.impl.source.resolve.VariableResolverProcessor;
import com.intellij.psi.impl.source.tree.*;
@@ -345,16 +346,7 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
}
}
if (result.length > 0 && result[0].getElement() instanceof PsiClass) {
PsiType[] parameters = ((PsiJavaCodeReferenceElement)ref).getTypeParameters();
for (int i = 0; i < result.length; i++) {
CandidateInfo resolveResult = (CandidateInfo)result[i];
PsiElement resultElement = resolveResult.getElement();
if (resultElement instanceof PsiClass && ((PsiClass)resultElement).hasTypeParameters()) {
result[i] = new CandidateInfo(resolveResult, resolveResult.getSubstitutor().putAll((PsiClass)resultElement, parameters));
}
}
}
JavaResolveUtil.substituteResults((PsiJavaCodeReferenceElement)ref, result);
return result;
}
@@ -449,7 +441,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
final PsiElement classNameElement = getReferenceNameElement();
if (!(classNameElement instanceof PsiIdentifier)) return JavaResolveResult.EMPTY_ARRAY;
final String className = classNameElement.getText();
final ClassResolverProcessor processor = new ClassResolverProcessor(className, this);
PsiScopesUtil.resolveAndWalk(processor, this, null);
return processor.getResult();
@@ -464,18 +455,21 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
}
return new JavaResolveResult[]{new CandidateInfo(aPackage, PsiSubstitutor.EMPTY)};
}
case CLASS_FQ_OR_PACKAGE_NAME_KIND: {
JavaResolveResult[] result = resolve(CLASS_FQ_NAME_KIND);
if (result.length == 0) {
result = resolve(PACKAGE_NAME_KIND);
}
return result;
}
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
case CLASS_OR_PACKAGE_NAME_KIND: {
JavaResolveResult[] result = resolve(CLASS_NAME_KIND);
if (result.length == 0) {
int classKind = kind == CLASS_OR_PACKAGE_NAME_KIND ? CLASS_NAME_KIND : CLASS_FQ_NAME_KIND;
JavaResolveResult[] result = resolve(classKind);
if (result.length == 1 && !result[0].isAccessible()) {
JavaResolveResult[] packageResult = resolve(PACKAGE_NAME_KIND);
if (packageResult.length != 0) {
result = packageResult;
}
}
else if (result.length == 0) {
result = resolve(PACKAGE_NAME_KIND);
}
return result;
}
}
@@ -23,6 +23,7 @@ import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.InheritanceUtil;
@@ -216,4 +217,17 @@ public class JavaResolveUtil {
return true;
}
public static void substituteResults(@NotNull PsiJavaCodeReferenceElement ref, @NotNull JavaResolveResult[] result) {
if (result.length > 0 && result[0].getElement() instanceof PsiClass) {
PsiType[] parameters = ref.getTypeParameters();
for (int i = 0; i < result.length; i++) {
CandidateInfo resolveResult = (CandidateInfo)result[i];
PsiElement resultElement = resolveResult.getElement();
if (resultElement instanceof PsiClass && ((PsiClass)resultElement).hasTypeParameters()) {
result[i] = new CandidateInfo(resolveResult, resolveResult.getSubstitutor().putAll((PsiClass)resultElement, parameters));
}
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,10 +34,7 @@ import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.impl.source.SourceJavaCodeReference;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.impl.source.resolve.ClassResolverProcessor;
import com.intellij.psi.impl.source.resolve.JavaResolveCache;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.impl.source.resolve.VariableResolverProcessor;
import com.intellij.psi.impl.source.resolve.*;
import com.intellij.psi.impl.source.tree.*;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.scope.ElementClassFilter;
@@ -187,59 +184,46 @@ public class PsiReferenceExpressionImpl extends PsiReferenceExpressionBase imple
private static final class OurGenericsResolver implements ResolveCache.PolyVariantResolver<PsiJavaReference> {
private static final OurGenericsResolver INSTANCE = new OurGenericsResolver();
@NotNull
private static JavaResolveResult[] _resolve(boolean incompleteCode, PsiReferenceExpressionImpl expression) {
CompositeElement treeParent = expression.getTreeParent();
IElementType parentType = treeParent == null ? null : treeParent.getElementType();
expression.resolveAllQualifiers();
final JavaResolveResult[] result = expression.resolve(parentType);
if (incompleteCode && parentType != JavaElementType.REFERENCE_EXPRESSION && result.length == 0) {
return expression.resolve(JavaElementType.REFERENCE_EXPRESSION);
}
return result;
}
@Override
@NotNull
public JavaResolveResult[] resolve(@NotNull PsiJavaReference ref, boolean incompleteCode) {
final JavaResolveResult[] result = _resolve(incompleteCode, (PsiReferenceExpressionImpl)ref);
if (result.length > 0 && result[0].getElement() instanceof PsiClass) {
final PsiType[] parameters = ((PsiJavaCodeReferenceElement)ref).getTypeParameters();
final JavaResolveResult[] newResult = new JavaResolveResult[result.length];
for (int i = 0; i < result.length; i++) {
final CandidateInfo resolveResult = (CandidateInfo)result[i];
newResult[i] = new CandidateInfo(resolveResult, resolveResult.getSubstitutor().putAll(
(PsiClass)resolveResult.getElement(), parameters));
}
return newResult;
PsiReferenceExpressionImpl expression = (PsiReferenceExpressionImpl)ref;
CompositeElement treeParent = expression.getTreeParent();
IElementType parentType = treeParent == null ? null : treeParent.getElementType();
resolveAllQualifiers(expression);
JavaResolveResult[] result = expression.resolve(parentType);
if (result.length == 0 && incompleteCode && parentType != JavaElementType.REFERENCE_EXPRESSION) {
result = expression.resolve(JavaElementType.REFERENCE_EXPRESSION);
}
JavaResolveUtil.substituteResults(expression, result);
return result;
}
}
private void resolveAllQualifiers() {
// to avoid SOE, resolve all qualifiers starting from the innermost
PsiElement qualifier = getQualifier();
if (qualifier == null) return;
final ResolveCache resolveCache = ResolveCache.getInstance(getProject());
qualifier.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
if (!(expression instanceof PsiReferenceExpressionImpl) || resolveCache.isCached(expression, true, false, true)) {
return;
private static void resolveAllQualifiers(PsiReferenceExpressionImpl expression) {
// to avoid SOE, resolve all qualifiers starting from the innermost
PsiElement qualifier = expression.getQualifier();
if (qualifier == null) return;
final ResolveCache resolveCache = ResolveCache.getInstance(expression.getProject());
qualifier.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
if (!(expression instanceof PsiReferenceExpressionImpl) || resolveCache.isCached(expression, true, false, true)) {
return;
}
visitElement(expression);
}
visitElement(expression);
}
@Override
protected void elementFinished(PsiElement element) {
if (!(element instanceof PsiReferenceExpressionImpl)) return;
PsiReferenceExpressionImpl expression = (PsiReferenceExpressionImpl)element;
resolveCache.resolveWithCaching(expression, OurGenericsResolver.INSTANCE, false, false);
}
});
@Override
protected void elementFinished(PsiElement element) {
if (!(element instanceof PsiReferenceExpressionImpl)) return;
PsiReferenceExpressionImpl expression = (PsiReferenceExpressionImpl)element;
resolveCache.resolveWithCaching(expression, INSTANCE, false, false);
}
});
}
}
@NotNull
@@ -250,21 +234,33 @@ public class PsiReferenceExpressionImpl extends PsiReferenceExpressionBase imple
return result;
}
final PsiElement classNameElement = getReferenceNameElement();
if (!(classNameElement instanceof PsiIdentifier)) return JavaResolveResult.EMPTY_ARRAY;
result = resolveToClass(classNameElement);
if (result.length > 0) {
return result;
PsiElement classNameElement = getReferenceNameElement();
if (!(classNameElement instanceof PsiIdentifier)) {
return JavaResolveResult.EMPTY_ARRAY;
}
return resolveToPackage();
result = resolveToClass(classNameElement);
if (result.length == 1 && !result[0].isAccessible()) {
JavaResolveResult[] packageResult = resolveToPackage();
if (packageResult.length != 0) {
result = packageResult;
}
}
else if (result.length == 0) {
result = resolveToPackage();
}
return result;
}
if (parentType == JavaElementType.METHOD_CALL_EXPRESSION) {
return resolveToMethod();
}
if (parentType == JavaElementType.METHOD_REF_EXPRESSION) {
return resolve(JavaElementType.REFERENCE_EXPRESSION);
}
return resolveToVariable();
}
@@ -0,0 +1,12 @@
package main;
public class Main {
public static void main(String[] args) {
pkg.sub.Test obj = new pkg.sub.Test();
obj.m1();
obj.<error descr="Cannot resolve method 'm2()'">m2</error>();
pkg.sub.Test.s1();
pkg.sub.Test.<error descr="Cannot resolve method 's2()'">s2</error>();
}
}
@@ -0,0 +1,9 @@
package pkg;
class sub {
public static class Test {
public void m2() { }
public static void s2() { }
}
}
@@ -0,0 +1,7 @@
package pkg.sub;
public class Test {
public void m1() { }
public static void s1() { }
}
@@ -297,4 +297,8 @@ public class AdvHighlightingTest extends DaemonAnalyzerTestCase {
public void testPackageAndClassConflict3() throws Exception {
doTest(BASE_PATH + "/packageClassClash/java/lang.java", false, false);
}
public void testPackageObscuring() throws Exception {
doTest(BASE_PATH + "/packageObscuring/main/Main.java", BASE_PATH + "/packageObscuring", false, false);
}
}