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 org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
025    import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
026    import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
027    import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl;
028    import org.jetbrains.jet.lang.psi.*;
029    import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
030    import org.jetbrains.jet.lang.resolve.java.wrapper.PsiMethodWrapper;
031    import org.jetbrains.jet.lang.resolve.name.Name;
032    import org.jetbrains.jet.lang.types.JetType;
033    import org.jetbrains.jet.lang.types.TypeSubstitutor;
034    import org.jetbrains.jet.lang.types.TypeUtils;
035    import org.jetbrains.jet.lang.types.Variance;
036    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
037    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
038    
039    import java.util.ArrayList;
040    import java.util.List;
041    import java.util.Map;
042    
043    import static org.jetbrains.jet.lang.resolve.java.TypeUsage.MEMBER_SIGNATURE_CONTRAVARIANT;
044    import static org.jetbrains.jet.lang.resolve.java.TypeUsage.UPPER_BOUND;
045    
046    public 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    }