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    }