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    
043    import java.util.*;
044    
045    import static org.jetbrains.kotlin.load.java.components.TypeUsage.MEMBER_SIGNATURE_CONTRAVARIANT;
046    import static org.jetbrains.kotlin.load.java.components.TypeUsage.UPPER_BOUND;
047    import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
048    
049    public class AlternativeMethodSignatureData extends ElementAlternativeSignatureData {
050        private final JetNamedFunction altFunDeclaration;
051    
052        private List<ValueParameterDescriptor> altValueParameters;
053        private JetType altReturnType;
054        private List<TypeParameterDescriptor> altTypeParameters;
055    
056        private Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters;
057    
058        public AlternativeMethodSignatureData(
059                @NotNull ExternalAnnotationResolver externalAnnotationResolver,
060                @NotNull JavaMember methodOrConstructor,
061                @Nullable JetType receiverType,
062                @NotNull Project project,
063                @NotNull List<ValueParameterDescriptor> valueParameters,
064                @Nullable JetType originalReturnType,
065                @NotNull List<TypeParameterDescriptor> methodTypeParameters,
066                boolean hasSuperMethods
067        ) {
068            String signature = SignaturesUtil.getKotlinSignature(externalAnnotationResolver, methodOrConstructor);
069    
070            if (signature == null) {
071                setAnnotated(false);
072                altFunDeclaration = null;
073                return;
074            }
075    
076            if (receiverType != null) {
077                throw new UnsupportedOperationException("Alternative annotations for extension functions are not supported yet");
078            }
079    
080            setAnnotated(true);
081            altFunDeclaration = JetPsiFactory(project).createFunction(signature);
082    
083            originalToAltTypeParameters = JavaResolverUtils.recreateTypeParametersAndReturnMapping(methodTypeParameters, null);
084    
085            try {
086                checkForSyntaxErrors(altFunDeclaration);
087                checkEqualFunctionNames(altFunDeclaration, methodOrConstructor);
088    
089                computeTypeParameters(methodTypeParameters);
090                computeValueParameters(valueParameters);
091    
092                if (originalReturnType != null) {
093                    altReturnType = computeReturnType(originalReturnType, altFunDeclaration.getTypeReference(), originalToAltTypeParameters);
094                }
095    
096                if (hasSuperMethods) {
097                    checkParameterAndReturnTypesForOverridingMethods(valueParameters, methodTypeParameters, originalReturnType);
098                }
099            }
100            catch (AlternativeSignatureMismatchException e) {
101                setError(e.getMessage());
102            }
103        }
104    
105        public static List<ValueParameterDescriptor> updateNames(
106                List<ValueParameterDescriptor> originalValueParameters,
107                List<ValueParameterDescriptor> altValueParameters
108        ) {
109            List<ValueParameterDescriptor> result = new ArrayList<ValueParameterDescriptor>(originalValueParameters.size());
110            for (int i = 0; i < originalValueParameters.size(); i++) {
111                ValueParameterDescriptor originalValueParameter = originalValueParameters.get(i);
112                ValueParameterDescriptor altValueParameter = altValueParameters.get(i);
113                result.add(originalValueParameter.copy(originalValueParameter.getContainingDeclaration(), altValueParameter.getName()));
114            }
115            return result;
116        }
117    
118        private void checkParameterAndReturnTypesForOverridingMethods(
119                @NotNull List<ValueParameterDescriptor> valueParameters,
120                @NotNull List<TypeParameterDescriptor> methodTypeParameters,
121                @Nullable JetType returnType
122        ) {
123            if (JvmPackage.getPLATFORM_TYPES()) return;
124            TypeSubstitutor substitutor = JavaResolverUtils.createSubstitutorForTypeParameters(originalToAltTypeParameters);
125    
126            for (ValueParameterDescriptor parameter : valueParameters) {
127                int index = parameter.getIndex();
128                ValueParameterDescriptor altParameter = altValueParameters.get(index);
129    
130                JetType substituted = substitutor.substitute(parameter.getType(), Variance.INVARIANT);
131                assert substituted != null;
132    
133                if (!TypeUtils.equalTypes(substituted, altParameter.getType())) {
134                    throw new AlternativeSignatureMismatchException(
135                            "Parameter type changed for method which overrides another: " + altParameter.getType()
136                            + ", was: " + parameter.getType());
137                }
138            }
139    
140            // don't check receiver
141    
142            for (TypeParameterDescriptor parameter : methodTypeParameters) {
143                int index = parameter.getIndex();
144    
145                JetType substituted = substitutor.substitute(parameter.getUpperBoundsAsType(), Variance.INVARIANT);
146                assert substituted != null;
147    
148                if (!TypeUtils.equalTypes(substituted, altTypeParameters.get(index).getUpperBoundsAsType())) {
149                    throw new AlternativeSignatureMismatchException(
150                            "Type parameter's upper bound changed for method which overrides another: "
151                            + altTypeParameters.get(index).getUpperBoundsAsType() + ", was: " + parameter.getUpperBoundsAsType());
152                }
153            }
154    
155            if (returnType != null) {
156                JetType substitutedReturnType = substitutor.substitute(returnType, Variance.INVARIANT);
157                assert substitutedReturnType != null;
158    
159                if (!JetTypeChecker.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 JetType 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>();
191            for (int i = 0, size = parameterDescriptors.size(); i < size; i++) {
192                ValueParameterDescriptor originalParameterDescriptor = parameterDescriptors.get(i);
193                JetParameter annotationValueParameter = altFunDeclaration.getValueParameters().get(i);
194    
195                //noinspection ConstantConditions
196                JetTypeElement alternativeTypeElement = annotationValueParameter.getTypeReference().getTypeElement();
197                assert alternativeTypeElement != null;
198    
199                JetType alternativeType;
200                JetType alternativeVarargElementType;
201    
202                JetType 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 = KotlinBuiltIns.getInstance().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                        alternativeVarargElementType,
232                        SourceElement.NO_SOURCE
233                        ));
234            }
235    
236            altValueParameters = altParamDescriptors;
237        }
238    
239        private void computeTypeParameters(List<TypeParameterDescriptor> typeParameters) {
240            if (typeParameters.size() != altFunDeclaration.getTypeParameters().size()) {
241                throw new AlternativeSignatureMismatchException("Method signature has %d type parameters, but alternative signature has %d",
242                                                                typeParameters.size(), altFunDeclaration.getTypeParameters().size());
243            }
244    
245            altTypeParameters = new ArrayList<TypeParameterDescriptor>();
246    
247            for (int i = 0, size = typeParameters.size(); i < size; i++) {
248                TypeParameterDescriptor originalTypeParamDescriptor = typeParameters.get(i);
249    
250                TypeParameterDescriptorImpl altParamDescriptor = originalToAltTypeParameters.get(originalTypeParamDescriptor);
251                JetTypeParameter altTypeParameter = altFunDeclaration.getTypeParameters().get(i);
252    
253                Set<JetType> originalUpperBounds = originalTypeParamDescriptor.getUpperBounds();
254                List<JetTypeReference> altUpperBounds = getUpperBounds(altFunDeclaration, altTypeParameter);
255                if (altUpperBounds.size() != originalUpperBounds.size()) {
256                    if (altUpperBounds.isEmpty()
257                        && originalUpperBounds.equals(Collections.singleton(KotlinBuiltIns.getInstance().getDefaultBound()))) {
258                        // Only default bound => no error
259                    }
260                    else {
261                        throw new AlternativeSignatureMismatchException("Upper bound number mismatch for %s. Expected %d, but found %d",
262                                                                        originalTypeParamDescriptor.getName(),
263                                                                        originalUpperBounds.size(),
264                                                                        altUpperBounds.size());
265                    }
266                }
267    
268                if (altUpperBounds.isEmpty()) {
269                    altParamDescriptor.addDefaultUpperBound();
270                }
271                else {
272                    int upperBoundIndex = 0;
273                    for (JetType upperBound : originalUpperBounds) {
274    
275                        JetTypeElement altTypeElement = altUpperBounds.get(upperBoundIndex).getTypeElement();
276                        assert altTypeElement != null;
277    
278                        altParamDescriptor.addUpperBound(TypeTransformingVisitor.computeType(altTypeElement, upperBound,
279                                                                                             originalToAltTypeParameters, UPPER_BOUND));
280                        upperBoundIndex++;
281                    }
282                }
283    
284                altParamDescriptor.setInitialized();
285                altTypeParameters.add(altParamDescriptor);
286            }
287        }
288    
289        @NotNull
290        private static List<JetTypeReference> getUpperBounds(@NotNull JetFunction function, @NotNull JetTypeParameter jetTypeParameter) {
291            List<JetTypeReference> result = new ArrayList<JetTypeReference>();
292            ContainerUtil.addIfNotNull(result, jetTypeParameter.getExtendsBound());
293    
294            Name name = jetTypeParameter.getNameAsName();
295            if (name == null) return result;
296    
297            for (JetTypeConstraint constraint : function.getTypeConstraints()) {
298                JetSimpleNameExpression parameterName = constraint.getSubjectTypeParameterName();
299                assert parameterName != null : "No parameter name in constraint " + constraint.getText();
300                if (name.equals(parameterName.getReferencedNameAsName())) {
301                    result.add(constraint.getBoundTypeReference());
302                }
303            }
304    
305            return result;
306        }
307    
308        private static void checkEqualFunctionNames(@NotNull PsiNamedElement namedElement, @NotNull JavaMember methodOrConstructor) {
309            if (!ComparatorUtil.equalsNullable(methodOrConstructor.getName().asString(), namedElement.getName())) {
310                throw new AlternativeSignatureMismatchException("Function names mismatch, original: %s, alternative: %s",
311                                                                methodOrConstructor.getName().asString(), namedElement.getName());
312            }
313        }
314    }