IDEA-123484: generate stub type definition for implicit trait type mixing

This commit is contained in:
Max Medvedev
2014-05-23 11:59:32 +04:00
parent a4094bf0a2
commit 9ee6a48458
2 changed files with 250 additions and 52 deletions
@@ -44,6 +44,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.dsl.CustomMembersGenerator;
import org.jetbrains.plugins.groovy.dsl.holders.NonCodeMembersHolder;
import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor;
import org.jetbrains.plugins.groovy.lang.completion.GrPropertyForCompletion;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocComment;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocCommentOwner;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.impl.GrDocCommentUtil;
@@ -59,8 +60,8 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrTraitType;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import org.jetbrains.plugins.groovy.lang.completion.GrPropertyForCompletion;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrImplicitVariable;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightVariable;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
@@ -234,7 +235,6 @@ public class GroovyDocumentationProvider implements CodeDocumentationProvider, E
buffer.append(LINE_SEPARATOR);
}
@SuppressWarnings({"HardCodedStringLiteral"})
private static String generateClassInfo(PsiClass aClass) {
StringBuilder buffer = new StringBuilder();
GroovyFile file = (GroovyFile)aClass.getContainingFile();
@@ -303,8 +303,11 @@ public class GroovyDocumentationProvider implements CodeDocumentationProvider, E
}
public static void appendTypeString(StringBuilder buffer, final PsiType type, PsiElement context) {
if (type != null) {
public static void appendTypeString(@NotNull StringBuilder buffer, @Nullable PsiType type, PsiElement context) {
if (type instanceof GrTraitType) {
generateTraitType(buffer, ((GrTraitType)type), context);
}
else if (type != null) {
JavaDocInfoGenerator.generateType(buffer, type, context);
}
else {
@@ -312,6 +315,18 @@ public class GroovyDocumentationProvider implements CodeDocumentationProvider, E
}
}
private static void generateTraitType(@NotNull StringBuilder buffer, @NotNull GrTraitType type, PsiElement context) {
PsiClassType exprType = type.getExprType();
List<PsiClassType> traitTypes = type.getTraitTypes();
appendTypeString(buffer, exprType, context);
buffer.append(" as ");
for (PsiClassType traitType : traitTypes) {
appendTypeString(buffer, traitType, context);
buffer.append(", ");
}
buffer.delete(buffer.length() - 2, buffer.length());
}
@Override
@Nullable
public List<String> getUrlFor(PsiElement element, PsiElement originalElement) {
@@ -17,11 +17,10 @@ package org.jetbrains.plugins.groovy.lang.psi.impl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.VolatileNotNullLazyValue;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiTypeVisitor;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
@@ -37,32 +36,49 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrSafeCa
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrImplementsClause;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrReferenceList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClassTypeElement;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameterList;
import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Created by Max Medvedev on 20/05/14
*/
public class GrTraitType extends PsiType {
public class GrTraitType extends PsiClassType {
private static final Logger LOG = Logger.getInstance(GrTraitType.class);
private final GrExpression myOriginal;
private final GlobalSearchScope myResolveScope;
private final PsiClassType myExprType;
private final List<PsiClassType> myTraitTypes;
private final GlobalSearchScope myResolveScope;
private final VolatileNotNullLazyValue<PsiType[]> myParameters = new VolatileNotNullLazyValue<PsiType[]>() {
@NotNull
@Override
protected PsiType[] compute() {
List<PsiType> result = ContainerUtil.newArrayList();
ContainerUtil.addAll(result, myExprType.getParameters());
for (PsiClassType type : myTraitTypes) {
ContainerUtil.addAll(result, type.getParameters());
}
return result.toArray(new PsiType[result.size()]);
}
};
public GrTraitType(@NotNull GrExpression original,
@NotNull PsiClassType exprType,
@NotNull List<PsiClassType> traitTypes,
@NotNull GlobalSearchScope resolveScope) {
super(PsiAnnotation.EMPTY_ARRAY);
@NotNull GlobalSearchScope resolveScope, LanguageLevel languageLevel) {
super(languageLevel);
myOriginal = original;
myResolveScope = resolveScope;
myExprType = exprType;
@@ -112,9 +128,33 @@ public class GrTraitType extends PsiType {
return false;
}
@NotNull
@Override
public <A> A accept(@NotNull PsiTypeVisitor<A> visitor) {
return visitor.visitType(this);
public LanguageLevel getLanguageLevel() {
return myLanguageLevel;
}
@NotNull
@Override
public PsiClassType setLanguageLevel(@NotNull LanguageLevel languageLevel) {
return new GrTraitType(myOriginal, myExprType, myTraitTypes, myResolveScope, languageLevel);
}
@Nullable
@Override
public PsiClass resolve() {
return getMockTypeDefinition();
}
@Override
public String getClassName() {
return null;
}
@NotNull
@Override
public PsiType[] getParameters() {
return myParameters.getValue();
}
@NotNull
@@ -126,6 +166,32 @@ public class GrTraitType extends PsiType {
return result;
}
@NotNull
@Override
public ClassResolveResult resolveGenerics() {
return CachedValuesManager.getCachedValue(myOriginal, new CachedValueProvider<ClassResolveResult>() {
@Nullable
@Override
public Result<ClassResolveResult> compute() {
final GrTypeDefinition definition = new MockTypeBuilder().buildMockTypeDefinition();
final PsiSubstitutor substitutor = new SubstitutorBuilder(definition).buildSubstitutor();
return Result.<ClassResolveResult>create(new TraitResolveResult(definition, substitutor), PsiModificationTracker.MODIFICATION_COUNT);
}
});
}
@NotNull
@Override
public PsiClassType rawType() {
return new GrTraitType(myOriginal, myExprType.rawType(), ContainerUtil.map(myTraitTypes, new Function<PsiClassType, PsiClassType>() {
@Override
public PsiClassType fun(PsiClassType type) {
return type.rawType();
}
}), myResolveScope, myLanguageLevel);
}
@NotNull
@Override
public GlobalSearchScope getResolveScope() {
@@ -138,7 +204,7 @@ public class GrTraitType extends PsiType {
@Nullable
@Override
public Result<GrTypeDefinition> compute() {
return Result.create(buildMockTypeDefinition(), PsiModificationTracker.MODIFICATION_COUNT);
return Result.create(new MockTypeBuilder().buildMockTypeDefinition(), PsiModificationTracker.MODIFICATION_COUNT);
}
});
}
@@ -151,39 +217,6 @@ public class GrTraitType extends PsiType {
return Collections.unmodifiableList(myTraitTypes);
}
@Nullable
private GrTypeDefinition buildMockTypeDefinition() {
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myOriginal.getProject());
try {
GrTypeDefinition definition = factory.createTypeDefinition("class ___________Temp______ <T> extends Super implements Trait {}");
replaceReferenceWith(factory, definition.getExtendsClause(), myExprType);
addReferencesWith(factory, definition.getImplementsClause(), myTraitTypes);
return definition;
}
catch (IncorrectOperationException e) {
return null;
}
}
private static void addReferencesWith(GroovyPsiElementFactory factory, GrImplementsClause clause, List<PsiClassType> traitTypes) {
clause.getReferenceElementsGroovy()[0].delete();
for (PsiClassType type : traitTypes) {
GrCodeReferenceElement ref = ((GrClassTypeElement)factory.createTypeElement(type)).getReferenceElement();
clause.add(ref);
}
}
private static void replaceReferenceWith(@NotNull GroovyPsiElementFactory factory,
@Nullable GrReferenceList clause,
@NotNull PsiClassType type) {
LOG.assertTrue(clause != null);
GrCodeReferenceElement mockSuperReference = clause.getReferenceElementsGroovy()[0];
GrClassTypeElement superTypeElement = ((GrClassTypeElement)factory.createTypeElement(type));
GrCodeReferenceElement superReference = superTypeElement.getReferenceElement();
mockSuperReference.replace(superReference);
}
public GrTraitType erasure() {
PsiClassType exprType = (PsiClassType)TypeConversionUtil.erasure(myExprType);
List<PsiClassType> traitTypes = ContainerUtil.map(myTraitTypes, new Function<PsiClassType, PsiClassType>() {
@@ -192,7 +225,7 @@ public class GrTraitType extends PsiType {
return (PsiClassType)TypeConversionUtil.erasure(type);
}
});
return new GrTraitType(myOriginal, exprType, traitTypes, myResolveScope);
return new GrTraitType(myOriginal, exprType, traitTypes, myResolveScope, LanguageLevel.JDK_1_5);
}
@Nullable
@@ -206,7 +239,8 @@ public class GrTraitType extends PsiType {
PsiType type = typeElement.getType();
if (!GrTraitUtil.isTrait(PsiTypesUtil.getPsiClass(type))) return null;
return new GrTraitType(safeCastExpression, ((PsiClassType)exprType), Collections.singletonList((PsiClassType)type), safeCastExpression.getResolveScope());
return new GrTraitType(safeCastExpression, ((PsiClassType)exprType), Collections.singletonList((PsiClassType)type), safeCastExpression.getResolveScope(),
LanguageLevel.JDK_1_5);
}
@@ -215,6 +249,155 @@ public class GrTraitType extends PsiType {
@NotNull PsiClassType exprType,
@NotNull List<PsiClassType> traitTypes,
@NotNull GlobalSearchScope resolveScope) {
return new GrTraitType(context, exprType, traitTypes, resolveScope);
return new GrTraitType(context, exprType, traitTypes, resolveScope, LanguageLevel.JDK_1_5);
}
private static class TraitResolveResult implements ClassResolveResult {
private final GrTypeDefinition myDefinition;
private final PsiSubstitutor mySubstitutor;
public TraitResolveResult(GrTypeDefinition definition, PsiSubstitutor substitutor) {
myDefinition = definition;
mySubstitutor = substitutor;
}
@Override
public GrTypeDefinition getElement() {
return myDefinition;
}
@NotNull
@Override
public PsiSubstitutor getSubstitutor() {
return mySubstitutor;
}
@Override
public boolean isPackagePrefixPackageReference() {
return false;
}
@Override
public boolean isAccessible() {
return true;
}
@Override
public boolean isStaticsScopeCorrect() {
return true;
}
@Override
public PsiElement getCurrentFileResolveScope() {
return null;
}
@Override
public boolean isValidResult() {
return true;
}
}
private class MockTypeBuilder {
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myOriginal.getProject());
@Nullable
public GrTypeDefinition buildMockTypeDefinition() {
try {
StringBuilder buffer = new StringBuilder("class _____________Temp___________ ");
prepareGenerics(buffer);
buffer.append(" extends Super implements Trait {}");
GrTypeDefinition definition = factory.createTypeDefinition(buffer.toString());
replaceReferenceWith(definition.getExtendsClause(), myExprType);
addReferencesWith(definition.getImplementsClause(), myTraitTypes, myExprType.getParameterCount());
return definition;
}
catch (IncorrectOperationException e) {
return null;
}
}
private void prepareGenerics(StringBuilder buffer) {
int count = myExprType.getParameterCount();
for (PsiClassType trait : myTraitTypes) {
count += trait.getParameterCount();
}
if (count == 0) return;
buffer.append('<');
for (int i = 0; i < count; i++) {
buffer.append("T").append(i).append(",");
}
buffer.replace(buffer.length() - 1, buffer.length(), ">");
}
private void addReferencesWith(@Nullable GrImplementsClause clause, @NotNull List<PsiClassType> traitTypes, int parameterOffset) {
LOG.assertTrue(clause != null);
clause.getReferenceElementsGroovy()[0].delete();
for (PsiClassType type : traitTypes) {
processType(clause, type, parameterOffset);
parameterOffset += type.getParameterCount();
}
}
private void replaceReferenceWith(@Nullable GrReferenceList clause, @NotNull PsiClassType type) {
LOG.assertTrue(clause != null);
clause.getReferenceElementsGroovy()[0].delete();
processType(clause, type, 0);
}
private void processType(@NotNull GrReferenceList clause, @NotNull PsiClassType type, int parameterOffset) {
PsiClass resolved = type.resolve();
if (resolved != null) {
String qname = resolved.getQualifiedName();
StringBuilder buffer = new StringBuilder();
buffer.append(qname);
int parameterCount = type.getParameterCount();
if (parameterCount > 0) {
buffer.append('<');
for (int i = 0; i < parameterCount; i++) {
buffer.append("T").append(parameterOffset + i).append(',');
}
buffer.replace(buffer.length() - 1, buffer.length(), ">");
}
GrCodeReferenceElement ref = factory.createCodeReferenceElementFromText(buffer.toString());
clause.add(ref);
}
}
}
private class SubstitutorBuilder {
private final GrTypeParameter[] myParameters;
private int myOffset = 0;
public SubstitutorBuilder(@NotNull GrTypeDefinition definition) {
GrTypeParameterList typeParameterList = definition.getTypeParameterList();
myParameters = typeParameterList != null ? typeParameterList.getTypeParameters() : GrTypeParameter.EMPTY_ARRAY;
}
@NotNull
public PsiSubstitutor buildSubstitutor() {
if (myParameters.length == 0) return PsiSubstitutor.EMPTY;
Map<PsiTypeParameter, PsiType> map = ContainerUtil.newLinkedHashMap();
putMappingAndReturnOffset(map, myExprType);
for (PsiClassType type : myTraitTypes) {
putMappingAndReturnOffset(map, type);
}
return JavaPsiFacade.getElementFactory(myOriginal.getProject()).createSubstitutor(map);
}
private void putMappingAndReturnOffset(@NotNull Map<PsiTypeParameter, PsiType> map, @NotNull PsiClassType type) {
PsiType[] args = type.getParameters();
for (int i = 0; i < args.length; i++) {
map.put(myParameters[myOffset + i], args[i]);
}
myOffset += args.length;
}
}
}