blob: 674311ec5fde25c1cd0116b74a1685cb2fc7d039 [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.
*/
/*
* User: anna
* Date: 21-Dec-2007
*/
package com.intellij.codeInspection.reference;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.VisibilityUtil;
import org.jetbrains.annotations.Nullable;
public class RefJavaUtilImpl extends RefJavaUtil{
@Override
public void addReferences(final PsiModifierListOwner psiFrom, final RefJavaElement ref, @Nullable PsiElement findIn) {
final RefJavaElementImpl refFrom = (RefJavaElementImpl)ref;
if (findIn != null) {
findIn.accept(
new JavaRecursiveElementWalkingVisitor() {
@Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
final PsiElement target = reference.resolve();
if (target instanceof PsiClass) {
final PsiClass aClass = (PsiClass)target;
final RefClassImpl refClass = (RefClassImpl)refFrom.getRefManager().getReference(aClass);
refFrom.addReference(refClass, aClass, psiFrom, false, true, null);
}
if (target instanceof PsiModifierListOwner && isDeprecated(target)) {
refFrom.setUsesDeprecatedApi(true);
}
}
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
visitElement(expression);
PsiElement psiResolved = expression.resolve();
if (psiResolved instanceof PsiModifierListOwner) {
if (isDeprecated(psiResolved)) refFrom.setUsesDeprecatedApi(true);
}
RefElement refResolved = refFrom.getRefManager().getReference(psiResolved);
refFrom.addReference(
refResolved, psiResolved, psiFrom, PsiUtil.isAccessedForWriting(expression),
PsiUtil.isAccessedForReading(expression), expression
);
if (refResolved instanceof RefMethod) {
updateRefMethod(psiResolved, refResolved, expression, psiFrom, refFrom);
}
}
@Override public void visitEnumConstant(PsiEnumConstant enumConstant) {
super.visitEnumConstant(enumConstant);
processNewLikeConstruct(enumConstant.resolveConstructor(), enumConstant.getArgumentList());
}
@Override public void visitNewExpression(PsiNewExpression newExpr) {
super.visitNewExpression(newExpr);
PsiMethod psiConstructor = newExpr.resolveConstructor();
final PsiExpressionList argumentList = newExpr.getArgumentList();
RefMethod refConstructor = processNewLikeConstruct(psiConstructor, argumentList);
if (refConstructor == null) { // No explicit constructor referenced. Should use default one.
PsiType newType = newExpr.getType();
if (newType instanceof PsiClassType) {
processClassReference(PsiUtil.resolveClassInType(newType), refFrom, psiFrom, true);
}
}
}
@Nullable
private RefMethod processNewLikeConstruct(final PsiMethod psiConstructor, final PsiExpressionList argumentList) {
if (psiConstructor != null) {
if (isDeprecated(psiConstructor)) refFrom.setUsesDeprecatedApi(true);
}
RefMethodImpl refConstructor = (RefMethodImpl)refFrom.getRefManager().getReference(
psiConstructor
);
refFrom.addReference(refConstructor, psiConstructor, psiFrom, false, true, null);
if (argumentList != null) {
PsiExpression[] psiParams = argumentList.getExpressions();
for (PsiExpression param : psiParams) {
param.accept(this);
}
if (refConstructor != null) {
refConstructor.updateParameterValues(psiParams);
}
}
return refConstructor;
}
@Override public void visitAnonymousClass(PsiAnonymousClass psiClass) {
super.visitAnonymousClass(psiClass);
RefClassImpl refClass = (RefClassImpl)refFrom.getRefManager().getReference(psiClass);
refFrom.addReference(refClass, psiClass, psiFrom, false, true, null);
}
@Override public void visitReturnStatement(PsiReturnStatement statement) {
super.visitReturnStatement(statement);
if (refFrom instanceof RefMethodImpl) {
RefMethodImpl refMethod = (RefMethodImpl)refFrom;
refMethod.updateReturnValueTemplate(statement.getReturnValue());
}
}
@Override public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) {
super.visitClassObjectAccessExpression(expression);
final PsiTypeElement operand = expression.getOperand();
final PsiType type = operand.getType();
if (type instanceof PsiClassType) {
processClassReference(((PsiClassType)type).resolve(), refFrom, psiFrom, false);
}
}
private void processClassReference(final PsiClass psiClass,
final RefJavaElementImpl refFrom,
final PsiModifierListOwner psiFrom,
boolean defaultConstructorOnly) {
if (psiClass != null) {
RefClassImpl refClass = (RefClassImpl)refFrom.getRefManager().getReference(psiClass);
if (refClass != null) {
boolean hasConstructorsMarked = false;
if (defaultConstructorOnly) {
RefMethodImpl refDefaultConstructor = (RefMethodImpl)refClass.getDefaultConstructor();
if (refDefaultConstructor != null && !(refDefaultConstructor instanceof RefImplicitConstructor)) {
refDefaultConstructor.addInReference(refFrom);
refFrom.addOutReference(refDefaultConstructor);
hasConstructorsMarked = true;
}
}
else {
for (RefMethod cons : refClass.getConstructors()) {
if (cons instanceof RefImplicitConstructor) continue;
((RefMethodImpl)cons).addInReference(refFrom);
refFrom.addOutReference(cons);
hasConstructorsMarked = true;
}
}
if (!hasConstructorsMarked) {
refFrom.addReference(refClass, psiClass, psiFrom, false, true, null);
}
}
}
}
}
);
}
}
private void updateRefMethod(PsiElement psiResolved,
RefElement refResolved,
PsiElement refExpression,
final PsiElement psiFrom,
final RefElement refFrom) {
PsiMethod psiMethod = (PsiMethod)psiResolved;
RefMethodImpl refMethod = (RefMethodImpl)refResolved;
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(
refExpression,
PsiMethodCallExpression.class
);
if (call != null) {
PsiType returnType = psiMethod.getReturnType();
if (!psiMethod.isConstructor() && returnType != PsiType.VOID) {
if (!(call.getParent() instanceof PsiExpressionStatement)) {
refMethod.setReturnValueUsed(true);
}
addTypeReference(psiFrom, returnType, refFrom.getRefManager());
}
PsiExpressionList argumentList = call.getArgumentList();
if (argumentList.getExpressions().length > 0) {
refMethod.updateParameterValues(argumentList.getExpressions());
}
final PsiExpression psiExpression = call.getMethodExpression().getQualifierExpression();
if (psiExpression != null) {
final PsiType usedType = psiExpression.getType();
if (usedType != null) {
final String fqName = psiMethod.getContainingClass().getQualifiedName();
if (fqName != null) {
final PsiClassType methodOwnerType = JavaPsiFacade.getInstance(call.getProject()).getElementFactory()
.createTypeByFQClassName(fqName, GlobalSearchScope.allScope(psiMethod.getProject()));
if (!usedType.equals(methodOwnerType)) {
refMethod.setCalledOnSubClass(true);
}
}
}
}
}
}
@Override
public RefClass getTopLevelClass(RefElement refElement) {
RefEntity refParent = refElement.getOwner();
while (refParent != null && refParent instanceof RefElement && !(refParent instanceof RefFile)) {
refElement = (RefElementImpl)refParent;
refParent = refParent.getOwner();
}
return refElement instanceof RefClass ? (RefClass)refElement : null;
}
@Override
public boolean isInheritor(RefClass subClass, RefClass superClass) {
if (subClass == superClass) return true;
for (RefClass baseClass : subClass.getBaseClasses()) {
if (isInheritor(baseClass, superClass)) return true;
}
return false;
}
@Override
@Nullable
public String getPackageName(RefEntity refEntity) {
if (refEntity instanceof RefProject) {
return null;
}
RefPackage refPackage = getPackage(refEntity);
return refPackage == null ? InspectionsBundle.message("inspection.reference.default.package") : refPackage.getQualifiedName();
}
@Override
public String getAccessModifier(PsiModifierListOwner psiElement) {
if (psiElement instanceof PsiParameter) return PsiModifier.PACKAGE_LOCAL;
PsiModifierList list = psiElement.getModifierList();
String result = PsiModifier.PACKAGE_LOCAL;
if (list != null) {
if (list.hasModifierProperty(PsiModifier.PRIVATE)) {
result = PsiModifier.PRIVATE;
}
else if (list.hasModifierProperty(PsiModifier.PROTECTED)) {
result = PsiModifier.PROTECTED;
}
else if (list.hasModifierProperty(PsiModifier.PUBLIC)) {
result = PsiModifier.PUBLIC;
}
else if (psiElement.getParent() instanceof PsiClass) {
PsiClass ownerClass = (PsiClass)psiElement.getParent();
if (ownerClass.isInterface()) {
result = PsiModifier.PUBLIC;
}
if (ownerClass.isEnum() && result.equals(PsiModifier.PACKAGE_LOCAL)) {
result = PsiModifier.PRIVATE;
}
}
}
return result;
}
@Override
@Nullable public RefClass getOwnerClass(RefManager refManager, PsiElement psiElement) {
while (psiElement != null && !(psiElement instanceof PsiClass)) {
psiElement = psiElement.getParent();
}
return psiElement != null ? (RefClass)refManager.getReference(psiElement) : null;
}
@Override
@Nullable public RefClass getOwnerClass(RefElement refElement) {
RefEntity parent = refElement.getOwner();
while (!(parent instanceof RefClass) && parent instanceof RefElement) {
parent = parent.getOwner();
}
if (parent instanceof RefClass) return (RefClass)parent;
return null;
}
@Override
public boolean isMethodOnlyCallsSuper(PsiMethod method) {
boolean hasStatements = false;
PsiCodeBlock body = method.getBody();
if (body != null) {
PsiStatement[] statements = body.getStatements();
for (PsiStatement statement : statements) {
boolean isCallToSameSuper = false;
if (statement instanceof PsiExpressionStatement) {
isCallToSameSuper = isCallToSuperMethod(((PsiExpressionStatement)statement).getExpression(), method);
}
else if (statement instanceof PsiReturnStatement) {
PsiExpression expression = ((PsiReturnStatement)statement).getReturnValue();
isCallToSameSuper = expression == null || isCallToSuperMethod(expression, method);
}
hasStatements = true;
if (isCallToSameSuper) continue;
return false;
}
}
if (hasStatements) {
final PsiMethod[] superMethods = method.findSuperMethods();
for (PsiMethod superMethod : superMethods) {
if (VisibilityUtil.compare(VisibilityUtil.getVisibilityModifier(superMethod.getModifierList()),
VisibilityUtil.getVisibilityModifier(method.getModifierList())) > 0) return false;
}
}
return hasStatements;
}
@Override
public boolean isCallToSuperMethod(PsiExpression expression, PsiMethod method) {
if (expression instanceof PsiMethodCallExpression) {
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
if (methodCall.getMethodExpression().getQualifierExpression() instanceof PsiSuperExpression) {
PsiMethod superMethod = (PsiMethod)methodCall.getMethodExpression().resolve();
if (superMethod == null || !MethodSignatureUtil.areSignaturesEqual(method, superMethod)) return false;
PsiExpression[] args = methodCall.getArgumentList().getExpressions();
PsiParameter[] parms = method.getParameterList().getParameters();
for (int i = 0; i < args.length; i++) {
PsiExpression arg = args[i];
if (!(arg instanceof PsiReferenceExpression)) return false;
if (!parms[i].equals(((PsiReferenceExpression)arg).resolve())) return false;
}
return true;
}
}
return false;
}
@Override
public int compareAccess(String a1, String a2) {
int i1 = getAccessNumber(a1);
int i2 = getAccessNumber(a2);
if (i1 == i2) return 0;
if (i1 < i2) return -1;
return 1;
}
@SuppressWarnings("StringEquality")
private static int getAccessNumber(String a) {
if (a == PsiModifier.PRIVATE) {
return 0;
}
else if (a == PsiModifier.PACKAGE_LOCAL) {
return 1;
}
else if (a == PsiModifier.PROTECTED) {
return 2;
}
else if (a == PsiModifier.PUBLIC) return 3;
return -1;
}
@Override
public void setAccessModifier(RefJavaElement refElement, String newAccess) {
((RefJavaElementImpl)refElement).setAccessModifier(newAccess);
}
@Override
public void setIsStatic(RefJavaElement refElement, boolean isStatic) {
((RefJavaElementImpl)refElement).setIsStatic(isStatic);
}
@Override
public void setIsFinal(RefJavaElement refElement, boolean isFinal) {
((RefJavaElementImpl)refElement).setIsFinal(isFinal);
}
@Override
public void addTypeReference(PsiElement psiElement, PsiType psiType, RefManager refManager) {
RefClass ownerClass = getOwnerClass(refManager, psiElement);
if (ownerClass != null) {
psiType = psiType.getDeepComponentType();
if (psiType instanceof PsiClassType) {
PsiClass psiClass = PsiUtil.resolveClassInType(psiType);
if (psiClass != null && refManager.belongsToScope(psiClass)) {
RefClassImpl refClass = (RefClassImpl)refManager.getReference(psiClass);
if (refClass != null) {
refClass.addTypeReference(ownerClass);
}
}
}
}
}
}