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 com.intellij.openapi.project.Project;
020    import com.intellij.psi.PsiNamedElement;
021    import com.intellij.util.containers.ComparatorUtil;
022    import com.intellij.util.containers.ContainerUtil;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.kotlin.descriptors.SourceElement;
026    import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
027    import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
028    import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl;
029    import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
030    import org.jetbrains.kotlin.load.java.structure.JavaMember;
031    import org.jetbrains.kotlin.name.Name;
032    import org.jetbrains.kotlin.psi.*;
033    import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolverKt;
034    import org.jetbrains.kotlin.resolve.jvm.JavaResolverUtils;
035    import org.jetbrains.kotlin.types.KotlinType;
036    import org.jetbrains.kotlin.types.TypeSubstitutor;
037    import org.jetbrains.kotlin.types.TypeUtils;
038    import org.jetbrains.kotlin.types.Variance;
039    import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
040    import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
041    
042    import java.util.ArrayList;
043    import java.util.List;
044    import java.util.Map;
045    import java.util.Set;
046    
047    import static org.jetbrains.kotlin.load.java.components.TypeUsage.MEMBER_SIGNATURE_CONTRAVARIANT;
048    import static org.jetbrains.kotlin.load.java.components.TypeUsage.UPPER_BOUND;
049    import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getBuiltIns;
050    
051    public class AlternativeMethodSignatureData extends ElementAlternativeSignatureData {
052        private final KtNamedFunction altFunDeclaration;
053    
054        private List<ValueParameterDescriptor> altValueParameters;
055        private KotlinType altReturnType;
056        private List<TypeParameterDescriptor> altTypeParameters;
057    
058        private Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters;
059    
060        public AlternativeMethodSignatureData(
061                @NotNull JavaMember methodOrConstructor,
062                @Nullable KotlinType receiverType,
063                @NotNull Project project,
064                @NotNull List<ValueParameterDescriptor> valueParameters,
065                @Nullable KotlinType originalReturnType,
066                @NotNull List<TypeParameterDescriptor> methodTypeParameters,
067                boolean hasSuperMethods
068        ) {
069            String signature = SignaturesUtil.getKotlinSignature(methodOrConstructor);
070    
071            if (signature == null) {
072                setAnnotated(false);
073                altFunDeclaration = null;
074                return;
075            }
076    
077            if (receiverType != null) {
078                throw new UnsupportedOperationException("Alternative annotations for extension functions are not supported yet");
079            }
080    
081            setAnnotated(true);
082            altFunDeclaration = KtPsiFactoryKt.KtPsiFactory(project).createFunction(signature);
083    
084            originalToAltTypeParameters = JavaResolverUtils.recreateTypeParametersAndReturnMapping(methodTypeParameters, null);
085    
086            try {
087                checkForSyntaxErrors(altFunDeclaration);
088                checkEqualFunctionNames(altFunDeclaration, methodOrConstructor);
089    
090                computeTypeParameters(methodTypeParameters);
091                computeValueParameters(valueParameters);
092    
093                if (originalReturnType != null) {
094                    altReturnType = computeReturnType(originalReturnType, altFunDeclaration.getTypeReference(), originalToAltTypeParameters);
095                }
096    
097                if (hasSuperMethods) {
098                    checkParameterAndReturnTypesForOverridingMethods(valueParameters, methodTypeParameters, originalReturnType);
099                }
100            }
101            catch (AlternativeSignatureMismatchException e) {
102                setError(e.getMessage());
103            }
104        }
105    
106        public static List<ValueParameterDescriptor> updateNames(
107                List<ValueParameterDescriptor> originalValueParameters,
108                List<ValueParameterDescriptor> altValueParameters
109        ) {
110            List<ValueParameterDescriptor> result = new ArrayList<ValueParameterDescriptor>(originalValueParameters.size());
111            for (int i = 0; i < originalValueParameters.size(); i++) {
112                ValueParameterDescriptor originalValueParameter = originalValueParameters.get(i);
113                ValueParameterDescriptor altValueParameter = altValueParameters.get(i);
114                result.add(originalValueParameter.copy(originalValueParameter.getContainingDeclaration(), altValueParameter.getName()));
115            }
116            return result;
117        }
118    
119        private void checkParameterAndReturnTypesForOverridingMethods(
120                @NotNull List<ValueParameterDescriptor> valueParameters,
121                @NotNull List<TypeParameterDescriptor> methodTypeParameters,
122                @Nullable KotlinType returnType
123        ) {
124            if (JavaDescriptorResolverKt.getPLATFORM_TYPES()) return;
125            TypeSubstitutor substitutor = JavaResolverUtils.createSubstitutorForTypeParameters(originalToAltTypeParameters);
126    
127            for (ValueParameterDescriptor parameter : valueParameters) {
128                int index = parameter.getIndex();
129                ValueParameterDescriptor altParameter = altValueParameters.get(index);
130    
131                KotlinType substituted = substitutor.substitute(parameter.getType(), Variance.INVARIANT);
132                assert substituted != null;
133    
134                if (!TypeUtils.equalTypes(substituted, altParameter.getType())) {
135                    throw new AlternativeSignatureMismatchException(
136                            "Parameter type changed for method which overrides another: " + altParameter.getType()
137                            + ", was: " + parameter.getType());
138                }
139            }
140    
141            // don't check receiver
142    
143            for (TypeParameterDescriptor parameter : methodTypeParameters) {
144                int index = parameter.getIndex();
145    
146                KotlinType substituted = substitutor.substitute(parameter.getUpperBoundsAsType(), Variance.INVARIANT);
147                assert substituted != null;
148    
149                if (!TypeUtils.equalTypes(substituted, altTypeParameters.get(index).getUpperBoundsAsType())) {
150                    throw new AlternativeSignatureMismatchException(
151                            "Type parameter's upper bound changed for method which overrides another: "
152                            + altTypeParameters.get(index).getUpperBoundsAsType() + ", was: " + parameter.getUpperBoundsAsType());
153                }
154            }
155    
156            if (returnType != null) {
157                KotlinType substitutedReturnType = substitutor.substitute(returnType, Variance.INVARIANT);
158                assert substitutedReturnType != null;
159    
160                if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(altReturnType, substitutedReturnType)) {
161                    throw new AlternativeSignatureMismatchException(
162                            "Return type is changed to not subtype for method which overrides another: " + altReturnType + ", was: " + returnType);
163                }
164            }
165        }
166    
167        @NotNull
168        public List<ValueParameterDescriptor> getValueParameters() {
169            checkForErrors();
170            return altValueParameters;
171        }
172    
173        @Nullable
174        public KotlinType getReturnType() {
175            checkForErrors();
176            return altReturnType;
177        }
178    
179        @NotNull
180        public List<TypeParameterDescriptor> getTypeParameters() {
181            checkForErrors();
182            return altTypeParameters;
183        }
184    
185        private void computeValueParameters(@NotNull List<ValueParameterDescriptor> parameterDescriptors) {
186            if (parameterDescriptors.size() != altFunDeclaration.getValueParameters().size()) {
187                throw new AlternativeSignatureMismatchException("Method signature has %d value parameters, but alternative signature has %d",
188                                                                parameterDescriptors.size(), altFunDeclaration.getValueParameters().size());
189            }
190    
191            List<ValueParameterDescriptor> altParamDescriptors = new ArrayList<ValueParameterDescriptor>(parameterDescriptors.size());
192            for (int i = 0; i < parameterDescriptors.size(); i++) {
193                ValueParameterDescriptor originalParameterDescriptor = parameterDescriptors.get(i);
194                KtParameter annotationValueParameter = altFunDeclaration.getValueParameters().get(i);
195    
196                //noinspection ConstantConditions
197                KtTypeElement alternativeTypeElement = annotationValueParameter.getTypeReference().getTypeElement();
198                assert alternativeTypeElement != null;
199    
200                KotlinType alternativeType;
201                KotlinType alternativeVarargElementType;
202    
203                KotlinType originalParamVarargElementType = originalParameterDescriptor.getVarargElementType();
204                if (originalParamVarargElementType == null) {
205                    if (annotationValueParameter.isVarArg()) {
206                        throw new AlternativeSignatureMismatchException("Parameter in method signature is not vararg, but in alternative signature it is vararg");
207                    }
208    
209                    alternativeType = TypeTransformingVisitor.computeType(alternativeTypeElement, originalParameterDescriptor.getType(), originalToAltTypeParameters, MEMBER_SIGNATURE_CONTRAVARIANT);
210                    alternativeVarargElementType = null;
211                }
212                else {
213                    if (!annotationValueParameter.isVarArg()) {
214                        throw new AlternativeSignatureMismatchException("Parameter in method signature is vararg, but in alternative signature it is not");
215                    }
216    
217                    alternativeVarargElementType = TypeTransformingVisitor.computeType(alternativeTypeElement, originalParamVarargElementType,
218                                                                                       originalToAltTypeParameters, MEMBER_SIGNATURE_CONTRAVARIANT);
219                    alternativeType = getBuiltIns(originalParameterDescriptor).getArrayType(Variance.OUT_VARIANCE, alternativeVarargElementType);
220                }
221    
222                Name altName = annotationValueParameter.getNameAsName();
223    
224                altParamDescriptors.add(new ValueParameterDescriptorImpl(
225                        originalParameterDescriptor.getContainingDeclaration(),
226                        null,
227                        originalParameterDescriptor.getIndex(),
228                        originalParameterDescriptor.getAnnotations(),
229                        altName != null ? altName : originalParameterDescriptor.getName(),
230                        alternativeType,
231                        originalParameterDescriptor.declaresDefaultValue(),
232                        originalParameterDescriptor.isCrossinline(),
233                        originalParameterDescriptor.isNoinline(),
234                        alternativeVarargElementType,
235                        SourceElement.NO_SOURCE
236                ));
237            }
238    
239            altValueParameters = altParamDescriptors;
240        }
241    
242        private void computeTypeParameters(List<TypeParameterDescriptor> typeParameters) {
243            if (typeParameters.size() != altFunDeclaration.getTypeParameters().size()) {
244                throw new AlternativeSignatureMismatchException("Method signature has %d type parameters, but alternative signature has %d",
245                                                                typeParameters.size(), altFunDeclaration.getTypeParameters().size());
246            }
247    
248            altTypeParameters = new ArrayList<TypeParameterDescriptor>(typeParameters.size());
249    
250            for (int i = 0; i < typeParameters.size(); i++) {
251                TypeParameterDescriptor originalTypeParamDescriptor = typeParameters.get(i);
252    
253                TypeParameterDescriptorImpl altParamDescriptor = originalToAltTypeParameters.get(originalTypeParamDescriptor);
254                KtTypeParameter altTypeParameter = altFunDeclaration.getTypeParameters().get(i);
255    
256                Set<KotlinType> originalUpperBounds = originalTypeParamDescriptor.getUpperBounds();
257                List<KtTypeReference> altUpperBounds = getUpperBounds(altFunDeclaration, altTypeParameter);
258                if (altUpperBounds.size() != originalUpperBounds.size()) {
259                    if (altUpperBounds.isEmpty()
260                        && originalUpperBounds.size() == 1
261                        && TypeUtilsKt.isDefaultBound(originalUpperBounds.iterator().next())) {
262                        // Only default bound => no error
263                    }
264                    else {
265                        throw new AlternativeSignatureMismatchException("Upper bound number mismatch for %s. Expected %d, but found %d",
266                                                                        originalTypeParamDescriptor.getName(),
267                                                                        originalUpperBounds.size(),
268                                                                        altUpperBounds.size());
269                    }
270                }
271    
272                if (altUpperBounds.isEmpty()) {
273                    altParamDescriptor.addDefaultUpperBound();
274                }
275                else {
276                    int upperBoundIndex = 0;
277                    for (KotlinType upperBound : originalUpperBounds) {
278    
279                        KtTypeElement altTypeElement = altUpperBounds.get(upperBoundIndex).getTypeElement();
280                        assert altTypeElement != null;
281    
282                        altParamDescriptor.addUpperBound(TypeTransformingVisitor.computeType(altTypeElement, upperBound,
283                                                                                             originalToAltTypeParameters, UPPER_BOUND));
284                        upperBoundIndex++;
285                    }
286                }
287    
288                altParamDescriptor.setInitialized();
289                altTypeParameters.add(altParamDescriptor);
290            }
291        }
292    
293        @NotNull
294        private static List<KtTypeReference> getUpperBounds(@NotNull KtFunction function, @NotNull KtTypeParameter jetTypeParameter) {
295            List<KtTypeReference> result = new ArrayList<KtTypeReference>();
296            ContainerUtil.addIfNotNull(result, jetTypeParameter.getExtendsBound());
297    
298            Name name = jetTypeParameter.getNameAsName();
299            if (name == null) return result;
300    
301            for (KtTypeConstraint constraint : function.getTypeConstraints()) {
302                KtSimpleNameExpression parameterName = constraint.getSubjectTypeParameterName();
303                assert parameterName != null : "No parameter name in constraint " + constraint.getText();
304                if (name.equals(parameterName.getReferencedNameAsName())) {
305                    result.add(constraint.getBoundTypeReference());
306                }
307            }
308    
309            return result;
310        }
311    
312        private static void checkEqualFunctionNames(@NotNull PsiNamedElement namedElement, @NotNull JavaMember methodOrConstructor) {
313            if (!ComparatorUtil.equalsNullable(methodOrConstructor.getName().asString(), namedElement.getName())) {
314                throw new AlternativeSignatureMismatchException("Function names mismatch, original: %s, alternative: %s",
315                                                                methodOrConstructor.getName().asString(), namedElement.getName());
316            }
317        }
318    }