mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-01 06:51:17 +07:00
IDEA-61962 Groovy runtime mixin support
This commit is contained in:
+2
-6
@@ -74,12 +74,8 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
|
||||
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;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyConstantExpressionEvaluator;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.*;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.noncode.MixinMemberContributor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -819,7 +815,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
|
||||
if (method instanceof GrGdkMethod && place instanceof GrReferenceExpression) {
|
||||
final PsiMethod staticMethod = ((GrGdkMethod)method).getStaticMethod();
|
||||
final PsiType qualifier = inferQualifierTypeByPlace((GrReferenceExpression)place);
|
||||
if (qualifier != null && !MixinMemberContributor.isCategoryMethod(staticMethod, qualifier, methodResolveResult.getSubstitutor())) {
|
||||
if (qualifier != null && !GdkMethodUtil.isCategoryMethod(staticMethod, qualifier, methodResolveResult.getSubstitutor())) {
|
||||
registerError(((GrReferenceExpression)place).getReferenceNameElement(),
|
||||
GroovyInspectionBundle.message("category.method.0.cannot.be.applied.to.1", method.getName(),
|
||||
qualifier.getCanonicalText()));
|
||||
|
||||
+2
-2
@@ -28,9 +28,9 @@ import com.intellij.util.Processor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrGdkMethodImpl;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GdkMethodUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.noncode.MixinMemberContributor;
|
||||
|
||||
/**
|
||||
* author ven
|
||||
@@ -46,7 +46,7 @@ public class AccessorMethodReferencesSearcher extends QueryExecutorBase<PsiRefer
|
||||
final PsiMethod method = queryParameters.getMethod();
|
||||
|
||||
final String propertyName;
|
||||
if (MixinMemberContributor.isCategoryMethod(method, null, PsiSubstitutor.EMPTY)) {
|
||||
if (GdkMethodUtil.isCategoryMethod(method, null, PsiSubstitutor.EMPTY)) {
|
||||
final GrGdkMethod cat = GrGdkMethodImpl.createGdkMethod(method, false, null);
|
||||
propertyName = GroovyPropertyUtils.getPropertyName((PsiMethod)cat);
|
||||
}
|
||||
|
||||
@@ -16,21 +16,28 @@
|
||||
package org.jetbrains.plugins.groovy.lang.psi.util;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.scope.DelegatingScopeProcessor;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
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.statements.GrStatement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
|
||||
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.processors.ClassHint;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor;
|
||||
@@ -73,11 +80,7 @@ public class GdkMethodUtil {
|
||||
|
||||
final GrClosableBlock[] closures = call.getClosureArguments();
|
||||
final GrExpression[] args = call.getExpressionArguments();
|
||||
int last = args.length - 1;
|
||||
if (!(closures.length == 1 && place.equals(closures[0])) &&
|
||||
!(args.length > 0 && place.equals(args[last]))) {
|
||||
return true;
|
||||
}
|
||||
if (!(placeEqualsSingleClosureArg(place, closures) || placeEqualsLastArg(place, args))) return true;
|
||||
|
||||
if (!(call.resolveMethod() instanceof GrGdkMethod)) return true;
|
||||
|
||||
@@ -93,6 +96,14 @@ public class GdkMethodUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean placeEqualsLastArg(GrClosableBlock place, GrExpression[] args) {
|
||||
return args.length > 0 && place.equals(args[args.length - 1]);
|
||||
}
|
||||
|
||||
private static boolean placeEqualsSingleClosureArg(GrClosableBlock place, GrClosableBlock[] closures) {
|
||||
return closures.length == 1 && place.equals(closures[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param place - context of processing
|
||||
@@ -106,13 +117,10 @@ public class GdkMethodUtil {
|
||||
@NotNull final PsiClass categoryClass) {
|
||||
final DelegatingScopeProcessor delegate = new DelegatingScopeProcessor(processor) {
|
||||
@Override
|
||||
public boolean execute(@NotNull PsiElement element, ResolveState state) {
|
||||
if (element instanceof PsiMethod) {
|
||||
public boolean execute(@NotNull PsiElement element, ResolveState delegateState) {
|
||||
if (isCategoryMethod(element, null, null)) {
|
||||
PsiMethod method = (PsiMethod)element;
|
||||
if (!method.hasModifierProperty(PsiModifier.STATIC)) return true;
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
if (parameters.length == 0) return true;
|
||||
return processor.execute(GrGdkMethodImpl.createGdkMethod(method, false, generateOriginInfo(method)), state);
|
||||
return processor.execute(GrGdkMethodImpl.createGdkMethod(method, false, generateOriginInfo(method)), delegateState);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -120,6 +128,14 @@ public class GdkMethodUtil {
|
||||
return categoryClass.processDeclarations(delegate, state, null, place);
|
||||
}
|
||||
|
||||
private static boolean acceptClass(@Nullable PsiType type, @NotNull PsiClass aClass) {
|
||||
if (!(type instanceof PsiClassType)) return false;
|
||||
|
||||
PsiClass resolved = ((PsiClassType)type).resolve();
|
||||
|
||||
return InheritanceUtil.isInheritorOrSelf(aClass, resolved, true);
|
||||
}
|
||||
|
||||
public static boolean withIteration(GrClosableBlock block, final PsiScopeProcessor processor) {
|
||||
GrMethodCall call = checkMethodCall(block, WITH);
|
||||
if (call == null) {
|
||||
@@ -186,4 +202,168 @@ public class GdkMethodUtil {
|
||||
//'\u2191'
|
||||
return "via " + cc.getName();
|
||||
}
|
||||
|
||||
public static boolean processMixinToMetaclass(GrStatementOwner run,
|
||||
final PsiScopeProcessor processor,
|
||||
ResolveState state,
|
||||
PsiElement lastParent,
|
||||
GroovyPsiElement place) {
|
||||
GrStatement[] statements = run.getStatements();
|
||||
for (GrStatement statement : statements) {
|
||||
if (statement == lastParent) break;
|
||||
|
||||
final Pair<PsiClassType, PsiClass> result = getMixinTypes(statement);
|
||||
|
||||
if (result != null) {
|
||||
final PsiClassType subjectType = result.first;
|
||||
final PsiClass mixin = result.second;
|
||||
|
||||
final DelegatingScopeProcessor delegate = new DelegatingScopeProcessor(processor) {
|
||||
@Override
|
||||
public boolean execute(@NotNull PsiElement element, ResolveState delegateState) {
|
||||
if (isCategoryMethod(element, subjectType, 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;
|
||||
}
|
||||
};
|
||||
mixin.processDeclarations(delegate, state, null, place);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Pair<PsiClassType, PsiClass> getMixinTypes(GrStatement statement) {
|
||||
if (statement instanceof GrMethodCall) {
|
||||
GrMethodCall call = (GrMethodCall)statement;
|
||||
PsiClassType original = getTypeToMixIn(call);
|
||||
PsiClass mix = getTypeToMix(call);
|
||||
|
||||
if (original != null && mix != null) {
|
||||
return new Pair<PsiClassType, PsiClass>(original, mix);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClass getTypeToMix(GrMethodCall call) {
|
||||
if (!isSingleExpressionArg(call)) return null;
|
||||
|
||||
GrExpression mixinRef = call.getExpressionArguments()[0];
|
||||
if (isClassRef(mixinRef)) {
|
||||
mixinRef = ((GrReferenceExpression)mixinRef).getQualifier();
|
||||
}
|
||||
|
||||
if (mixinRef instanceof GrReferenceExpression) {
|
||||
PsiElement resolved = ((GrReferenceExpression)mixinRef).resolve();
|
||||
if (resolved instanceof PsiClass) {
|
||||
return (PsiClass)resolved;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isSingleExpressionArg(GrMethodCall call) {
|
||||
GrExpression[] exprs = call.getExpressionArguments();
|
||||
GrNamedArgument[] named = call.getNamedArguments();
|
||||
GrClosableBlock[] closures = call.getClosureArguments();
|
||||
|
||||
return exprs.length == 1 && named.length == 0 && closures.length == 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClassType 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);
|
||||
if (type != null) {
|
||||
return type;
|
||||
}
|
||||
if (qualifier == null) {
|
||||
qualifier = PsiImplUtil.getRuntimeQualifier((GrReferenceExpression)invoked);
|
||||
}
|
||||
if (qualifier != null && isMetaClass(qualifier.getType())) {
|
||||
if (qualifier instanceof GrMethodCall) qualifier = ((GrMethodCall)qualifier).getInvokedExpression();
|
||||
|
||||
if (qualifier instanceof GrReferenceExpression) {
|
||||
GrExpression qqualifier = ((GrReferenceExpression)qualifier).getQualifier();
|
||||
if (qqualifier != null) {
|
||||
PsiClassType type1 = getPsiClassFromReference(qqualifier);
|
||||
if (type1 != null) {
|
||||
return type1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
PsiType qtype = GrReferenceResolveUtil.getQualifierType((GrReferenceExpression)qualifier);
|
||||
if (qtype instanceof PsiClassType && ((PsiClassType)qtype).resolve() != null) {
|
||||
return (PsiClassType)qtype;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isMixinMethod(PsiMethod method) {
|
||||
if (method instanceof GrGdkMethod) method = ((GrGdkMethod)method).getStaticMethod();
|
||||
PsiClass containingClass = method.getContainingClass();
|
||||
String name = method.getName();
|
||||
return "mixin".equals(name) && containingClass != null && GroovyCommonClassNames.DEFAULT_GROOVY_METHODS.equals(containingClass.getQualifiedName());
|
||||
}
|
||||
|
||||
private static boolean isMetaClass(PsiType qualifierType) {
|
||||
return qualifierType != null && qualifierType.equalsToText(GroovyCommonClassNames.GROOVY_LANG_META_CLASS);
|
||||
}
|
||||
|
||||
private static boolean isClassRef(GrExpression mixinRef) {
|
||||
return mixinRef instanceof GrReferenceExpression && "class".equals(((GrReferenceExpression)mixinRef).getReferenceName());
|
||||
}
|
||||
|
||||
private static PsiClassType 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 null;
|
||||
}
|
||||
|
||||
public static boolean isCategoryMethod(@Nullable PsiElement element, @Nullable PsiType qualifierType, @Nullable PsiSubstitutor substitutor) {
|
||||
if (!(element instanceof PsiMethod)) return false;
|
||||
PsiMethod method = (PsiMethod)element;
|
||||
if (!method.hasModifierProperty(PsiModifier.STATIC)) return false;
|
||||
if (!method.hasModifierProperty(PsiModifier.PUBLIC)) return false;
|
||||
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
if (parameters.length == 0) return false;
|
||||
|
||||
if (qualifierType == null) return true;
|
||||
|
||||
PsiType selfType = parameters[0].getType();
|
||||
if (selfType instanceof PsiPrimitiveType) return false;
|
||||
|
||||
if (substitutor != null) {
|
||||
selfType = substitutor.substitute(selfType);
|
||||
}
|
||||
return TypesUtil.isAssignable(selfType, qualifierType, element.getManager(), element.getResolveScope());
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -57,6 +57,7 @@ public final class GroovyCommonClassNames {
|
||||
@NonNls public static final String JAVA_LANG_CHAR_SEQUENCE = "java.lang.CharSequence";
|
||||
public static final String GROOVY_TRANSFORM_INHERIT_CONSTRUCTORS = "groovy.transform.InheritConstructors";
|
||||
public static final String GROOVY_LANG_IMMUTABLE = "groovy.lang.Immutable";
|
||||
@NonNls public static final String GROOVY_LANG_META_CLASS = "groovy.lang.MetaClass";
|
||||
|
||||
|
||||
private GroovyCommonClassNames() {
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGd
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
|
||||
@@ -112,9 +113,10 @@ public class ResolveUtil {
|
||||
static boolean doProcessDeclarations(GroovyPsiElement place,
|
||||
PsiElement lastParent,
|
||||
PsiElement scope,
|
||||
@NotNull PsiScopeProcessor plainProcessor, @Nullable PsiScopeProcessor nonCodeProcessor) {
|
||||
@NotNull PsiScopeProcessor plainProcessor,
|
||||
@Nullable PsiScopeProcessor nonCodeProcessor) {
|
||||
if (!scope.processDeclarations(plainProcessor, ResolveState.initial(), lastParent, place)) return false;
|
||||
if (nonCodeProcessor != null && !processScopeNonCodeMethods(place, nonCodeProcessor, scope)) return false;
|
||||
if (nonCodeProcessor != null && !processScopeNonCodeMethods(place, lastParent, nonCodeProcessor, scope)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -136,14 +138,16 @@ public class ResolveUtil {
|
||||
return processor;
|
||||
}
|
||||
|
||||
static boolean processScopeNonCodeMethods(GroovyPsiElement place, PsiScopeProcessor processor, PsiElement scope) {
|
||||
static boolean processScopeNonCodeMethods(GroovyPsiElement place, PsiElement lastParent, PsiScopeProcessor processor, PsiElement scope) {
|
||||
if (scope instanceof GrTypeDefinition) {
|
||||
return processNonCodeMembers(createPsiType((GrTypeDefinition)scope), processor, place, ResolveState.initial());
|
||||
}
|
||||
|
||||
if (scope instanceof GroovyFileBase && ((GroovyFileBase)scope).isScript()) {
|
||||
final PsiClass psiClass = ((GroovyFileBase)scope).getScriptClass();
|
||||
return psiClass == null || processNonCodeMembers(createPsiType(psiClass), processor, place, ResolveState.initial());
|
||||
if (psiClass != null) {
|
||||
if (!processNonCodeMembers(createPsiType(psiClass), processor, place, ResolveState.initial())) return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (scope instanceof GrClosableBlock) {
|
||||
@@ -154,6 +158,10 @@ public class ResolveUtil {
|
||||
if (!GdkMethodUtil.withIteration((GrClosableBlock)scope, processor)) return false;
|
||||
}
|
||||
|
||||
if (scope instanceof GrStatementOwner) {
|
||||
if (!GdkMethodUtil.processMixinToMetaclass((GrStatementOwner)scope, processor, ResolveState.initial(), lastParent, place)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -188,7 +196,6 @@ public class ResolveUtil {
|
||||
|
||||
public static boolean processElement(PsiScopeProcessor processor, PsiNamedElement namedElement, ResolveState state) {
|
||||
NameHint nameHint = processor.getHint(NameHint.KEY);
|
||||
//todo [DIANA] look more carefully
|
||||
String name = nameHint == null ? null : nameHint.getName(state);
|
||||
if (name == null || name.equals(namedElement.getName())) {
|
||||
return processor.execute(namedElement, state);
|
||||
@@ -215,10 +222,10 @@ public class ResolveUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean processNonCodeMembers(PsiType type,
|
||||
PsiScopeProcessor processor,
|
||||
public static boolean processNonCodeMembers(@NotNull PsiType type,
|
||||
@NotNull PsiScopeProcessor processor,
|
||||
GroovyPsiElement place,
|
||||
ResolveState state) {
|
||||
@NotNull ResolveState state) {
|
||||
if (type instanceof PsiEllipsisType) {
|
||||
type = ((PsiEllipsisType)type).toArrayType();
|
||||
}
|
||||
@@ -407,6 +414,8 @@ public class ResolveUtil {
|
||||
|
||||
boolean inCodeBlock = true;
|
||||
PsiElement run = place;
|
||||
PsiElement lastParent = null;
|
||||
|
||||
while (run != null) {
|
||||
if (run instanceof GrMember) {
|
||||
inCodeBlock = false;
|
||||
@@ -417,11 +426,18 @@ public class ResolveUtil {
|
||||
PsiClass superClass = getLiteralSuperClass((GrClosableBlock)run);
|
||||
if (superClass != null && !GdkMethodUtil.processCategoryMethods(((GrClosableBlock)run), processor, state, superClass)) return false;
|
||||
}
|
||||
|
||||
if (gpp && run instanceof GrTypeDefinition) {
|
||||
final GrTypeDefinition typeDefinition = (GrTypeDefinition)run;
|
||||
state = state.put(ResolverProcessor.RESOLVE_CONTEXT, typeDefinition);
|
||||
if (!GdkMethodUtil.processCategoryMethods(typeDefinition, processor, state, typeDefinition)) return false;
|
||||
}
|
||||
|
||||
if (run instanceof GrStatementOwner) {
|
||||
if (!GdkMethodUtil.processMixinToMetaclass((GrStatementOwner)run, processor, state, lastParent, place)) return false;
|
||||
}
|
||||
|
||||
lastParent = run;
|
||||
run = run.getContext();
|
||||
}
|
||||
|
||||
|
||||
+2
-23
@@ -19,15 +19,14 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.scope.DelegatingScopeProcessor;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
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;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationMemberValue;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
|
||||
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.psi.util.GdkMethodUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor;
|
||||
|
||||
@@ -71,7 +70,7 @@ public class MixinMemberContributor extends NonCodeMembersContributor {
|
||||
if (!mixin.processDeclarations(new DelegatingScopeProcessor(processor) {
|
||||
@Override
|
||||
public boolean execute(@NotNull PsiElement element, ResolveState state) {
|
||||
if (isCategoryMethod(element, qualifierType, state.get(PsiSubstitutor.KEY))) {
|
||||
if (GdkMethodUtil.isCategoryMethod(element, qualifierType, state.get(PsiSubstitutor.KEY))) {
|
||||
PsiMethod method = (PsiMethod)element;
|
||||
String originInfo = getOriginInfo(method);
|
||||
return super.execute(GrGdkMethodImpl.createGdkMethod(method, false, originInfo), state);
|
||||
@@ -104,26 +103,6 @@ public class MixinMemberContributor extends NonCodeMembersContributor {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isCategoryMethod(@Nullable PsiElement element, @Nullable PsiType qualifierType, @Nullable PsiSubstitutor substitutor) {
|
||||
if (!(element instanceof PsiMethod)) return false;
|
||||
PsiMethod method = (PsiMethod)element;
|
||||
if (!method.hasModifierProperty(PsiModifier.STATIC)) return false;
|
||||
if (!method.hasModifierProperty(PsiModifier.PUBLIC)) return false;
|
||||
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
if (parameters.length == 0) return false;
|
||||
|
||||
if (qualifierType == null) return true;
|
||||
|
||||
PsiType selfType = parameters[0].getType();
|
||||
if (selfType instanceof PsiPrimitiveType) return false;
|
||||
|
||||
if (substitutor != null) {
|
||||
selfType = substitutor.substitute(selfType);
|
||||
}
|
||||
return TypesUtil.isAssignable(selfType, qualifierType, element.getManager(), element.getResolveScope());
|
||||
}
|
||||
|
||||
private static boolean isInAnnotation(GroovyPsiElement place) {
|
||||
return place.getParent() instanceof GrAnnotation || place.getParent() instanceof GrAnnotationArrayInitializer;
|
||||
}
|
||||
|
||||
+318
@@ -1111,4 +1111,322 @@ setFo<caret>o(2)
|
||||
assertEquals('setFoo', resolved.name)
|
||||
}
|
||||
|
||||
void testRuntimeMixin1() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
metaClass.mixin(Foo)
|
||||
d<caret>oSmth()
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin2() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
this.metaClass.mixin(Foo)
|
||||
do<caret>Smth()
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin3() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
_a.metaClass.mixin(Foo)
|
||||
do<caret>Smth()
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin4() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
_a.class.mixin(Foo)
|
||||
do<caret>Smth()
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin5() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
_a.mixin(Foo)
|
||||
do<caret>Smth()
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin6() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
class _a {
|
||||
def foo() {
|
||||
metaClass.mixin(Foo)
|
||||
d<caret>oSmth()
|
||||
}
|
||||
}
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin7() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
class _a {
|
||||
def foo() {
|
||||
this.metaClass.mixin(Foo)
|
||||
do<caret>Smth()
|
||||
}
|
||||
}
|
||||
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin8() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
class _a {
|
||||
def foo() {
|
||||
_a.metaClass.mixin(Foo)
|
||||
do<caret>Smth()
|
||||
}
|
||||
}
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin9() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
class _a {
|
||||
def foo() {
|
||||
_a.class.mixin(Foo)
|
||||
do<caret>Smth()
|
||||
}
|
||||
}
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin10() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
class _a {
|
||||
def foo() {
|
||||
_a.mixin(Foo)
|
||||
do<caret>Smth()
|
||||
}
|
||||
}
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin11() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
metaClass.mixin(Foo)
|
||||
new _a().d<caret>oSmth()
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin12() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
this.metaClass.mixin(Foo)
|
||||
new _a().do<caret>Smth()
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin13() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
_a.metaClass.mixin(Foo)
|
||||
new _a().do<caret>Smth()
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin14() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
_a.class.mixin(Foo)
|
||||
new _a().do<caret>Smth()
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin15() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
_a.mixin(Foo)
|
||||
new _a().do<caret>Smth()
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin16() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
class _a {
|
||||
def foo() {
|
||||
metaClass.mixin(Foo)
|
||||
new _a().d<caret>oSmth()
|
||||
}
|
||||
}
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin17() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
class _a {
|
||||
def foo() {
|
||||
this.metaClass.mixin(Foo)
|
||||
new _a().do<caret>Smth()
|
||||
}
|
||||
}
|
||||
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin18() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
class _a {
|
||||
def foo() {
|
||||
_a.metaClass.mixin(Foo)
|
||||
new _a().do<caret>Smth()
|
||||
}
|
||||
}
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin19() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
class _a {
|
||||
def foo() {
|
||||
_a.class.mixin(Foo)
|
||||
new _a().do<caret>Smth()
|
||||
}
|
||||
}
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin20() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public static void doSmth(Script u) {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
class _a {
|
||||
def foo() {
|
||||
_a.mixin(Foo)
|
||||
new _a().do<caret>Smth()
|
||||
}
|
||||
}
|
||||
''', PsiMethod)
|
||||
}
|
||||
|
||||
void testRuntimeMixin21() {
|
||||
resolveByText('''\
|
||||
class Foo {
|
||||
public void doSmth() {
|
||||
println "hello"
|
||||
}
|
||||
}
|
||||
|
||||
class _a {
|
||||
def foo() {
|
||||
_a.mixin(Foo)
|
||||
new _a().do<caret>Smth()
|
||||
}
|
||||
}
|
||||
''', PsiMethod)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user