001 /*
002 * Copyright 2010-2015 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.kotlin.resolve.jvm.kotlinSignature;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.annotations.TestOnly;
022 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
023 import org.jetbrains.kotlin.descriptors.ClassDescriptor;
024 import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
025 import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
026 import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl;
027 import org.jetbrains.kotlin.load.java.components.TypeUsage;
028 import org.jetbrains.kotlin.name.ClassId;
029 import org.jetbrains.kotlin.name.FqNameUnsafe;
030 import org.jetbrains.kotlin.platform.JavaToKotlinClassMap;
031 import org.jetbrains.kotlin.psi.*;
032 import org.jetbrains.kotlin.renderer.DescriptorRenderer;
033 import org.jetbrains.kotlin.resolve.DescriptorUtils;
034 import org.jetbrains.kotlin.resolve.TypeResolver;
035 import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolverKt;
036 import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
037 import org.jetbrains.kotlin.resolve.scopes.MemberScope;
038 import org.jetbrains.kotlin.types.*;
039
040 import java.util.*;
041
042 import static org.jetbrains.kotlin.load.java.components.TypeUsage.TYPE_ARGUMENT;
043 import static org.jetbrains.kotlin.types.Variance.INVARIANT;
044
045 public class TypeTransformingVisitor extends KtVisitor<KotlinType, Void> {
046 private static boolean strictMode = false;
047
048 private final KotlinType originalType;
049 private final Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters;
050
051 private final TypeUsage typeUsage;
052
053 private TypeTransformingVisitor(
054 KotlinType originalType,
055 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters,
056 TypeUsage typeUsage
057 ) {
058 this.originalType = originalType;
059 this.typeUsage = typeUsage;
060 this.originalToAltTypeParameters = Collections.unmodifiableMap(originalToAltTypeParameters);
061 }
062
063 @NotNull
064 public static KotlinType computeType(
065 @NotNull KtTypeElement alternativeTypeElement,
066 @NotNull KotlinType originalType,
067 @NotNull Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters,
068 @NotNull TypeUsage typeUsage
069 ) {
070 KotlinType computedType = alternativeTypeElement.accept(new TypeTransformingVisitor(originalType, originalToAltTypeParameters, typeUsage), null);
071 assert (computedType != null);
072 return computedType;
073 }
074
075 @Override
076 public KotlinType visitNullableType(@NotNull KtNullableType nullableType, Void aVoid) {
077 if (!TypeUtils.isNullableType(originalType) && typeUsage != TYPE_ARGUMENT) {
078 throw new AlternativeSignatureMismatchException("Auto type '%s' is not-null, while type in alternative signature is nullable: '%s'",
079 DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(originalType), nullableType.getText());
080 }
081 KtTypeElement innerType = nullableType.getInnerType();
082 assert innerType != null : "Syntax error: " + nullableType.getText();
083 return TypeUtils.makeNullable(computeType(innerType, originalType, originalToAltTypeParameters, typeUsage));
084 }
085
086 @Override
087 public KotlinType visitFunctionType(@NotNull KtFunctionType type, Void data) {
088 KotlinBuiltIns builtIns = JvmPlatform.INSTANCE$.getBuiltIns();
089 return visitCommonType(type.getReceiverTypeReference() == null
090 ? builtIns.getFunction(type.getParameters().size())
091 : builtIns.getExtensionFunction(type.getParameters().size()), type);
092 }
093
094 @Override
095 public KotlinType visitUserType(@NotNull KtUserType type, Void data) {
096 KtUserType qualifier = type.getQualifier();
097
098 //noinspection ConstantConditions
099 String shortName = type.getReferenceExpression().getReferencedName();
100 String longName = (qualifier == null ? "" : qualifier.getText() + ".") + shortName;
101
102 return visitCommonType(longName, type);
103 }
104
105 private KotlinType visitCommonType(@NotNull ClassDescriptor classDescriptor, @NotNull KtTypeElement type) {
106 return visitCommonType(DescriptorUtils.getFqNameSafe(classDescriptor).asString(), type);
107 }
108
109 @NotNull
110 private KotlinType visitCommonType(@NotNull String qualifiedName, @NotNull KtTypeElement type) {
111 if (originalType.isError()) {
112 return originalType;
113 }
114 TypeConstructor originalTypeConstructor = originalType.getConstructor();
115 ClassifierDescriptor declarationDescriptor = originalTypeConstructor.getDeclarationDescriptor();
116 assert declarationDescriptor != null;
117 FqNameUnsafe originalClassFqName = DescriptorUtils.getFqName(declarationDescriptor);
118 ClassDescriptor classFromLibrary = getAutoTypeAnalogWithinBuiltins(originalClassFqName, qualifiedName);
119 if (!isSameName(qualifiedName, originalClassFqName.asString()) && classFromLibrary == null) {
120 throw new AlternativeSignatureMismatchException("Alternative signature type mismatch, expected: %s, actual: %s",
121 qualifiedName, originalClassFqName);
122 }
123
124 TypeConstructor typeConstructor;
125 if (classFromLibrary != null) {
126 typeConstructor = classFromLibrary.getTypeConstructor();
127 }
128 else {
129 typeConstructor = originalTypeConstructor;
130 }
131 ClassifierDescriptor typeConstructorClassifier = typeConstructor.getDeclarationDescriptor();
132 if (typeConstructorClassifier instanceof TypeParameterDescriptor && originalToAltTypeParameters.containsKey(typeConstructorClassifier)) {
133 typeConstructor = originalToAltTypeParameters.get(typeConstructorClassifier).getTypeConstructor();
134 }
135
136 List<TypeProjection> arguments = originalType.getArguments();
137
138 if (arguments.size() != type.getTypeArgumentsAsTypes().size()) {
139 if (JavaDescriptorResolverKt.getPLATFORM_TYPES()) return originalType;
140
141 throw new AlternativeSignatureMismatchException("'%s' type in method signature has %d type arguments, while '%s' in alternative signature has %d of them",
142 DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(originalType), arguments.size(), type.getText(),
143 type.getTypeArgumentsAsTypes().size());
144 }
145
146 List<TypeProjection> altArguments = new ArrayList<TypeProjection>();
147 for (int i = 0, size = arguments.size(); i < size; i++) {
148 altArguments.add(getAltArgument(type, typeConstructor, i, arguments.get(i)));
149 }
150
151 MemberScope memberScope;
152 if (typeConstructorClassifier instanceof TypeParameterDescriptor) {
153 memberScope = typeConstructorClassifier.getDefaultType().getMemberScope();
154 }
155 else if (typeConstructorClassifier instanceof ClassDescriptor) {
156 memberScope = ((ClassDescriptor) typeConstructorClassifier).getMemberScope(altArguments);
157 }
158 else {
159 throw new AssertionError("Unexpected class of type constructor classifier "
160 + (typeConstructorClassifier == null ? "null" : typeConstructorClassifier.getClass().getName()));
161 }
162 return KotlinTypeImpl.create(originalType.getAnnotations(), typeConstructor, false, altArguments, memberScope);
163 }
164
165 @NotNull
166 private TypeProjection getAltArgument(
167 @NotNull KtTypeElement type,
168 @NotNull TypeConstructor typeConstructor,
169 int i,
170 @NotNull TypeProjection originalArgument
171 ) {
172 KtTypeReference typeReference = type.getTypeArgumentsAsTypes().get(i); // process both function type and user type
173
174 if (typeReference == null) {
175 // star projection
176 assert type instanceof KtUserType
177 && ((KtUserType) type).getTypeArguments().get(i).getProjectionKind() == KtProjectionKind.STAR;
178
179 return originalArgument;
180 }
181
182 KtTypeElement argumentAlternativeTypeElement = typeReference.getTypeElement();
183 assert argumentAlternativeTypeElement != null;
184
185 TypeParameterDescriptor parameter = typeConstructor.getParameters().get(i);
186 KotlinType alternativeArgumentType = computeType(argumentAlternativeTypeElement, originalArgument.getType(), originalToAltTypeParameters, TYPE_ARGUMENT);
187 Variance projectionKind = originalArgument.getProjectionKind();
188 Variance altProjectionKind;
189 if (type instanceof KtUserType) {
190 KtTypeProjection typeProjection = ((KtUserType) type).getTypeArguments().get(i);
191 altProjectionKind = TypeResolver.resolveProjectionKind(typeProjection.getProjectionKind());
192 if (altProjectionKind != projectionKind && projectionKind != Variance.INVARIANT && !JavaDescriptorResolverKt.getPLATFORM_TYPES()) {
193 throw new AlternativeSignatureMismatchException("Projection kind mismatch, actual: %s, in alternative signature: %s",
194 projectionKind, altProjectionKind);
195 }
196 if (altProjectionKind != INVARIANT && parameter.getVariance() != INVARIANT) {
197 if (altProjectionKind == parameter.getVariance()) {
198 if (strictMode) {
199 throw new AlternativeSignatureMismatchException("Projection kind '%s' is redundant",
200 altProjectionKind, DescriptorUtils.getFqName(typeConstructor.getDeclarationDescriptor()));
201 }
202 else {
203 altProjectionKind = projectionKind;
204 }
205 }
206 else {
207 throw new AlternativeSignatureMismatchException("Projection kind '%s' is conflicting with variance of %s",
208 altProjectionKind, DescriptorUtils.getFqName(typeConstructor.getDeclarationDescriptor()));
209 }
210 }
211 }
212 else {
213 altProjectionKind = projectionKind;
214 }
215 return new TypeProjectionImpl(altProjectionKind, alternativeArgumentType);
216 }
217
218 @Nullable
219 private static ClassDescriptor getAutoTypeAnalogWithinBuiltins(
220 @NotNull FqNameUnsafe originalClassFqName,
221 @NotNull String qualifiedName
222 ) {
223 ClassId javaClassId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(originalClassFqName);
224 if (javaClassId == null) return null;
225
226 Collection<ClassDescriptor> descriptors = JavaToKotlinClassMap.INSTANCE.mapPlatformClass(javaClassId.asSingleFqName());
227 for (ClassDescriptor descriptor : descriptors) {
228 String fqName = DescriptorUtils.getFqName(descriptor).asString();
229 if (isSameName(qualifiedName, fqName)) {
230 return descriptor;
231 }
232 }
233 return null;
234 }
235
236 @Override
237 public KotlinType visitSelfType(@NotNull KtSelfType type, Void data) {
238 throw new UnsupportedOperationException("Self-types are not supported yet");
239 }
240
241 private static boolean isSameName(String qualifiedName, String fullyQualifiedName) {
242 return fullyQualifiedName.equals(qualifiedName) || fullyQualifiedName.endsWith("." + qualifiedName);
243 }
244
245 @TestOnly
246 public static void setStrictMode(boolean strictMode) {
247 TypeTransformingVisitor.strictMode = strictMode;
248 }
249 }