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.resolver;
018
019 import com.intellij.openapi.diagnostic.Logger;
020 import com.intellij.psi.*;
021 import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
022 import com.intellij.psi.util.MethodSignatureUtil;
023 import com.intellij.psi.util.PsiUtil;
024 import com.intellij.psi.util.TypeConversionUtil;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
027 import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
028 import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
029 import org.jetbrains.jet.lang.resolve.OverridingUtil;
030 import org.jetbrains.jet.lang.resolve.java.kotlinSignature.SignaturesUtil;
031 import org.jetbrains.jet.lang.resolve.java.structure.*;
032 import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaMethodImpl;
033 import org.jetbrains.jet.lang.types.ErrorUtils;
034 import org.jetbrains.jet.lang.types.SubstitutionUtils;
035 import org.jetbrains.jet.lang.types.TypeSubstitution;
036 import org.jetbrains.jet.lang.types.TypeSubstitutor;
037 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
038
039 import javax.inject.Inject;
040 import java.util.ArrayList;
041 import java.util.List;
042
043 import static org.jetbrains.jet.lang.resolve.OverridingUtil.isOverridableBy;
044 import static org.jetbrains.jet.lang.resolve.OverridingUtil.isReturnTypeOkForOverride;
045
046 public class PsiBasedMethodSignatureChecker implements MethodSignatureChecker {
047 private static final Logger LOG = Logger.getInstance(PsiBasedMethodSignatureChecker.class);
048
049 private JavaAnnotationResolver annotationResolver;
050 private ExternalSignatureResolver externalSignatureResolver;
051
052 @Inject
053 public void setAnnotationResolver(JavaAnnotationResolver annotationResolver) {
054 this.annotationResolver = annotationResolver;
055 }
056
057 @Inject
058 public void setExternalSignatureResolver(ExternalSignatureResolver externalSignatureResolver) {
059 this.externalSignatureResolver = externalSignatureResolver;
060 }
061
062 private void checkFunctionOverridesCorrectly(
063 @NotNull JavaMethod method,
064 @NotNull FunctionDescriptor function,
065 @NotNull FunctionDescriptor superFunction
066 ) {
067 ClassDescriptor klass = (ClassDescriptor) function.getContainingDeclaration();
068 List<TypeSubstitution> substitutions = new ArrayList<TypeSubstitution>();
069 while (true) {
070 substitutions.add(SubstitutionUtils.buildDeepSubstitutor(klass.getDefaultType()).getSubstitution());
071 if (!klass.isInner()) {
072 break;
073 }
074 klass = (ClassDescriptor) klass.getContainingDeclaration();
075 }
076 TypeSubstitutor substitutor = TypeSubstitutor.create(substitutions.toArray(new TypeSubstitution[substitutions.size()]));
077 FunctionDescriptor superFunctionSubstituted = superFunction.substitute(substitutor);
078
079 assert superFunctionSubstituted != null : "Couldn't substitute super function: " + superFunction + ", substitutor = " + substitutor;
080
081 OverridingUtil.OverrideCompatibilityInfo.Result overridableResult = isOverridableBy(superFunctionSubstituted, function).getResult();
082 boolean paramsOk = overridableResult == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE;
083 boolean returnTypeOk = isReturnTypeOkForOverride(JetTypeChecker.INSTANCE, superFunctionSubstituted, function);
084 if (!paramsOk || !returnTypeOk) {
085 LOG.error("Loaded Java method overrides another, but resolved as Kotlin function, doesn't.\n"
086 + "super function = " + superFunction + "\n"
087 + "super class = " + superFunction.getContainingDeclaration() + "\n"
088 + "sub function = " + function + "\n"
089 + "sub class = " + function.getContainingDeclaration() + "\n"
090 + "sub method = " + JavaSignatureFormatter.getInstance().getExternalName(method) + "\n"
091 + "@KotlinSignature = " + SignaturesUtil.getKotlinSignature(annotationResolver, method));
092 }
093 }
094
095 private static boolean containsErrorType(@NotNull List<FunctionDescriptor> superFunctions, @NotNull FunctionDescriptor function) {
096 if (ErrorUtils.containsErrorType(function)) {
097 return true;
098 }
099
100 for (FunctionDescriptor superFunction : superFunctions) {
101 if (ErrorUtils.containsErrorType(superFunction)) {
102 return true;
103 }
104 }
105
106 return false;
107 }
108
109 // Originally from com.intellij.codeInsight.daemon.impl.analysis.HighlightMethodUtil
110 private static boolean isMethodReturnTypeCompatible(@NotNull JavaMethodImpl method) {
111 if (method.isStatic()) return true;
112
113 HierarchicalMethodSignature methodSignature = method.getPsi().getHierarchicalMethodSignature();
114 List<HierarchicalMethodSignature> superSignatures = methodSignature.getSuperSignatures();
115
116 PsiType returnType = methodSignature.getSubstitutor().substitute(method.getPsi().getReturnType());
117 if (returnType == null) return true;
118
119 for (MethodSignatureBackedByPsiMethod superMethodSignature : superSignatures) {
120 PsiMethod superMethod = superMethodSignature.getMethod();
121 PsiType declaredReturnType = superMethod.getReturnType();
122 PsiType superReturnType = superMethodSignature.isRaw() ? TypeConversionUtil.erasure(declaredReturnType) : declaredReturnType;
123 if (superReturnType == null || method == superMethod || superMethod.getContainingClass() == null) continue;
124 if (!areMethodsReturnTypesCompatible(superMethodSignature, superReturnType, methodSignature, returnType)) {
125 return false;
126 }
127 }
128
129 return true;
130 }
131
132 // Originally from com.intellij.codeInsight.daemon.impl.analysis.HighlightMethodUtil
133 private static boolean areMethodsReturnTypesCompatible(
134 @NotNull MethodSignatureBackedByPsiMethod superMethodSignature,
135 @NotNull PsiType superReturnType,
136 @NotNull MethodSignatureBackedByPsiMethod methodSignature,
137 @NotNull PsiType returnType
138 ) {
139 PsiType substitutedSuperReturnType;
140 boolean isJdk15 = PsiUtil.isLanguageLevel5OrHigher(methodSignature.getMethod());
141 if (isJdk15 && !superMethodSignature.isRaw() && superMethodSignature.equals(methodSignature)) { //see 8.4.5
142 PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature,
143 superMethodSignature);
144 substitutedSuperReturnType = unifyingSubstitutor == null
145 ? superReturnType
146 : unifyingSubstitutor.substitute(superReturnType);
147 }
148 else {
149 substitutedSuperReturnType = TypeConversionUtil.erasure(superMethodSignature.getSubstitutor().substitute(superReturnType));
150 }
151
152 if (returnType.equals(substitutedSuperReturnType)) return true;
153 if (!(returnType instanceof PsiPrimitiveType) && substitutedSuperReturnType.getDeepComponentType() instanceof PsiClassType) {
154 if (isJdk15 && TypeConversionUtil.isAssignable(substitutedSuperReturnType, returnType)) {
155 return true;
156 }
157 }
158
159 return false;
160 }
161
162 @Override
163 public void checkSignature(
164 @NotNull JavaMethod method,
165 boolean reportSignatureErrors,
166 @NotNull SimpleFunctionDescriptor descriptor,
167 @NotNull List<String> signatureErrors,
168 @NotNull List<FunctionDescriptor> superFunctions
169 ) {
170 JavaMethodImpl methodWithPsi = (JavaMethodImpl) method;
171 if (!RawTypesCheck.hasRawTypesInHierarchicalSignature(methodWithPsi) &&
172 isMethodReturnTypeCompatible(methodWithPsi) &&
173 !containsErrorType(superFunctions, descriptor)) {
174 if (signatureErrors.isEmpty()) {
175 for (FunctionDescriptor superFunction : superFunctions) {
176 checkFunctionOverridesCorrectly(method, descriptor, superFunction);
177 }
178 }
179 else if (reportSignatureErrors) {
180 externalSignatureResolver.reportSignatureErrors(descriptor, signatureErrors);
181 }
182 }
183 }
184
185 private static class RawTypesCheck {
186 private static boolean isPartiallyRawType(@NotNull JavaType type) {
187 if (type instanceof JavaPrimitiveType) {
188 return false;
189 }
190 else if (type instanceof JavaClassifierType) {
191 JavaClassifierType classifierType = (JavaClassifierType) type;
192
193 if (classifierType.isRaw()) {
194 return true;
195 }
196
197 for (JavaType argument : classifierType.getTypeArguments()) {
198 if (isPartiallyRawType(argument)) {
199 return true;
200 }
201 }
202
203 return false;
204 }
205 else if (type instanceof JavaArrayType) {
206 return isPartiallyRawType(((JavaArrayType) type).getComponentType());
207 }
208 else if (type instanceof JavaWildcardType) {
209 JavaType bound = ((JavaWildcardType) type).getBound();
210 return bound != null && isPartiallyRawType(bound);
211 }
212 else {
213 throw new IllegalStateException("Unexpected type: " + type);
214 }
215 }
216
217 private static boolean hasRawTypesInSignature(@NotNull JavaMethod method) {
218 JavaType returnType = method.getReturnType();
219 if (returnType != null && isPartiallyRawType(returnType)) {
220 return true;
221 }
222
223 for (JavaValueParameter parameter : method.getValueParameters()) {
224 if (isPartiallyRawType(parameter.getType())) {
225 return true;
226 }
227 }
228
229 for (JavaTypeParameter typeParameter : method.getTypeParameters()) {
230 for (JavaClassifierType upperBound : typeParameter.getUpperBounds()) {
231 if (isPartiallyRawType(upperBound)) {
232 return true;
233 }
234 }
235 }
236
237 return false;
238 }
239
240 public static boolean hasRawTypesInHierarchicalSignature(@NotNull JavaMethodImpl method) {
241 // This is a very important optimization: package-classes are big and full of static methods
242 // building method hierarchies for such classes takes a very long time
243 if (method.isStatic()) return false;
244
245 if (hasRawTypesInSignature(method)) {
246 return true;
247 }
248
249 for (HierarchicalMethodSignature superSignature : method.getPsi().getHierarchicalMethodSignature().getSuperSignatures()) {
250 JavaMethod superMethod = new JavaMethodImpl(superSignature.getMethod());
251 if (superSignature.isRaw() || typeParameterIsErased(method, superMethod) || hasRawTypesInSignature(superMethod)) {
252 return true;
253 }
254 }
255
256 return false;
257 }
258
259 private static boolean typeParameterIsErased(@NotNull JavaMethod method, @NotNull JavaMethod superMethod) {
260 // Java allows you to write
261 // <T extends Foo> T foo(), in the superclass and then
262 // Foo foo(), in the subclass
263 // this is a valid Java override, but in fact it is an erasure
264 return method.getTypeParameters().size() != superMethod.getTypeParameters().size();
265 }
266
267 private RawTypesCheck() {
268 }
269 }
270 }