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.DEFAULT.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 }