blob: d725158a5201c3a0e6e58b16a4bd3cbc60d7ae77 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.miscGenerics;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.*;
import com.intellij.util.containers.IntArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class SuspiciousMethodCallUtil {
static void setupPatternMethods(PsiManager manager,
GlobalSearchScope searchScope,
List<PsiMethod> patternMethods,
IntArrayList indices) {
final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(manager.getProject());
final PsiClass
collectionClass = javaPsiFacade.findClass(CommonClassNames.JAVA_UTIL_COLLECTION, searchScope);
PsiType[] javaLangObject = {PsiType.getJavaLangObject(manager, searchScope)};
MethodSignature removeSignature = MethodSignatureUtil
.createMethodSignature("remove", javaLangObject, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
if (collectionClass != null) {
PsiMethod remove = MethodSignatureUtil.findMethodBySignature(collectionClass, removeSignature, false);
addMethod(remove, 0, patternMethods, indices);
MethodSignature containsSignature = MethodSignatureUtil.createMethodSignature("contains", javaLangObject, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
PsiMethod contains = MethodSignatureUtil.findMethodBySignature(collectionClass, containsSignature, false);
addMethod(contains, 0, patternMethods, indices);
if (PsiUtil.isLanguageLevel5OrHigher(collectionClass)) {
PsiClassType wildcardCollection = javaPsiFacade.getElementFactory().createType(collectionClass, PsiWildcardType.createUnbounded(manager));
MethodSignature removeAllSignature = MethodSignatureUtil.createMethodSignature("removeAll", new PsiType[] {wildcardCollection}, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
PsiMethod removeAll = MethodSignatureUtil.findMethodBySignature(collectionClass, removeAllSignature, false);
addMethod(removeAll, 0, patternMethods, indices);
}
}
final PsiClass listClass = javaPsiFacade.findClass(CommonClassNames.JAVA_UTIL_LIST, searchScope);
if (listClass != null) {
MethodSignature indexofSignature = MethodSignatureUtil.createMethodSignature("indexOf", javaLangObject, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
PsiMethod indexof = MethodSignatureUtil.findMethodBySignature(listClass, indexofSignature, false);
addMethod(indexof, 0, patternMethods, indices);
MethodSignature lastindexofSignature = MethodSignatureUtil.createMethodSignature("lastIndexOf", javaLangObject, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
PsiMethod lastindexof = MethodSignatureUtil.findMethodBySignature(listClass, lastindexofSignature, false);
addMethod(lastindexof, 0, patternMethods, indices);
}
final PsiClass mapClass = javaPsiFacade.findClass(CommonClassNames.JAVA_UTIL_MAP, searchScope);
if (mapClass != null) {
PsiMethod remove = MethodSignatureUtil.findMethodBySignature(mapClass, removeSignature, false);
addMethod(remove, 0, patternMethods, indices);
MethodSignature getSignature = MethodSignatureUtil.createMethodSignature("get", javaLangObject, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
PsiMethod get = MethodSignatureUtil.findMethodBySignature(mapClass, getSignature, false);
addMethod(get, 0, patternMethods, indices);
MethodSignature containsKeySignature = MethodSignatureUtil.createMethodSignature("containsKey", javaLangObject, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
PsiMethod containsKey = MethodSignatureUtil.findMethodBySignature(mapClass, containsKeySignature, false);
addMethod(containsKey, 0, patternMethods, indices);
MethodSignature containsValueSignature = MethodSignatureUtil.createMethodSignature("containsValue", javaLangObject, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
PsiMethod containsValue = MethodSignatureUtil.findMethodBySignature(mapClass, containsValueSignature, false);
addMethod(containsValue, 1, patternMethods, indices);
}
final PsiClass concurrentMapClass = javaPsiFacade.findClass(CommonClassNames.JAVA_UTIL_CONCURRENT_HASH_MAP, searchScope);
if (concurrentMapClass != null) {
MethodSignature containsSignature = MethodSignatureUtil.createMethodSignature("contains", javaLangObject, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
PsiMethod contains = MethodSignatureUtil.findMethodBySignature(concurrentMapClass, containsSignature, false);
addMethod(contains, 1, patternMethods, indices);
}
}
private static void addMethod(final PsiMethod patternMethod, int typeParamIndex, List<PsiMethod> patternMethods, IntArrayList indices) {
if (patternMethod != null) {
patternMethods.add(patternMethod);
indices.add(typeParamIndex);
}
}
static boolean isInheritorOrSelf(PsiMethod inheritorCandidate, PsiMethod base) {
PsiClass aClass = inheritorCandidate.getContainingClass();
PsiClass bClass = base.getContainingClass();
if (aClass == null || bClass == null) return false;
PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(bClass, aClass, PsiSubstitutor.EMPTY);
return substitutor != null &&
MethodSignatureUtil.findMethodBySignature(bClass, inheritorCandidate.getSignature(substitutor), false) == base;
}
@Nullable
public static String getSuspiciousMethodCallMessage(@NotNull PsiMethodCallExpression methodCall,
PsiType argType,
boolean reportConvertibleMethodCalls,
@NotNull List<PsiMethod> patternMethods,
@NotNull IntArrayList indices) {
final PsiReferenceExpression methodExpression = methodCall.getMethodExpression();
final PsiExpression qualifier = methodExpression.getQualifierExpression();
if (qualifier == null || qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) return null;
if (argType instanceof PsiPrimitiveType) {
argType = ((PsiPrimitiveType)argType).getBoxedType(methodCall);
}
if (!(argType instanceof PsiClassType)) return null;
final JavaResolveResult resolveResult = methodExpression.advancedResolve(false);
PsiMethod calleeMethod = (PsiMethod)resolveResult.getElement();
if (calleeMethod == null) return null;
PsiMethod contextMethod = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class);
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (patternMethods) {
if (patternMethods.isEmpty()) {
setupPatternMethods(methodCall.getManager(), methodCall.getResolveScope(), patternMethods, indices);
}
}
for (int i = 0; i < patternMethods.size(); i++) {
PsiMethod patternMethod = patternMethods.get(i);
if (!patternMethod.getName().equals(methodExpression.getReferenceName())) continue;
int index = indices.get(i);
//we are in collections method implementation
if (contextMethod != null && isInheritorOrSelf(contextMethod, patternMethod)) return null;
final PsiClass calleeClass = calleeMethod.getContainingClass();
PsiSubstitutor substitutor = resolveResult.getSubstitutor();
final PsiClass patternClass = patternMethod.getContainingClass();
assert patternClass != null;
assert calleeClass != null;
substitutor = TypeConversionUtil.getClassSubstitutor(patternClass, calleeClass, substitutor);
if (substitutor == null) continue;
if (!patternMethod.getSignature(substitutor).equals(calleeMethod.getSignature(PsiSubstitutor.EMPTY))) continue;
PsiTypeParameter[] typeParameters = patternClass.getTypeParameters();
if (typeParameters.length <= index) return null;
final PsiTypeParameter typeParameter = typeParameters[index];
PsiType typeParamMapping = substitutor.substitute(typeParameter);
if (typeParamMapping == null) return null;
PsiParameter[] parameters = patternMethod.getParameterList().getParameters();
if (parameters.length == 1 && "removeAll".equals(patternMethod.getName())) {
PsiType paramType = parameters[0].getType();
if (InheritanceUtil.isInheritor(paramType, CommonClassNames.JAVA_UTIL_COLLECTION)) {
PsiType qualifierType = qualifier.getType();
if (qualifierType != null) {
final PsiType itemType = JavaGenericsUtil.getCollectionItemType(argType, calleeMethod.getResolveScope());
final PsiType qualifierItemType = JavaGenericsUtil.getCollectionItemType(qualifierType, calleeMethod.getResolveScope());
if (qualifierItemType != null && itemType != null && !qualifierItemType.isAssignableFrom(itemType)) {
return InspectionsBundle.message("inspection.suspicious.collections.method.calls.problem.descriptor",
PsiFormatUtil.formatType(qualifierType, 0, PsiSubstitutor.EMPTY),
PsiFormatUtil.formatType(itemType, 0, PsiSubstitutor.EMPTY));
}
}
return null;
}
}
String message = null;
if (typeParamMapping instanceof PsiCapturedWildcardType) {
typeParamMapping = ((PsiCapturedWildcardType)typeParamMapping).getWildcard();
}
if (!typeParamMapping.isAssignableFrom(argType)) {
if (typeParamMapping.isConvertibleFrom(argType)) {
if (reportConvertibleMethodCalls) {
message = InspectionsBundle.message("inspection.suspicious.collections.method.calls.problem.descriptor1",
PsiFormatUtil.formatMethod(calleeMethod, substitutor,
PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_CONTAINING_CLASS,
PsiFormatUtilBase.SHOW_TYPE));
}
}
else {
PsiType qualifierType = qualifier.getType();
if (qualifierType != null) {
message = InspectionsBundle.message("inspection.suspicious.collections.method.calls.problem.descriptor",
PsiFormatUtil.formatType(qualifierType, 0, PsiSubstitutor.EMPTY),
PsiFormatUtil.formatType(argType, 0, PsiSubstitutor.EMPTY));
}
}
}
return message;
}
return null;
}
}