001 /*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.jet.lang.resolve.java.kotlinSignature;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.annotations.TestOnly;
022 import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
023 import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
024 import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
025 import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
026 import org.jetbrains.jet.lang.psi.*;
027 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028 import org.jetbrains.jet.lang.resolve.TypeResolver;
029 import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap;
030 import org.jetbrains.jet.lang.resolve.java.mapping.KotlinToJavaTypesMap;
031 import org.jetbrains.jet.lang.resolve.java.resolver.TypeUsage;
032 import org.jetbrains.jet.lang.resolve.name.FqName;
033 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
034 import org.jetbrains.jet.lang.types.*;
035 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
036 import org.jetbrains.jet.renderer.DescriptorRenderer;
037
038 import java.util.*;
039
040 import static org.jetbrains.jet.lang.resolve.java.resolver.TypeUsage.TYPE_ARGUMENT;
041 import static org.jetbrains.jet.lang.types.Variance.INVARIANT;
042
043 public class TypeTransformingVisitor extends JetVisitor<JetType, Void> {
044 private static boolean strictMode = false;
045
046 private final JetType originalType;
047 private final Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters;
048
049 private final TypeUsage typeUsage;
050
051 private TypeTransformingVisitor(
052 JetType originalType,
053 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters,
054 TypeUsage typeUsage
055 ) {
056 this.originalType = originalType;
057 this.typeUsage = typeUsage;
058 this.originalToAltTypeParameters = Collections.unmodifiableMap(originalToAltTypeParameters);
059 }
060
061 @NotNull
062 public static JetType computeType(
063 @NotNull JetTypeElement alternativeTypeElement,
064 @NotNull JetType originalType,
065 @NotNull Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters,
066 @NotNull TypeUsage typeUsage
067 ) {
068 JetType computedType = alternativeTypeElement.accept(new TypeTransformingVisitor(originalType, originalToAltTypeParameters, typeUsage), null);
069 assert (computedType != null);
070 return computedType;
071 }
072
073 @Override
074 public JetType visitNullableType(@NotNull JetNullableType nullableType, Void aVoid) {
075 if (!originalType.isNullable() && typeUsage != TYPE_ARGUMENT) {
076 throw new AlternativeSignatureMismatchException("Auto type '%s' is not-null, while type in alternative signature is nullable: '%s'",
077 DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(originalType), nullableType.getText());
078 }
079 JetTypeElement innerType = nullableType.getInnerType();
080 assert innerType != null : "Syntax error: " + nullableType.getText();
081 return TypeUtils.makeNullable(computeType(innerType, originalType, originalToAltTypeParameters, typeUsage));
082 }
083
084 @Override
085 public JetType visitFunctionType(@NotNull JetFunctionType type, Void data) {
086 return visitCommonType(type.getReceiverTypeRef() == null
087 ? KotlinBuiltIns.getInstance().getFunction(type.getParameters().size())
088 : KotlinBuiltIns.getInstance().getExtensionFunction(type.getParameters().size()), type);
089 }
090
091 @Override
092 public JetType visitUserType(@NotNull JetUserType type, Void data) {
093 JetUserType qualifier = type.getQualifier();
094
095 //noinspection ConstantConditions
096 String shortName = type.getReferenceExpression().getReferencedName();
097 String longName = (qualifier == null ? "" : qualifier.getText() + ".") + shortName;
098
099 return visitCommonType(longName, type);
100 }
101
102 private JetType visitCommonType(@NotNull ClassDescriptor classDescriptor, @NotNull JetTypeElement type) {
103 return visitCommonType(DescriptorUtils.getFqNameSafe(classDescriptor).asString(), type);
104 }
105
106 @NotNull
107 private JetType visitCommonType(@NotNull String qualifiedName, @NotNull JetTypeElement type) {
108 if (originalType.isError()) {
109 return originalType;
110 }
111 TypeConstructor originalTypeConstructor = originalType.getConstructor();
112 ClassifierDescriptor declarationDescriptor = originalTypeConstructor.getDeclarationDescriptor();
113 assert declarationDescriptor != null;
114 FqName originalClassFqName = DescriptorUtils.getFqNameSafe(declarationDescriptor);
115 ClassDescriptor classFromLibrary = getAutoTypeAnalogWithinBuiltins(originalClassFqName, qualifiedName);
116 if (!isSameName(qualifiedName, originalClassFqName.asString()) && classFromLibrary == null) {
117 throw new AlternativeSignatureMismatchException("Alternative signature type mismatch, expected: %s, actual: %s",
118 qualifiedName, originalClassFqName);
119 }
120
121 TypeConstructor typeConstructor;
122 if (classFromLibrary != null) {
123 typeConstructor = classFromLibrary.getTypeConstructor();
124 }
125 else {
126 typeConstructor = originalTypeConstructor;
127 }
128 ClassifierDescriptor typeConstructorClassifier = typeConstructor.getDeclarationDescriptor();
129 if (typeConstructorClassifier instanceof TypeParameterDescriptor && originalToAltTypeParameters.containsKey(typeConstructorClassifier)) {
130 typeConstructor = originalToAltTypeParameters.get(typeConstructorClassifier).getTypeConstructor();
131 }
132
133 List<TypeProjection> arguments = originalType.getArguments();
134
135 if (arguments.size() != type.getTypeArgumentsAsTypes().size()) {
136 throw new AlternativeSignatureMismatchException("'%s' type in method signature has %d type arguments, while '%s' in alternative signature has %d of them",
137 DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(originalType), arguments.size(), type.getText(),
138 type.getTypeArgumentsAsTypes().size());
139 }
140
141 List<TypeProjection> altArguments = new ArrayList<TypeProjection>();
142 for (int i = 0, size = arguments.size(); i < size; i++) {
143 altArguments.add(getAltArgument(type, typeConstructor, i, arguments.get(i)));
144 }
145
146 JetScope memberScope;
147 if (typeConstructorClassifier instanceof TypeParameterDescriptor) {
148 memberScope = ((TypeParameterDescriptor) typeConstructorClassifier).getUpperBoundsAsType().getMemberScope();
149 }
150 else if (typeConstructorClassifier instanceof ClassDescriptor) {
151 memberScope = ((ClassDescriptor) typeConstructorClassifier).getMemberScope(altArguments);
152 }
153 else {
154 throw new AssertionError("Unexpected class of type constructor classifier "
155 + (typeConstructorClassifier == null ? "null" : typeConstructorClassifier.getClass().getName()));
156 }
157 return new JetTypeImpl(originalType.getAnnotations(), typeConstructor, false,
158 altArguments, memberScope);
159 }
160
161 @NotNull
162 private TypeProjection getAltArgument(
163 @NotNull JetTypeElement type,
164 @NotNull TypeConstructor typeConstructor,
165 int i,
166 @NotNull TypeProjection originalArgument
167 ) {
168 JetTypeReference typeReference = type.getTypeArgumentsAsTypes().get(i); // process both function type and user type
169
170 if (typeReference == null) {
171 // star projection
172 assert type instanceof JetUserType
173 && ((JetUserType) type).getTypeArguments().get(i).getProjectionKind() == JetProjectionKind.STAR;
174
175 return originalArgument;
176 }
177
178 JetTypeElement argumentAlternativeTypeElement = typeReference.getTypeElement();
179 assert argumentAlternativeTypeElement != null;
180
181 TypeParameterDescriptor parameter = typeConstructor.getParameters().get(i);
182 JetType alternativeArgumentType = computeType(argumentAlternativeTypeElement, originalArgument.getType(), originalToAltTypeParameters, TYPE_ARGUMENT);
183 Variance projectionKind = originalArgument.getProjectionKind();
184 Variance altProjectionKind;
185 if (type instanceof JetUserType) {
186 JetTypeProjection typeProjection = ((JetUserType) type).getTypeArguments().get(i);
187 altProjectionKind = TypeResolver.resolveProjectionKind(typeProjection.getProjectionKind());
188 if (altProjectionKind != projectionKind && projectionKind != Variance.INVARIANT) {
189 throw new AlternativeSignatureMismatchException("Projection kind mismatch, actual: %s, in alternative signature: %s",
190 projectionKind, altProjectionKind);
191 }
192 if (altProjectionKind != INVARIANT && parameter.getVariance() != INVARIANT) {
193 if (altProjectionKind == parameter.getVariance()) {
194 if (strictMode) {
195 throw new AlternativeSignatureMismatchException("Projection kind '%s' is redundant",
196 altProjectionKind, DescriptorUtils.getFqName(typeConstructor.getDeclarationDescriptor()));
197 }
198 else {
199 altProjectionKind = projectionKind;
200 }
201 }
202 else {
203 throw new AlternativeSignatureMismatchException("Projection kind '%s' is conflicting with variance of %s",
204 altProjectionKind, DescriptorUtils.getFqName(typeConstructor.getDeclarationDescriptor()));
205 }
206 }
207 }
208 else {
209 altProjectionKind = projectionKind;
210 }
211 return new TypeProjectionImpl(altProjectionKind, alternativeArgumentType);
212 }
213
214 @Nullable
215 private static ClassDescriptor getAutoTypeAnalogWithinBuiltins(@NotNull FqName originalClassFqName, @NotNull String qualifiedName) {
216 FqName javaFqName = KotlinToJavaTypesMap.getInstance().getKotlinToJavaFqName(originalClassFqName);
217 if (javaFqName == null) return null;
218
219 Collection<ClassDescriptor> descriptors = JavaToKotlinClassMap.getInstance().mapPlatformClass(javaFqName);
220 for (ClassDescriptor descriptor : descriptors) {
221 String fqName = DescriptorUtils.getFqName(descriptor).asString();
222 if (isSameName(qualifiedName, fqName)) {
223 return descriptor;
224 }
225 }
226 return null;
227 }
228
229 @Override
230 public JetType visitSelfType(@NotNull JetSelfType type, Void data) {
231 throw new UnsupportedOperationException("Self-types are not supported yet");
232 }
233
234 private static boolean isSameName(String qualifiedName, String fullyQualifiedName) {
235 return fullyQualifiedName.equals(qualifiedName) || fullyQualifiedName.endsWith("." + qualifiedName);
236 }
237
238 @TestOnly
239 public static void setStrictMode(boolean strictMode) {
240 TypeTransformingVisitor.strictMode = strictMode;
241 }
242 }