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.JavaPackage;
030    import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap;
031    import org.jetbrains.jet.lang.resolve.java.mapping.KotlinToJavaTypesMap;
032    import org.jetbrains.jet.lang.resolve.java.resolver.TypeUsage;
033    import org.jetbrains.jet.lang.resolve.name.FqName;
034    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
035    import org.jetbrains.jet.lang.types.*;
036    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
037    import org.jetbrains.jet.renderer.DescriptorRenderer;
038    
039    import java.util.*;
040    
041    import static org.jetbrains.jet.lang.resolve.java.resolver.TypeUsage.TYPE_ARGUMENT;
042    import static org.jetbrains.jet.lang.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.getReceiverTypeRef() == 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            FqName originalClassFqName = DescriptorUtils.getFqNameSafe(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 (JavaPackage.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 new JetTypeImpl(originalType.getAnnotations(), typeConstructor, false,
161                                   altArguments, memberScope);
162        }
163    
164        @NotNull
165        private TypeProjection getAltArgument(
166                @NotNull JetTypeElement type,
167                @NotNull TypeConstructor typeConstructor,
168                int i,
169                @NotNull TypeProjection originalArgument
170        ) {
171            JetTypeReference typeReference = type.getTypeArgumentsAsTypes().get(i); // process both function type and user type
172    
173            if (typeReference == null) {
174                // star projection
175                assert type instanceof JetUserType
176                       && ((JetUserType) type).getTypeArguments().get(i).getProjectionKind() == JetProjectionKind.STAR;
177    
178                return originalArgument;
179            }
180    
181            JetTypeElement argumentAlternativeTypeElement = typeReference.getTypeElement();
182            assert argumentAlternativeTypeElement != null;
183    
184            TypeParameterDescriptor parameter = typeConstructor.getParameters().get(i);
185            JetType alternativeArgumentType = computeType(argumentAlternativeTypeElement, originalArgument.getType(), originalToAltTypeParameters, TYPE_ARGUMENT);
186            Variance projectionKind = originalArgument.getProjectionKind();
187            Variance altProjectionKind;
188            if (type instanceof JetUserType) {
189                JetTypeProjection typeProjection = ((JetUserType) type).getTypeArguments().get(i);
190                altProjectionKind = TypeResolver.resolveProjectionKind(typeProjection.getProjectionKind());
191                if (altProjectionKind != projectionKind && projectionKind != Variance.INVARIANT && !JavaPackage.getPLATFORM_TYPES()) {
192                    throw new AlternativeSignatureMismatchException("Projection kind mismatch, actual: %s, in alternative signature: %s",
193                                                                    projectionKind, altProjectionKind);
194                }
195                if (altProjectionKind != INVARIANT && parameter.getVariance() != INVARIANT) {
196                    if (altProjectionKind == parameter.getVariance()) {
197                        if (strictMode) {
198                            throw new AlternativeSignatureMismatchException("Projection kind '%s' is redundant",
199                                    altProjectionKind, DescriptorUtils.getFqName(typeConstructor.getDeclarationDescriptor()));
200                        }
201                        else {
202                            altProjectionKind = projectionKind;
203                        }
204                    }
205                    else {
206                        throw new AlternativeSignatureMismatchException("Projection kind '%s' is conflicting with variance of %s",
207                                altProjectionKind, DescriptorUtils.getFqName(typeConstructor.getDeclarationDescriptor()));
208                    }
209                }
210            }
211            else {
212                altProjectionKind = projectionKind;
213            }
214            return new TypeProjectionImpl(altProjectionKind, alternativeArgumentType);
215        }
216    
217        @Nullable
218        private static ClassDescriptor getAutoTypeAnalogWithinBuiltins(@NotNull FqName originalClassFqName, @NotNull String qualifiedName) {
219            FqName javaFqName = KotlinToJavaTypesMap.getInstance().getKotlinToJavaFqName(originalClassFqName);
220            if (javaFqName == null) return null;
221    
222            Collection<ClassDescriptor> descriptors = JavaToKotlinClassMap.getInstance().mapPlatformClass(javaFqName);
223            for (ClassDescriptor descriptor : descriptors) {
224                String fqName = DescriptorUtils.getFqName(descriptor).asString();
225                if (isSameName(qualifiedName, fqName)) {
226                    return descriptor;
227                }
228            }
229            return null;
230        }
231    
232        @Override
233        public JetType visitSelfType(@NotNull JetSelfType type, Void data) {
234            throw new UnsupportedOperationException("Self-types are not supported yet");
235        }
236    
237        private static boolean isSameName(String qualifiedName, String fullyQualifiedName) {
238            return fullyQualifiedName.equals(qualifiedName) || fullyQualifiedName.endsWith("." + qualifiedName);
239        }
240    
241        @TestOnly
242        public static void setStrictMode(boolean strictMode) {
243            TypeTransformingVisitor.strictMode = strictMode;
244        }
245    }