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