diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index a0715fd8ca89..0db49c882976 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java @@ -390,99 +390,110 @@ public class PsiUtil { return isStaticsOK(owner, place, null); } - public static boolean isStaticsOK(PsiModifierListOwner owner, PsiElement place, @Nullable PsiElement resolveContext) { - if (owner instanceof PsiMember) { - if (place instanceof GrReferenceExpression) { - GrExpression qualifier = ((GrReferenceExpression)place).getQualifierExpression(); - if (qualifier != null) { - PsiClass containingClass = ((PsiMember)owner).getContainingClass(); - final boolean isStatic = owner.hasModifierProperty(PsiModifier.STATIC) && !(ResolveUtil.isInUseScope(resolveContext, owner)); - if (qualifier instanceof GrReferenceExpression) { - if ("class".equals(((GrReferenceExpression)qualifier).getReferenceName())) { - //invoke static members of class from A.class.foo() - final PsiType type = qualifier.getType(); - if (type instanceof PsiClassType) { - final PsiClass psiClass = ((PsiClassType)type).resolve(); - if (psiClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(psiClass.getQualifiedName())) { - final PsiType[] params = ((PsiClassType)type).getParameters(); - if (params.length == 1 && params[0] instanceof PsiClassType) { - if (place.getManager().areElementsEquivalent(containingClass, ((PsiClassType)params[0]).resolve())) { - return owner.hasModifierProperty(GrModifier.STATIC); - } - } + public static boolean isStaticsOK(PsiModifierListOwner member, PsiElement place, @Nullable PsiElement resolveContext) { + if (!(member instanceof PsiMember)) return true; + + if (!(place instanceof GrReferenceExpression)) return true; + + GrExpression qualifier = ((GrReferenceExpression)place).getQualifierExpression(); + final PsiClass containingClass = ((PsiMember)member).getContainingClass(); + if (qualifier != null) { + final boolean isStatic = member.hasModifierProperty(PsiModifier.STATIC) && !(ResolveUtil.isInUseScope(resolveContext, member)); + if (qualifier instanceof GrReferenceExpression) { + if ("class".equals(((GrReferenceExpression)qualifier).getReferenceName())) { + //invoke static members of class from A.class.foo() + final PsiType type = qualifier.getType(); + if (type instanceof PsiClassType) { + final PsiClass psiClass = ((PsiClassType)type).resolve(); + if (psiClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(psiClass.getQualifiedName())) { + final PsiType[] params = ((PsiClassType)type).getParameters(); + if (params.length == 1 && params[0] instanceof PsiClassType) { + if (place.getManager().areElementsEquivalent(containingClass, ((PsiClassType)params[0]).resolve())) { + return member.hasModifierProperty(GrModifier.STATIC); } } - - } - PsiElement qualifierResolved = ((GrReferenceExpression)qualifier).resolve(); - if (qualifierResolved instanceof PsiClass || qualifierResolved instanceof PsiPackage) { //static context - if (owner instanceof PsiClass) { - return true; - } - - //non-physical method, e.g. gdk - if (containingClass == null) { - return true; - } - - if (isStatic) { - return true; - } - - //members from java.lang.Class can be invoked without ".class" - final String qname = containingClass.getQualifiedName(); - if (qname != null && qname.startsWith("java.")) { - if (CommonClassNames.JAVA_LANG_OBJECT.equals(qname) || CommonClassNames.JAVA_LANG_CLASS.equals(qname)) { - return true; - } - - if (containingClass.isInterface()) { - PsiClass javaLangClass = - JavaPsiFacade.getInstance(place.getProject()).findClass(CommonClassNames.JAVA_LANG_CLASS, place.getResolveScope()); - if (javaLangClass != null && javaLangClass.isInheritor(containingClass, true)) { - return true; - } - } - } - - return false; } } - else if (qualifier instanceof GrThisReferenceExpression && ((GrThisReferenceExpression)qualifier).getQualifier() == null) { - //static members may be invoked from this.<...> - final boolean isInStatic = isInStaticContext((GrThisReferenceExpression)qualifier); - if (containingClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(containingClass.getQualifiedName())) { - return !(owner.hasModifierProperty(GrModifier.STATIC) && !CodeInsightSettings.getInstance().SHOW_STATIC_AFTER_INSTANCE); - } - else if (isInStatic) return owner.hasModifierProperty(GrModifier.STATIC); - } - //instance context - if (owner instanceof PsiClass) { - return false; - } - return !(isStatic && !CodeInsightSettings.getInstance().SHOW_STATIC_AFTER_INSTANCE); } - else { - if (((PsiMember)owner).getContainingClass() == null) return true; - if (owner instanceof GrVariable && !(owner instanceof GrField)) return true; - if (owner.hasModifierProperty(GrModifier.STATIC)) return true; + PsiElement qualifierResolved = ((GrReferenceExpression)qualifier).resolve(); + if (qualifierResolved instanceof PsiClass || qualifierResolved instanceof PsiPackage) { //static context + if (member instanceof PsiClass) { + return true; + } - PsiElement stopAt = resolveContext != null ? PsiTreeUtil.findCommonParent(place, resolveContext) : null; - while (place != null && place != stopAt && !(place instanceof GrMember)) { - if (place instanceof PsiFile) break; - place = place.getParent(); + //non-physical method, e.g. gdk + if (containingClass == null) { + return true; } - if (place == null || place instanceof PsiFile || place == stopAt) return true; - if (place instanceof GrTypeDefinition) { - return !(((GrTypeDefinition)place).hasModifierProperty(GrModifier.STATIC) || - ((GrTypeDefinition)place).getContainingClass() == null); + + if (isStatic) { + return true; } - return !((GrMember)place).hasModifierProperty(GrModifier.STATIC); + + //members from java.lang.Class can be invoked without ".class" + final String qname = containingClass.getQualifiedName(); + if (qname != null && qname.startsWith("java.")) { + if (CommonClassNames.JAVA_LANG_OBJECT.equals(qname) || CommonClassNames.JAVA_LANG_CLASS.equals(qname)) { + return true; + } + + if (containingClass.isInterface()) { + PsiClass javaLangClass = + JavaPsiFacade.getInstance(place.getProject()).findClass(CommonClassNames.JAVA_LANG_CLASS, place.getResolveScope()); + if (javaLangClass != null && javaLangClass.isInheritor(containingClass, true)) { + return true; + } + } + } + + return false; } } + else if (qualifier instanceof GrThisReferenceExpression && ((GrThisReferenceExpression)qualifier).getQualifier() == null) { + //static members may be invoked from this.<...> + final boolean isInStatic = isInStaticContext((GrThisReferenceExpression)qualifier); + if (containingClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(containingClass.getQualifiedName())) { + return !(member.hasModifierProperty(GrModifier.STATIC) && !CodeInsightSettings.getInstance().SHOW_STATIC_AFTER_INSTANCE); + } + else if (isInStatic) return member.hasModifierProperty(GrModifier.STATIC); + } + + //instance context + if (member instanceof PsiClass) { + return false; + } + return !(isStatic && !CodeInsightSettings.getInstance().SHOW_STATIC_AFTER_INSTANCE); + } + else { + if (containingClass == null) return true; + if (member instanceof GrVariable && !(member instanceof GrField)) return true; + if (member.hasModifierProperty(GrModifier.STATIC)) return true; + + if (resolveContext != null) { + PsiElement stopAt = PsiTreeUtil.findCommonParent(place, resolveContext); + while (place != null && place != stopAt && !(place instanceof GrMember)) { + if (place instanceof PsiFile) break; + place = place.getParent(); + } + if (place == null || place instanceof PsiFile || place == stopAt) return true; + if (place instanceof GrTypeDefinition) { + return !(((GrTypeDefinition)place).hasModifierProperty(GrModifier.STATIC) || + ((GrTypeDefinition)place).getContainingClass() == null); + } + return !((GrMember)place).hasModifierProperty(GrModifier.STATIC); + } + else { + while (place != null) { + place = place.getParent(); + if (place instanceof PsiClass && InheritanceUtil.isInheritorOrSelf((PsiClass)place, containingClass, true)) return true; + if (place instanceof PsiMember && ((PsiMember)place).hasModifierProperty(PsiModifier.STATIC)) { + return false; + } + } + return true; + } } - return true; } public static boolean isAccessible(PsiElement place, PsiMember member) { @@ -532,6 +543,7 @@ public class PsiUtil { else { PsiElement run = refExpression; while (run != null && run != targetClass) { + if (targetClass == null && run instanceof PsiClass) return false; if (run instanceof PsiModifierListOwner && ((PsiModifierListOwner)run).hasModifierProperty(PsiModifier.STATIC)) return true; run = run.getParent(); } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/StaticCheckTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/StaticCheckTest.java new file mode 100644 index 000000000000..1977eee7b36d --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/StaticCheckTest.java @@ -0,0 +1,31 @@ +package org.jetbrains.plugins.groovy.lang.resolve; + +import com.intellij.psi.PsiElement; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.util.TestUtils; + +/** + * @author Medvedev Max + */ +public class StaticCheckTest extends GroovyResolveTestCase { + @Override + protected String getBasePath() { + return TestUtils.getTestDataPath() + "resolve/static/"; + } + + protected void doTest(boolean staticOk) { + final GroovyResolveResult resolveResult = advancedResolve("a.groovy"); + assertNotNull(resolveResult); + final PsiElement element = resolveResult.getElement(); + assertNotNull(element); + assertEquals(staticOk, resolveResult.isStaticsOK()); + } + + public void testPropInStaticInnerClass() { + doTest(false); + } + + public void testThisInStaticInnerClass() { + doTest(true); + } +} diff --git a/plugins/groovy/testdata/resolve/static/propInStaticInnerClass/a.groovy b/plugins/groovy/testdata/resolve/static/propInStaticInnerClass/a.groovy new file mode 100644 index 000000000000..1e4a2ca0974a --- /dev/null +++ b/plugins/groovy/testdata/resolve/static/propInStaticInnerClass/a.groovy @@ -0,0 +1,7 @@ +class A { + def someService + + static private class B { + void method() { someService } + } +} \ No newline at end of file diff --git a/plugins/groovy/testdata/resolve/static/thisInStaticInnerClass/a.groovy b/plugins/groovy/testdata/resolve/static/thisInStaticInnerClass/a.groovy new file mode 100644 index 000000000000..ee67fa00e867 --- /dev/null +++ b/plugins/groovy/testdata/resolve/static/thisInStaticInnerClass/a.groovy @@ -0,0 +1,8 @@ +class A { + def someService + + static private class B { + int ii + B () { this.ii = 42 } + } +} \ No newline at end of file