diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/DelegateSubstitutor.java b/java/java-psi-impl/src/com/intellij/psi/impl/DelegateSubstitutor.java new file mode 100644 index 000000000000..fa8b1bef1a24 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/DelegateSubstitutor.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2015 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiSubstitutor; +import com.intellij.psi.PsiType; +import com.intellij.psi.PsiTypeParameter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public class DelegateSubstitutor implements PsiSubstitutor { + + private final @NotNull PsiSubstitutor myDelegate; + + public DelegateSubstitutor(@NotNull PsiSubstitutor delegate) { + myDelegate = delegate; + } + + @Override + @Nullable + public PsiType substitute(@NotNull PsiTypeParameter typeParameter) { + return myDelegate.substitute(typeParameter); + } + + @Override + public PsiType substitute(@Nullable PsiType type) { + return myDelegate.substitute(type); + } + + @Override + public PsiType substituteWithBoundsPromotion(@NotNull PsiTypeParameter typeParameter) { + return myDelegate.substituteWithBoundsPromotion(typeParameter); + } + + @Override + @NotNull + public PsiSubstitutor put(@NotNull PsiTypeParameter classParameter, PsiType mapping) { + return myDelegate.put(classParameter, mapping); + } + + @Override + @NotNull + public PsiSubstitutor putAll(@NotNull PsiClass parentClass, PsiType[] mappings) { + return myDelegate.putAll(parentClass, mappings); + } + + @Override + @NotNull + public PsiSubstitutor putAll(@NotNull PsiSubstitutor another) { + return myDelegate.putAll(another); + } + + @Override + @NotNull + public Map getSubstitutionMap() { + return myDelegate.getSubstitutionMap(); + } + + @Override + public boolean isValid() { + return myDelegate.isValid(); + } + + @Override + public void ensureValid() { + myDelegate.ensureValid(); + } +} diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java index b2ebeb28fadf..fd97d501e0b2 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionMembersCache.java @@ -21,6 +21,7 @@ import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.SimpleModificationTracker; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; +import com.intellij.psi.impl.DelegateSubstitutor; import com.intellij.psi.impl.compiled.ClsClassImpl; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.util.*; @@ -278,22 +279,44 @@ public class GrTypeDefinitionMembersCache { final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(trait.getProject()); final String helperFQN = trait.getQualifiedName(); final PsiClass traitHelper = psiFacade.findClass(helperFQN + "$Trait$Helper", trait.getResolveScope()); - if (traitHelper != null) { - final PsiType classType = TypesUtil.createJavaLangClassType( - psiFacade.getElementFactory().createType(trait), trait.getProject(), trait.getResolveScope() - ); - for (PsiMethod method : traitHelper.getMethods()) { - if (!method.hasModifierProperty(PsiModifier.STATIC)) continue; - final PsiParameter[] parameters = method.getParameterList().getParameters(); - if (parameters.length <= 0) continue; - final PsiParameter self = parameters[0]; - if (self.getType().equals(classType)) { - addCandidate(GrGdkMethodImpl.createGdkMethod(method, true, "via @Trait"), substitutor); - } + if (traitHelper == null) return; + final PsiType classType = TypesUtil.createJavaLangClassType( + psiFacade.getElementFactory().createType(trait), trait.getProject(), trait.getResolveScope() + ); + final PsiSubstitutor delegateSubstitutor = getSubstitutor(substitutor, trait); + for (PsiMethod method : traitHelper.getMethods()) { + if (!method.hasModifierProperty(PsiModifier.STATIC)) continue; + final PsiParameter[] parameters = method.getParameterList().getParameters(); + if (parameters.length <= 0) continue; + final PsiParameter self = parameters[0]; + if (self.getType().equals(classType)) { + addCandidate(GrGdkMethodImpl.createGdkMethod(method, true, "via @Trait"), delegateSubstitutor); } } } } + + @NotNull + private DelegateSubstitutor getSubstitutor(@NotNull final PsiSubstitutor substitutor, + @NotNull final PsiClass trait) { + final Map substitutionMap = ContainerUtil.newTroveMap(); + for (PsiTypeParameter parameter : trait.getTypeParameters()) { + substitutionMap.put(parameter.getName(), substitutor.substitute(parameter)); + } + return new DelegateSubstitutor(substitutor) { + @Override + public PsiType substitute(@Nullable PsiType type) { + final PsiType substituted = super.substitute(type); + if (type != null && (substituted == null || substituted.equals(type))) { + final PsiType byName = substitutionMap.get(type.getCanonicalText()); + return byName == null ? substituted : byName; + } + else { + return substituted; + } + } + }; + } }.getResult(); for (CandidateInfo candidateInfo : concreteTraitMethods) { List methodsToAdd = getExpandingMethods(candidateInfo);