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