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