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