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