diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GdkMethodUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GdkMethodUtil.java index 6fec090db0bb..4c5737df0f48 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GdkMethodUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GdkMethodUtil.java @@ -46,6 +46,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrReferenceResolveUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrGdkMethodImpl; +import org.jetbrains.plugins.groovy.lang.resolve.noncode.MixinMemberContributor; import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint; import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor; @@ -218,19 +219,7 @@ public class GdkMethodUtil { 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 (element instanceof PsiMethod && isCategoryMethod((PsiMethod)element, subjectType, qualifier, null)) { - PsiMethod method = (PsiMethod)element; - return processor.execute(GrGdkMethodImpl.createGdkMethod(method, false, generateOriginInfo(method)), delegateState); - } - else if (element instanceof PsiMethod && ((PsiMethod)element).hasModifierProperty(PsiModifier.PUBLIC)) { - super.execute(element, delegateState); - } - return true; - } - }; + final DelegatingScopeProcessor delegate = new MixinMemberContributor.MixinProcessor(processor, subjectType, qualifier); mixin.processDeclarations(delegate, state, null, place); } } 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 50d93980517b..3a6fbb3c605a 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 @@ -15,10 +15,14 @@ */ package org.jetbrains.plugins.groovy.lang.resolve.noncode; +import com.intellij.codeInsight.completion.originInfo.OriginInfoAwareElement; import com.intellij.psi.*; +import com.intellij.psi.impl.light.LightMethod; import com.intellij.psi.scope.DelegatingScopeProcessor; import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArrayInitializer; @@ -66,26 +70,15 @@ public class MixinMemberContributor extends NonCodeMembersContributor { } } + final MixinProcessor delegate = new MixinProcessor(processor, qualifierType, place); for (PsiClass mixin : mixins) { - if (!mixin.processDeclarations(new DelegatingScopeProcessor(processor) { - @Override - public boolean execute(@NotNull PsiElement element, ResolveState state) { - 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); - } - else { - return super.execute(element, state); - } - } - }, state, null, place)) { + if (!mixin.processDeclarations(delegate, state, null, place)) { return; } } } - private static String getOriginInfo(PsiMethod element) { + public static String getOriginInfoForCategory(PsiMethod element) { PsiClass aClass = element.getContainingClass(); if (aClass != null && aClass.getName() != null) { return "mixed in from " + aClass.getName(); @@ -93,6 +86,10 @@ public class MixinMemberContributor extends NonCodeMembersContributor { return "mixed in"; } + public static String getOriginInfoForMixin(@NotNull PsiType subjectType) { + return "mixed in " + subjectType.getPresentableText(); + } + private static List getAllMixins(PsiModifierList modifierList) { final ArrayList result = new ArrayList(); for (PsiAnnotation annotation : modifierList.getApplicableAnnotations()) { @@ -115,4 +112,53 @@ public class MixinMemberContributor extends NonCodeMembersContributor { } } } + + private static class MixinedMethod extends LightMethod implements OriginInfoAwareElement, PsiMirrorElement { + private final String myOriginInfo; + private final PsiMethod myPrototype; + + public MixinedMethod(@NotNull PsiMethod method, String originInfo) { + super(method.getManager(), method, ObjectUtils.assertNotNull(method.getContainingClass())); + myOriginInfo = originInfo; + myPrototype = method; + } + + @Nullable + @Override + public String getOriginInfo() { + return myOriginInfo; + } + + @NotNull + @Override + public PsiElement getPrototype() { + return myPrototype; + } + } + + public static class MixinProcessor extends DelegatingScopeProcessor { + private final PsiType myType; + private final PsiElement myPlace; + + public MixinProcessor(PsiScopeProcessor delegate, @NotNull PsiType qualifierType, @Nullable PsiElement place) { + super(delegate); + myType = qualifierType; + myPlace = place; + } + + @Override + public boolean execute(@NotNull PsiElement element, ResolveState state) { + if (element instanceof PsiMethod && GdkMethodUtil.isCategoryMethod((PsiMethod)element, myType, myPlace, state.get(PsiSubstitutor.KEY))) { + PsiMethod method = (PsiMethod)element; + String originInfo = getOriginInfoForCategory(method); + return super.execute(GrGdkMethodImpl.createGdkMethod(method, false, originInfo), state); + } + else if (element instanceof PsiMethod) { + return super.execute(new MixinedMethod((PsiMethod)element, getOriginInfoForMixin(myType)), state); + } + else { + return super.execute(element, state); + } + } + } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy index 3b3bb23ab3fb..5505d0fada9f 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveMethodTest.groovy @@ -783,8 +783,7 @@ print new B().foo() """) def resolved = ref.resolve() - assertInstanceOf(resolved, GrMethod) - assertTrue(resolved.physical) + assertInstanceOf(resolved, PsiMethod) } void testTwoMixinsInModifierList() { @@ -812,8 +811,7 @@ class OtherPersonMixin { """) def resolved = ref.resolve() - assertInstanceOf(resolved, GrMethod) - assertTrue(resolved.physical) + assertInstanceOf(resolved, PsiMethod) }