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    }