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