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.SourceElement;
026    import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
027    import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
028    import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
029    import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl;
030    import org.jetbrains.jet.lang.psi.*;
031    import org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils;
032    import org.jetbrains.jet.lang.resolve.java.resolver.ExternalAnnotationResolver;
033    import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod;
034    import org.jetbrains.jet.lang.resolve.name.Name;
035    import org.jetbrains.jet.lang.types.JetType;
036    import org.jetbrains.jet.lang.types.TypeSubstitutor;
037    import org.jetbrains.jet.lang.types.TypeUtils;
038    import org.jetbrains.jet.lang.types.Variance;
039    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
040    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
041    
042    import java.util.*;
043    
044    import static org.jetbrains.jet.lang.psi.PsiPackage.JetPsiFactory;
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 JavaMethod method,
060                @Nullable JetType receiverType,
061                @NotNull Project project,
062                @NotNull List<ValueParameterDescriptor> valueParameters,
063                @Nullable JetType originalReturnType,
064                @NotNull List<TypeParameterDescriptor> methodTypeParameters,
065                boolean hasSuperMethods
066        ) {
067            String signature = SignaturesUtil.getKotlinSignature(externalAnnotationResolver, method);
068    
069            if (signature == null) {
070                setAnnotated(false);
071                altFunDeclaration = null;
072                return;
073            }
074    
075            if (receiverType != null) {
076                throw new UnsupportedOperationException("Alternative annotations for extension functions are not supported yet");
077            }
078    
079            setAnnotated(true);
080            altFunDeclaration = JetPsiFactory(project).createFunction(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.DEFAULT.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                Name altName = annotationValueParameter.getNameAsName();
207    
208                altParamDescriptors.add(new ValueParameterDescriptorImpl(
209                        originalParameterDescriptor.getContainingDeclaration(),
210                        null,
211                        originalParameterDescriptor.getIndex(),
212                        originalParameterDescriptor.getAnnotations(),
213                        altName != null ? altName : originalParameterDescriptor.getName(),
214                        alternativeType,
215                        originalParameterDescriptor.declaresDefaultValue(),
216                        alternativeVarargElementType,
217                        SourceElement.NO_SOURCE
218                        ));
219            }
220    
221            altValueParameters = altParamDescriptors;
222        }
223    
224        private void computeTypeParameters(List<TypeParameterDescriptor> typeParameters) {
225            if (typeParameters.size() != altFunDeclaration.getTypeParameters().size()) {
226                throw new AlternativeSignatureMismatchException("Method signature has %d type parameters, but alternative signature has %d",
227                                                                typeParameters.size(), altFunDeclaration.getTypeParameters().size());
228            }
229    
230            altTypeParameters = new ArrayList<TypeParameterDescriptor>();
231    
232            for (int i = 0, size = typeParameters.size(); i < size; i++) {
233                TypeParameterDescriptor originalTypeParamDescriptor = typeParameters.get(i);
234    
235                TypeParameterDescriptorImpl altParamDescriptor = originalToAltTypeParameters.get(originalTypeParamDescriptor);
236                JetTypeParameter altTypeParameter = altFunDeclaration.getTypeParameters().get(i);
237    
238                Set<JetType> originalUpperBounds = originalTypeParamDescriptor.getUpperBounds();
239                List<JetTypeReference> altUpperBounds = getUpperBounds(altFunDeclaration, altTypeParameter);
240                if (altUpperBounds.size() != originalUpperBounds.size()) {
241                    if (altUpperBounds.isEmpty()
242                        && originalUpperBounds.equals(Collections.singleton(KotlinBuiltIns.getInstance().getDefaultBound()))) {
243                        // Only default bound => no error
244                    }
245                    else {
246                        throw new AlternativeSignatureMismatchException("Upper bound number mismatch for %s. Expected %d, but found %d",
247                                                                        originalTypeParamDescriptor.getName(),
248                                                                        originalUpperBounds.size(),
249                                                                        altUpperBounds.size());
250                    }
251                }
252    
253                if (altUpperBounds.isEmpty()) {
254                    altParamDescriptor.addDefaultUpperBound();
255                }
256                else {
257                    int upperBoundIndex = 0;
258                    for (JetType upperBound : originalUpperBounds) {
259    
260                        JetTypeElement altTypeElement = altUpperBounds.get(upperBoundIndex).getTypeElement();
261                        assert altTypeElement != null;
262    
263                        altParamDescriptor.addUpperBound(TypeTransformingVisitor.computeType(altTypeElement, upperBound,
264                                                                                             originalToAltTypeParameters, UPPER_BOUND));
265                        upperBoundIndex++;
266                    }
267                }
268    
269                altParamDescriptor.setInitialized();
270                altTypeParameters.add(altParamDescriptor);
271            }
272        }
273    
274        @NotNull
275        private static List<JetTypeReference> getUpperBounds(@NotNull JetFunction function, @NotNull JetTypeParameter jetTypeParameter) {
276            List<JetTypeReference> result = new ArrayList<JetTypeReference>();
277            ContainerUtil.addIfNotNull(result, jetTypeParameter.getExtendsBound());
278    
279            Name name = jetTypeParameter.getNameAsName();
280            if (name == null) return result;
281    
282            for (JetTypeConstraint constraint : function.getTypeConstraints()) {
283                JetSimpleNameExpression parameterName = constraint.getSubjectTypeParameterName();
284                assert parameterName != null : "No parameter name in constraint " + constraint.getText();
285                if (name.equals(parameterName.getReferencedNameAsName())) {
286                    result.add(constraint.getBoundTypeReference());
287                }
288            }
289    
290            return result;
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    }