Migrate PropagationHeuristics from PSI to JavaElement

Aside from refactorings, minor changes in logic are made:
- to find out if a type of a value parameter is vararg ('ellipsis type'), we
  now check if the method is vararg and the parameter is its last parameter
  (instead of 'instanceof PsiEllipsisType')
- 'visitedSuperclasses' is now a Set: this better reflects what it's supposed
  to represent. Also result check of the 'add()' method on a List was useless
This commit is contained in:
Alexander Udalov
2013-08-02 21:21:14 +04:00
parent 2c4c1d7cc6
commit be5ba7fa0d
8 changed files with 158 additions and 109 deletions
@@ -18,8 +18,6 @@ package org.jetbrains.jet.lang.resolve.java;
import com.google.common.collect.ImmutableSet;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMember;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.impl.compiled.ClsClassImpl;
import com.intellij.psi.util.PsiFormatUtil;
@@ -118,23 +116,19 @@ public final class DescriptorResolverUtils {
return isEnumClassObject(ownerDescriptor) == shouldBeInEnumClassObject(member);
}
public static boolean isObjectMethodInInterface(@NotNull PsiMember member) {
if (!(member instanceof PsiMethod)) {
public static boolean isObjectMethodInInterface(@NotNull JavaMember member) {
if (!(member instanceof JavaMethod)) {
return false;
}
PsiClass containingClass = member.getContainingClass();
assert containingClass != null : "containing class is null for " + member;
JavaClass containingClass = member.getContainingClass();
assert containingClass != null : "Containing class is null for member: " + member;
if (!containingClass.isInterface()) {
return false;
}
return isObjectMethod((PsiMethod) member);
return containingClass.isInterface() && isObjectMethod((JavaMethod) member);
}
public static boolean isObjectMethod(@NotNull PsiMethod method) {
public static boolean isObjectMethod(@NotNull JavaMethod method) {
String formattedMethod = PsiFormatUtil.formatMethod(
method, PsiSubstitutor.EMPTY, SHOW_NAME | SHOW_PARAMETERS, SHOW_TYPE | SHOW_FQ_CLASS_NAMES);
method.getPsi(), PsiSubstitutor.EMPTY, SHOW_NAME | SHOW_PARAMETERS, SHOW_TYPE | SHOW_FQ_CLASS_NAMES);
return OBJECT_METHODS.contains(formattedMethod);
}
@@ -16,7 +16,10 @@
package org.jetbrains.jet.lang.resolve.java.kotlinSignature;
import com.google.common.collect.*;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiSubstitutor;
@@ -25,6 +28,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.renderer.DescriptorRenderer;
@@ -45,24 +50,26 @@ public class JavaToKotlinMethodMap {
}
@NotNull
public List<FunctionDescriptor> getFunctions(@NotNull PsiMethod psiMethod, @NotNull ClassDescriptor containingClass) {
ImmutableCollection<ClassData> classDatas = mapContainer.map.get(psiMethod.getContainingClass().getQualifiedName());
public List<FunctionDescriptor> getFunctions(
@NotNull JavaMethod javaMethod,
@NotNull FqName classFqName,
@NotNull ClassDescriptor containingClass
) {
ImmutableCollection<ClassData> classDatas = mapContainer.map.get(classFqName.asString());
List<FunctionDescriptor> result = Lists.newArrayList();
Set<ClassDescriptor> allSuperClasses = DescriptorUtils.getAllSuperClasses(containingClass);
String serializedPsiMethod = serializePsiMethod(psiMethod);
String serializedMethod = serializePsiMethod(javaMethod.getPsi());
for (ClassData classData : classDatas) {
String expectedSerializedFunction = classData.method2Function.get(serializedPsiMethod);
String expectedSerializedFunction = classData.method2Function.get(serializedMethod);
if (expectedSerializedFunction == null) continue;
ClassDescriptor kotlinClass = classData.kotlinClass;
if (!allSuperClasses.contains(kotlinClass)) continue;
Collection<FunctionDescriptor> functions =
kotlinClass.getDefaultType().getMemberScope().getFunctions(Name.identifier(psiMethod.getName()));
Collection<FunctionDescriptor> functions = kotlinClass.getDefaultType().getMemberScope().getFunctions(javaMethod.getName());
for (FunctionDescriptor function : functions) {
if (expectedSerializedFunction.equals(serializeFunction(function))) {
@@ -18,15 +18,18 @@ package org.jetbrains.jet.lang.resolve.java.kotlinSignature;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intellij.openapi.util.Comparing;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiSubstitutorImpl;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.Visibilities;
import org.jetbrains.jet.lang.resolve.java.resolver.JavaSupertypeResolver;
import org.jetbrains.jet.lang.resolve.java.structure.*;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.JetTypeImpl;
@@ -36,12 +39,7 @@ import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.intellij.psi.util.TypeConversionUtil.erasure;
import java.util.*;
// This class contains heuristics for processing corner cases in propagation
class PropagationHeuristics {
@@ -123,7 +121,7 @@ class PropagationHeuristics {
}
@NotNull
static List<PsiMethod> getSuperMethods(@NotNull PsiMethod method) {
static List<JavaMethod> getSuperMethods(@NotNull JavaMethod method) {
return new SuperMethodCollector(method).collect();
}
@@ -131,79 +129,83 @@ class PropagationHeuristics {
}
private static class SuperMethodCollector {
private final PsiMethod initialMethod;
private final String initialMethodName;
private final List<PsiType> initialParametersErasure;
private final JavaMethod initialMethod;
private final Name initialMethodName;
private final List<JavaType> initialParametersErasure;
private final List<PsiClass> visitedSuperclasses = Lists.newArrayList();
private final List<PsiMethod> collectedMethods = Lists.newArrayList();
private final Set<JavaClass> visitedSuperclasses = Sets.newHashSet();
private final List<JavaMethod> collectedMethods = Lists.newArrayList();
private SuperMethodCollector(@NotNull PsiMethod initialMethod) {
private SuperMethodCollector(@NotNull JavaMethod initialMethod) {
this.initialMethod = initialMethod;
initialMethodName = initialMethod.getName();
PsiParameterList parameterList = initialMethod.getParameterList();
initialParametersErasure = Lists.newArrayListWithExpectedSize(parameterList.getParametersCount());
for (PsiParameter parameter : parameterList.getParameters()) {
initialParametersErasure.add(erasureNoEllipsis(parameter.getType()));
Collection<JavaValueParameter> valueParameters = initialMethod.getValueParameters();
initialParametersErasure = Lists.newArrayListWithExpectedSize(valueParameters.size());
for (Iterator<JavaValueParameter> iterator = valueParameters.iterator(); iterator.hasNext(); ) {
JavaType type = iterator.next().getType();
boolean isVararg = initialMethod.isVararg() && !iterator.hasNext();
initialParametersErasure.add(erasure(varargToArray(type, isVararg)));
}
}
public List<PsiMethod> collect() {
@NotNull
public List<JavaMethod> collect() {
if (!canHaveSuperMethod(initialMethod)) {
return Collections.emptyList();
}
PsiClass containingClass = initialMethod.getContainingClass();
JavaClass containingClass = initialMethod.getContainingClass();
assert containingClass != null : " containing class is null for " + initialMethod;
for (PsiClassType superType : containingClass.getSuperTypes()) {
collectFromSupertype(superType);
for (JavaClassifierType supertype : containingClass.getSupertypes()) {
collectFromSupertype(supertype);
}
return collectedMethods;
}
private void collectFromSupertype(PsiClassType type) {
PsiClass klass = type.resolve();
if (klass == null) {
return;
}
if (!visitedSuperclasses.add(klass)) {
return;
}
private void collectFromSupertype(@NotNull JavaClassifierType type) {
JavaClassifier classifier = type.getClassifier();
if (!(classifier instanceof JavaClass)) return;
PsiSubstitutor supertypeSubstitutor = getErasedSubstitutor(type);
for (PsiMethod methodFromSuper : klass.getMethods()) {
JavaClass klass = (JavaClass) classifier;
if (!visitedSuperclasses.add(klass)) return;
JavaTypeSubstitutor supertypeSubstitutor = getErasedSubstitutor(type);
for (JavaMethod methodFromSuper : klass.getMethods()) {
if (isSubMethodOf(methodFromSuper, supertypeSubstitutor)) {
collectedMethods.add(methodFromSuper);
return;
}
}
for (PsiType superType : type.getSuperTypes()) {
assert superType instanceof PsiClassType : "supertype is not a PsiClassType for " + type + ": " + superType;
collectFromSupertype((PsiClassType) superType);
for (JavaClassifierType supertype : type.getSupertypes()) {
collectFromSupertype(supertype);
}
}
private boolean isSubMethodOf(@NotNull PsiMethod methodFromSuper, @NotNull PsiSubstitutor supertypeSubstitutor) {
private boolean isSubMethodOf(@NotNull JavaMethod methodFromSuper, @NotNull JavaTypeSubstitutor supertypeSubstitutor) {
if (!methodFromSuper.getName().equals(initialMethodName)) {
return false;
}
PsiParameterList fromSuperParameterList = methodFromSuper.getParameterList();
if (fromSuperParameterList.getParametersCount() != initialParametersErasure.size()) {
Collection<JavaValueParameter> fromSuperParameters = methodFromSuper.getValueParameters();
if (fromSuperParameters.size() != initialParametersErasure.size()) {
return false;
}
for (int i = 0; i < initialParametersErasure.size(); i++) {
PsiType originalType = initialParametersErasure.get(i);
PsiType typeFromSuper = fromSuperParameterList.getParameters()[i].getType();
PsiType typeFromSuperErased = erasureNoEllipsis(supertypeSubstitutor.substitute(typeFromSuper));
Iterator<JavaType> originalIterator = initialParametersErasure.iterator();
Iterator<JavaValueParameter> superIterator = fromSuperParameters.iterator();
while (originalIterator.hasNext()) {
JavaType originalType = originalIterator.next();
JavaType typeFromSuper = superIterator.next().getType();
if (!Comparing.equal(originalType, typeFromSuperErased)) {
boolean isVarargType = methodFromSuper.isVararg() && !superIterator.hasNext();
JavaType typeFromSuperErased = erasure(varargToArray(supertypeSubstitutor.substitute(typeFromSuper), isVarargType));
if (!originalType.equals(typeFromSuperErased)) {
return false;
}
}
@@ -211,28 +213,33 @@ class PropagationHeuristics {
return true;
}
private static PsiType erasureNoEllipsis(PsiType type) {
if (type instanceof PsiEllipsisType) {
return erasureNoEllipsis(((PsiEllipsisType) type).toArrayType());
}
return erasure(type);
@NotNull
private static JavaType varargToArray(@NotNull JavaType type, boolean isVararg) {
return isVararg ? JavaArrayType.create(((JavaArrayType) type).getComponentType()) : type;
}
private static PsiSubstitutor getErasedSubstitutor(PsiClassType type) {
Map<PsiTypeParameter, PsiType> unerasedMap = type.resolveGenerics().getSubstitutor().getSubstitutionMap();
Map<PsiTypeParameter, PsiType> erasedMap = Maps.newHashMapWithExpectedSize(unerasedMap.size());
for (Map.Entry<PsiTypeParameter, PsiType> entry : unerasedMap.entrySet()) {
erasedMap.put(entry.getKey(), erasure(entry.getValue()));
}
return PsiSubstitutorImpl.createSubstitutor(erasedMap);
@NotNull
private static JavaType erasure(@NotNull JavaType type) {
return JavaType.create(TypeConversionUtil.erasure(type.getPsi()));
}
private static boolean canHaveSuperMethod(PsiMethod method) {
if (method.isConstructor()) return false;
if (method.hasModifierProperty(PsiModifier.STATIC)) return false;
if (method.hasModifierProperty(PsiModifier.PRIVATE)) return false;
PsiClass containingClass = method.getContainingClass();
return containingClass != null && !CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName());
@NotNull
private static JavaTypeSubstitutor getErasedSubstitutor(@NotNull JavaClassifierType type) {
Map<JavaTypeParameter, JavaType> unerasedMap = type.getSubstitutor().getSubstitutionMap();
Map<JavaTypeParameter, JavaType> erasedMap = Maps.newHashMapWithExpectedSize(unerasedMap.size());
for (Map.Entry<JavaTypeParameter, JavaType> entry : unerasedMap.entrySet()) {
JavaType value = entry.getValue();
erasedMap.put(entry.getKey(), value == null ? null : erasure(value));
}
return JavaTypeSubstitutor.create(erasedMap);
}
private static boolean canHaveSuperMethod(@NotNull JavaMethod method) {
if (method.isConstructor() || method.isStatic() || method.getVisibility() == Visibilities.PRIVATE) {
return false;
}
JavaClass containingClass = method.getContainingClass();
return containingClass != null && !JavaSupertypeResolver.OBJECT_FQ_NAME.equals(containingClass.getFqName());
}
}
}
@@ -20,7 +20,6 @@ import com.google.common.collect.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
@@ -40,6 +39,7 @@ import org.jetbrains.jet.lang.resolve.java.TypeUsage;
import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetClsMethod;
import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap;
import org.jetbrains.jet.lang.resolve.java.resolver.JavaValueParameterResolver;
import org.jetbrains.jet.lang.resolve.java.structure.JavaClass;
import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
@@ -217,30 +217,29 @@ public class SignaturesPropagationData {
Map<ClassDescriptor, JetType> superclassToSupertype = getSuperclassToSupertypeMap(containingClass);
Multimap<FqName, Pair<FunctionDescriptor, PsiMethod>> superclassToFunctions =
Multimap<FqName, Pair<FunctionDescriptor, JavaMethod>> superclassToFunctions =
getSuperclassToFunctionsMultimap(method, trace.getBindingContext(), containingClass);
for (PsiMethod superMethod : PropagationHeuristics.getSuperMethods(method.getPsi())) {
PsiClass psiClass = superMethod.getContainingClass();
assert psiClass != null;
String classFqNameString = psiClass.getQualifiedName();
assert classFqNameString != null;
FqName classFqName = new FqName(classFqNameString);
for (JavaMethod superMethod : PropagationHeuristics.getSuperMethods(method)) {
JavaClass javaClass = superMethod.getContainingClass();
assert javaClass != null : "Super method doesn't have a containing class: " + superMethod;
FqName classFqName = javaClass.getFqName();
assert classFqName != null : "Class FQ name should not be null: " + javaClass;
if (!JavaToKotlinClassMap.getInstance().mapPlatformClass(classFqName).isEmpty()) {
for (FunctionDescriptor superFun : JavaToKotlinMethodMap.INSTANCE.getFunctions(superMethod, containingClass)) {
for (FunctionDescriptor superFun : JavaToKotlinMethodMap.INSTANCE.getFunctions(superMethod, classFqName, containingClass)) {
superFunctions.add(substituteSuperFunction(superclassToSupertype, superFun));
}
continue;
}
DeclarationDescriptor superFun = superMethod instanceof JetClsMethod
? trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, ((JetClsMethod) superMethod).getOrigin())
DeclarationDescriptor superFun = superMethod.getPsi() instanceof JetClsMethod
? trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, ((JetClsMethod) superMethod.getPsi()).getOrigin())
: findSuperFunction(superclassToFunctions.get(classFqName), superMethod);
if (superFun == null) {
// Super methods which are Object methods in interfaces are not loaded by JDR.
if (!DescriptorResolverUtils.isObjectMethodInInterface(superMethod)) {
reportCantFindSuperFunction(method.getPsi());
reportCantFindSuperFunction(method);
}
continue;
}
@@ -264,12 +263,12 @@ public class SignaturesPropagationData {
}
@NotNull
private static Multimap<FqName, Pair<FunctionDescriptor, PsiMethod>> getSuperclassToFunctionsMultimap(
private static Multimap<FqName, Pair<FunctionDescriptor, JavaMethod>> getSuperclassToFunctionsMultimap(
@NotNull JavaMethod method,
@NotNull BindingContext bindingContext,
@NotNull ClassDescriptor containingClass
) {
Multimap<FqName, Pair<FunctionDescriptor, PsiMethod>> result = HashMultimap.create();
Multimap<FqName, Pair<FunctionDescriptor, JavaMethod>> result = HashMultimap.create();
Name functionName = method.getName();
int parameterCount = method.getValueParameters().size();
@@ -285,7 +284,7 @@ public class SignaturesPropagationData {
fun.getValueParameters().size() + (fun.getReceiverParameter() != null ? 1 : 0) == parameterCount) {
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, fun);
if (declaration instanceof PsiMethod) {
result.put(fqName, Pair.create(fun, (PsiMethod) declaration));
result.put(fqName, Pair.create(fun, new JavaMethod((PsiMethod) declaration)));
} // else declaration is null or JetNamedFunction: both cases are processed later
}
}
@@ -295,12 +294,12 @@ public class SignaturesPropagationData {
@Nullable
private static DeclarationDescriptor findSuperFunction(
@NotNull Collection<Pair<FunctionDescriptor, PsiMethod>> superFunctionCandidates,
@NotNull PsiMethod superMethod
@NotNull Collection<Pair<FunctionDescriptor, JavaMethod>> superFunctionCandidates,
@NotNull JavaMethod superMethod
) {
PsiManager psiManager = PsiManager.getInstance(superMethod.getProject());
for (Pair<FunctionDescriptor, PsiMethod> candidate : superFunctionCandidates) {
if (psiManager.areElementsEquivalent(candidate.second, superMethod)) {
PsiManager psiManager = PsiManager.getInstance(superMethod.getPsi().getProject());
for (Pair<FunctionDescriptor, JavaMethod> candidate : superFunctionCandidates) {
if (psiManager.areElementsEquivalent(candidate.second.getPsi(), superMethod.getPsi())) {
return candidate.first;
}
}
@@ -693,8 +692,8 @@ public class SignaturesPropagationData {
return builtIns.isArray(type) || builtIns.isPrimitiveArray(type);
}
private static void reportCantFindSuperFunction(@NotNull PsiMethod psiMethod) {
String errorMessage = "Can't find super function for " + psiMethod + " defined in " + psiMethod.getContainingClass();
private static void reportCantFindSuperFunction(@NotNull JavaMethod javaMethod) {
String errorMessage = "Can't find super function for " + javaMethod + " defined in " + javaMethod.getContainingClass();
if (SystemInfo.isMac) {
LOG.error("Remove duplicates from your JDK definition\n" + errorMessage);
}
@@ -375,7 +375,7 @@ public class SingleAbstractMethodUtils {
return true;
}
for (JavaMethod method : javaClass.getMethods()) {
if (DescriptorResolverUtils.isObjectMethod(method.getPsi())) { // e.g., ignore toString() declared in interface
if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface
continue;
}
if (!method.getTypeParameters().isEmpty()) {
@@ -211,7 +211,7 @@ import java.util.Map;
return false;
}
if (DescriptorResolverUtils.isObjectMethodInInterface(member.getPsi())) {
if (DescriptorResolverUtils.isObjectMethodInInterface(member)) {
return false;
}
@@ -30,6 +30,11 @@ public class JavaArrayType extends JavaType {
return (PsiArrayType) super.getPsi();
}
@NotNull
public static JavaArrayType create(@NotNull JavaType elementType) {
return new JavaArrayType(elementType.getPsi().createArrayType());
}
@NotNull
public JavaType getComponentType() {
return JavaType.create(getPsi().getComponentType());
@@ -17,25 +17,62 @@
package org.jetbrains.jet.lang.resolve.java.structure;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiTypeParameter;
import com.intellij.psi.impl.PsiSubstitutorImpl;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
public class JavaTypeSubstitutor {
private final PsiSubstitutor psiSubstitutor;
private Map<JavaTypeParameter, JavaType> substitutionMap;
public JavaTypeSubstitutor(@NotNull PsiSubstitutor psiSubstitutor) {
this.psiSubstitutor = psiSubstitutor;
}
public JavaTypeSubstitutor(@NotNull PsiSubstitutor psiSubstitutor, @NotNull Map<JavaTypeParameter, JavaType> substitutionMap) {
this(psiSubstitutor);
this.substitutionMap = substitutionMap;
}
@NotNull
public PsiSubstitutor getPsi() {
return psiSubstitutor;
}
@NotNull
public static JavaTypeSubstitutor create(@NotNull Map<JavaTypeParameter, JavaType> substitutionMap) {
Map<PsiTypeParameter, PsiType> psiMap = new HashMap<PsiTypeParameter, PsiType>();
for (Map.Entry<JavaTypeParameter, JavaType> entry : substitutionMap.entrySet()) {
JavaType value = entry.getValue();
psiMap.put(entry.getKey().getPsi(), value == null ? null : value.getPsi());
}
PsiSubstitutor psiSubstitutor = PsiSubstitutorImpl.createSubstitutor(psiMap);
return new JavaTypeSubstitutor(psiSubstitutor, substitutionMap);
}
@NotNull
public JavaType substitute(@NotNull JavaType type) {
return JavaType.create(getPsi().substitute(type.getPsi()));
}
@NotNull
public Map<JavaTypeParameter, JavaType> getSubstitutionMap() {
if (substitutionMap == null) {
Map<PsiTypeParameter, PsiType> psiMap = psiSubstitutor.getSubstitutionMap();
substitutionMap = new HashMap<JavaTypeParameter, JavaType>();
for (Map.Entry<PsiTypeParameter, PsiType> entry : psiMap.entrySet()) {
PsiType value = entry.getValue();
substitutionMap.put(new JavaTypeParameter(entry.getKey()), value == null ? null : JavaType.create(value));
}
}
return substitutionMap;
}
@Override
public int hashCode() {
return getPsi().hashCode();