diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java index 5ddc309ed1bd..7e4c5078be54 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java @@ -72,6 +72,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.*; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager; +import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrReferenceResolveUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; @@ -816,9 +817,13 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection { if (method instanceof GrGdkMethod && place instanceof GrReferenceExpression) { final PsiMethod staticMethod = ((GrGdkMethod)method).getStaticMethod(); final PsiType qualifierType = inferQualifierTypeByPlace((GrReferenceExpression)place); + + final GrExpression qualifier = PsiImplUtil.getRuntimeQualifier((GrReferenceExpression)place); + + //check methods processed by @Category(ClassWhichProcessMethod) annotation if (qualifierType != null && - !GdkMethodUtil.isCategoryMethod(staticMethod, qualifierType, methodResolveResult.getSubstitutor()) && - !checkCategoryQualifier((GrReferenceExpression)place, staticMethod, methodResolveResult.getSubstitutor())) { + !GdkMethodUtil.isCategoryMethod(staticMethod, qualifierType, qualifier, methodResolveResult.getSubstitutor()) && + !checkCategoryQualifier((GrReferenceExpression)place, qualifier, staticMethod, methodResolveResult.getSubstitutor())) { registerError(((GrReferenceExpression)place).getReferenceNameElement(), GroovyInspectionBundle .message("category.method.0.cannot.be.applied.to.1", method.getName(), qualifierType.getCanonicalText())); return false; @@ -861,13 +866,16 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection { } } - private static boolean checkCategoryQualifier(GrReferenceExpression place, PsiMethod gdkMethod, PsiSubstitutor substitutor) { - PsiClass categoryAnnotationOwner = inferCategoryAnnotationOwner(place, place.getQualifier()); + private static boolean checkCategoryQualifier(GrReferenceExpression place, + GrExpression qualifier, + PsiMethod gdkMethod, + PsiSubstitutor substitutor) { + PsiClass categoryAnnotationOwner = inferCategoryAnnotationOwner(place, qualifier); if (categoryAnnotationOwner != null) { PsiClassType categoryType = GdkMethodUtil.getCategoryType(categoryAnnotationOwner); if (categoryType != null) { - return GdkMethodUtil.isCategoryMethod(gdkMethod, categoryType, substitutor); + return GdkMethodUtil.isCategoryMethod(gdkMethod, categoryType, qualifier, substitutor); } } @@ -877,7 +885,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection { private static PsiClass inferCategoryAnnotationOwner(GrReferenceExpression place, GrExpression qualifier) { if (qualifier == null) { GrMethod container = PsiTreeUtil.getParentOfType(place, GrMethod.class, true, GrMember.class); - if (container != null && !container.hasModifierProperty(PsiModifier.STATIC)) { //only instance classes can be qualified by category class + if (container != null && !container.hasModifierProperty(PsiModifier.STATIC)) { //only instance methods can be qualified by category class return container.getContainingClass(); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/AccessorMethodReferencesSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/AccessorMethodReferencesSearcher.java index 7a2d8e567a65..03aad49038e0 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/AccessorMethodReferencesSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/AccessorMethodReferencesSearcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -46,7 +46,7 @@ public class AccessorMethodReferencesSearcher extends QueryExecutorBase result = getMixinTypes(statement); + final Trinity result = getMixinTypes(statement); if (result != null) { final PsiClassType subjectType = result.first; - final PsiClass mixin = result.second; + final GrReferenceExpression qualifier = result.second; + final PsiClass mixin = result.third; final DelegatingScopeProcessor delegate = new DelegatingScopeProcessor(processor) { @Override public boolean execute(@NotNull PsiElement element, ResolveState delegateState) { - if (isCategoryMethod(element, subjectType, null)) { + if (element instanceof PsiMethod && isCategoryMethod((PsiMethod)element, subjectType, qualifier, null)) { PsiMethod method = (PsiMethod)element; return processor.execute(GrGdkMethodImpl.createGdkMethod(method, false, generateOriginInfo(method)), delegateState); } @@ -244,14 +239,14 @@ public class GdkMethodUtil { } @Nullable - private static Pair getMixinTypes(GrStatement statement) { + private static Trinity getMixinTypes(GrStatement statement) { if (statement instanceof GrMethodCall) { GrMethodCall call = (GrMethodCall)statement; - PsiClassType original = getTypeToMixIn(call); + Pair original = getTypeToMixIn(call); PsiClass mix = getTypeToMix(call); if (original != null && mix != null) { - return new Pair(original, mix); + return new Trinity(original.first, original.second, mix); } } @@ -287,13 +282,13 @@ public class GdkMethodUtil { } @Nullable - private static PsiClassType getTypeToMixIn(GrMethodCall methodCall) { + private static Pair getTypeToMixIn(GrMethodCall methodCall) { GrExpression invoked = methodCall.getInvokedExpression(); if (invoked instanceof GrReferenceExpression) { PsiElement resolved = ((GrReferenceExpression)invoked).resolve(); if (resolved instanceof PsiMethod && isMixinMethod((PsiMethod)resolved)) { GrExpression qualifier = ((GrReferenceExpression)invoked).getQualifier(); - PsiClassType type = getPsiClassFromReference(qualifier); + Pair type = getPsiClassFromReference(qualifier); if (type != null) { return type; } @@ -306,7 +301,7 @@ public class GdkMethodUtil { if (qualifier instanceof GrReferenceExpression) { GrExpression qqualifier = ((GrReferenceExpression)qualifier).getQualifier(); if (qqualifier != null) { - PsiClassType type1 = getPsiClassFromReference(qqualifier); + Pair type1 = getPsiClassFromReference(qqualifier); if (type1 != null) { return type1; } @@ -314,7 +309,7 @@ public class GdkMethodUtil { else { PsiType qtype = GrReferenceResolveUtil.getQualifierType((GrReferenceExpression)qualifier); if (qtype instanceof PsiClassType && ((PsiClassType)qtype).resolve() != null) { - return (PsiClassType)qtype; + return Pair.create((PsiClassType)qtype, (GrReferenceExpression)qualifier); } } } @@ -339,22 +334,21 @@ public class GdkMethodUtil { return mixinRef instanceof GrReferenceExpression && "class".equals(((GrReferenceExpression)mixinRef).getReferenceName()); } - private static PsiClassType getPsiClassFromReference(GrExpression ref) { + @Nullable + private static Pair getPsiClassFromReference(GrExpression ref) { if (isClassRef(ref)) ref = ((GrReferenceExpression)ref).getQualifier(); if (ref instanceof GrReferenceExpression) { PsiElement resolved = ((GrReferenceExpression)ref).resolve(); if (resolved instanceof PsiClass) { PsiType type = ref.getType(); LOG.assertTrue(type instanceof PsiClassType, "reference resolved into PsiClass should have PsiClassType"); - return ((PsiClassType)type); + return Pair.create((PsiClassType)type, (GrReferenceExpression)ref); } } return null; } - public static boolean isCategoryMethod(@Nullable PsiElement element, @Nullable PsiType qualifierType, @Nullable PsiSubstitutor substitutor) { - if (!(element instanceof PsiMethod)) return false; - PsiMethod method = (PsiMethod)element; + public static boolean isCategoryMethod(@NotNull PsiMethod method, @Nullable PsiType qualifierType, @Nullable PsiElement place, @Nullable PsiSubstitutor substitutor) { if (!method.hasModifierProperty(PsiModifier.STATIC)) return false; if (!method.hasModifierProperty(PsiModifier.PUBLIC)) return false; @@ -369,7 +363,18 @@ public class GdkMethodUtil { if (substitutor != null) { selfType = substitutor.substitute(selfType); } - return TypesUtil.isAssignable(selfType, qualifierType, element.getManager(), element.getResolveScope()); + + final GlobalSearchScope scope = method.getResolveScope(); + final Project project = method.getProject(); + final PsiManager manager = method.getManager(); + + if (selfType instanceof PsiClassType && + ((PsiClassType)selfType).rawType().equalsToText(CommonClassNames.JAVA_LANG_CLASS) && + place instanceof GrReferenceExpression && + ((GrReferenceExpression)place).resolve() instanceof PsiClass) { // ClassType.categoryMethod() where categoryMethod(Class<> cl, ...) + return TypesUtil.isAssignable(selfType, TypesUtil.createJavaLangClassType(qualifierType, project, scope), manager, scope); + } + return TypesUtil.isAssignable(selfType, qualifierType, manager, scope); } @Nullable diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/MixinMemberContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/MixinMemberContributor.java index afc0bf53ab54..50d93980517b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/MixinMemberContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/MixinMemberContributor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -40,7 +40,7 @@ public class MixinMemberContributor extends NonCodeMembersContributor { @Override public void processDynamicElements(@NotNull final PsiType qualifierType, @NotNull PsiScopeProcessor processor, - @NotNull GroovyPsiElement place, + @NotNull final GroovyPsiElement place, @NotNull ResolveState state) { if (!(qualifierType instanceof PsiClassType)) return; if (isInAnnotation(place)) return; @@ -70,7 +70,7 @@ public class MixinMemberContributor extends NonCodeMembersContributor { if (!mixin.processDeclarations(new DelegatingScopeProcessor(processor) { @Override public boolean execute(@NotNull PsiElement element, ResolveState state) { - if (GdkMethodUtil.isCategoryMethod(element, qualifierType, state.get(PsiSubstitutor.KEY))) { + if (element instanceof PsiMethod && GdkMethodUtil.isCategoryMethod((PsiMethod)element, qualifierType, place, state.get(PsiSubstitutor.KEY))) { PsiMethod method = (PsiMethod)element; String originInfo = getOriginInfo(method); return super.execute(GrGdkMethodImpl.createGdkMethod(method, false, originInfo), state); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GrAssignabilityTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GrAssignabilityTest.groovy index c9e46fbc340b..f12b250a6527 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GrAssignabilityTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GrAssignabilityTest.groovy @@ -411,6 +411,21 @@ class EvenSieve { add(2, 3) } } +''') + } + + void testAssignabilityOfCategoryMethod() { + testHighlighting(''' +class Cat { + static foo(Class c, int x) {} +} + +class X {} + +use(Cat) { + X.foo(1) +} + ''') } }