| /* |
| * Copyright 2000-2014 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.siyeh.ig.migration; |
| |
| import com.intellij.psi.*; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.psi.util.InheritanceUtil; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.util.PsiUtil; |
| import com.siyeh.HardcodedMethodConstants; |
| import com.siyeh.InspectionGadgetsBundle; |
| import com.siyeh.ig.BaseInspection; |
| import com.siyeh.ig.BaseInspectionVisitor; |
| import com.siyeh.ig.psiutils.*; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| public class ForCanBeForeachInspectionBase extends BaseInspection { |
| @SuppressWarnings("PublicField") |
| public boolean REPORT_INDEXED_LOOP = true; |
| @SuppressWarnings("PublicField") |
| public boolean ignoreUntypedCollections = false; |
| |
| protected static boolean isIndexedListLoopStatement(PsiForStatement forStatement, boolean ignoreUntypedCollections) { |
| final PsiStatement initialization = forStatement.getInitialization(); |
| if (!(initialization instanceof PsiDeclarationStatement)) { |
| return false; |
| } |
| final PsiDeclarationStatement declaration = (PsiDeclarationStatement)initialization; |
| final PsiElement[] declaredElements = declaration.getDeclaredElements(); |
| final PsiElement secondDeclaredElement; |
| if (declaredElements.length == 1) { |
| secondDeclaredElement = null; |
| } |
| else if (declaredElements.length == 2) { |
| secondDeclaredElement = declaredElements[1]; |
| } |
| else { |
| return false; |
| } |
| final PsiElement declaredElement = declaredElements[0]; |
| if (!(declaredElement instanceof PsiVariable)) { |
| return false; |
| } |
| final PsiVariable indexVariable = (PsiVariable)declaredElement; |
| final PsiExpression initialValue = indexVariable.getInitializer(); |
| if (initialValue == null) { |
| return false; |
| } |
| final Object constant = ExpressionUtils.computeConstantExpression(initialValue); |
| if (!(constant instanceof Number)) { |
| return false; |
| } |
| final Number number = (Number)constant; |
| if (number.intValue() != 0) { |
| return false; |
| } |
| final PsiExpression condition = forStatement.getCondition(); |
| final Holder collectionHolder = getCollectionFromSizeComparison(condition, indexVariable, secondDeclaredElement); |
| if (collectionHolder == null) { |
| return false; |
| } |
| final PsiStatement update = forStatement.getUpdate(); |
| if (!VariableAccessUtils.variableIsIncremented(indexVariable, update)) { |
| return false; |
| } |
| final PsiStatement body = forStatement.getBody(); |
| if (!isIndexVariableOnlyUsedAsListIndex(collectionHolder, indexVariable, body)) { |
| return false; |
| } |
| if (collectionHolder != Holder.DUMMY) { |
| final PsiVariable collection = collectionHolder.getVariable(); |
| final PsiClassType collectionType = (PsiClassType)collection.getType(); |
| final PsiType[] parameters = collectionType.getParameters(); |
| if (ignoreUntypedCollections && parameters.length == 0) { |
| return false; |
| } |
| return !VariableAccessUtils.variableIsAssigned(collection, body); |
| } |
| return true; |
| } |
| |
| static boolean isArrayLoopStatement(PsiForStatement forStatement) { |
| final PsiStatement initialization = forStatement.getInitialization(); |
| if (!(initialization instanceof PsiDeclarationStatement)) { |
| return false; |
| } |
| final PsiDeclarationStatement declaration = (PsiDeclarationStatement)initialization; |
| final PsiElement[] declaredElements = declaration.getDeclaredElements(); |
| final PsiElement secondDeclaredElement; |
| if (declaredElements.length == 1) { |
| secondDeclaredElement = null; |
| } |
| else if (declaredElements.length == 2) { |
| secondDeclaredElement = declaredElements[1]; |
| } |
| else { |
| return false; |
| } |
| final PsiElement declaredElement = declaredElements[0]; |
| if (!(declaredElement instanceof PsiVariable)) { |
| return false; |
| } |
| final PsiVariable indexVariable = (PsiVariable)declaredElement; |
| final PsiExpression initialValue = indexVariable.getInitializer(); |
| if (initialValue == null) { |
| return false; |
| } |
| final Object constant = ExpressionUtils.computeConstantExpression(initialValue); |
| if (!(constant instanceof Integer)) { |
| return false; |
| } |
| final Integer integer = (Integer)constant; |
| if (integer.intValue() != 0) { |
| return false; |
| } |
| final PsiStatement update = forStatement.getUpdate(); |
| if (!VariableAccessUtils.variableIsIncremented(indexVariable, update)) { |
| return false; |
| } |
| final PsiExpression condition = forStatement.getCondition(); |
| final PsiReferenceExpression arrayReference = getVariableReferenceFromCondition(condition, indexVariable, secondDeclaredElement); |
| if (arrayReference == null) { |
| return false; |
| } |
| if (!(arrayReference.getType() instanceof PsiArrayType)) { |
| return false; |
| } |
| final PsiElement element = arrayReference.resolve(); |
| if (!(element instanceof PsiVariable)) { |
| return false; |
| } |
| final PsiVariable arrayVariable = (PsiVariable)element; |
| final PsiStatement body = forStatement.getBody(); |
| return body == null || |
| isIndexVariableOnlyUsedAsIndex(arrayVariable, indexVariable, body) && |
| !VariableAccessUtils.variableIsAssigned(arrayVariable, body) && |
| !VariableAccessUtils.arrayContentsAreAssigned(arrayVariable, body); |
| } |
| |
| private static boolean isIndexVariableOnlyUsedAsIndex( |
| @NotNull PsiVariable arrayVariable, |
| @NotNull PsiVariable indexVariable, |
| @Nullable PsiStatement body) { |
| if (body == null) { |
| return true; |
| } |
| final VariableOnlyUsedAsIndexVisitor visitor = |
| new VariableOnlyUsedAsIndexVisitor(arrayVariable, indexVariable); |
| body.accept(visitor); |
| return visitor.isIndexVariableUsedOnlyAsIndex(); |
| } |
| |
| private static boolean isIndexVariableOnlyUsedAsListIndex( |
| Holder collectionHolder, PsiVariable indexVariable, |
| PsiStatement body) { |
| if (body == null) { |
| return true; |
| } |
| final VariableOnlyUsedAsListIndexVisitor visitor = |
| new VariableOnlyUsedAsListIndexVisitor(collectionHolder, |
| indexVariable); |
| body.accept(visitor); |
| return visitor.isIndexVariableUsedOnlyAsIndex(); |
| } |
| |
| static boolean isCollectionLoopStatement( |
| PsiForStatement forStatement, boolean ignoreUntypedCollections) { |
| final PsiStatement initialization = forStatement.getInitialization(); |
| if (!(initialization instanceof PsiDeclarationStatement)) { |
| return false; |
| } |
| final PsiDeclarationStatement declaration = |
| (PsiDeclarationStatement)initialization; |
| final PsiElement[] declaredElements = declaration.getDeclaredElements(); |
| if (declaredElements.length != 1) { |
| return false; |
| } |
| final PsiElement declaredElement = declaredElements[0]; |
| if (!(declaredElement instanceof PsiVariable)) { |
| return false; |
| } |
| final PsiVariable variable = (PsiVariable)declaredElement; |
| if (!TypeUtils.variableHasTypeOrSubtype(variable, |
| CommonClassNames.JAVA_UTIL_ITERATOR, |
| "java.util.ListIterator")) { |
| return false; |
| } |
| final PsiExpression initialValue = variable.getInitializer(); |
| if (initialValue == null) { |
| return false; |
| } |
| if (!(initialValue instanceof PsiMethodCallExpression)) { |
| return false; |
| } |
| final PsiMethodCallExpression initialCall = |
| (PsiMethodCallExpression)initialValue; |
| final PsiReferenceExpression initialMethodExpression = |
| initialCall.getMethodExpression(); |
| @NonNls final String initialCallName = |
| initialMethodExpression.getReferenceName(); |
| if (!HardcodedMethodConstants.ITERATOR.equals(initialCallName) && |
| !"listIterator".equals(initialCallName)) { |
| return false; |
| } |
| final PsiExpressionList argumentList = initialCall.getArgumentList(); |
| final PsiExpression[] arguments = argumentList.getExpressions(); |
| if (arguments.length != 0) { |
| return false; |
| } |
| final PsiExpression qualifier = |
| initialMethodExpression.getQualifierExpression(); |
| final PsiClass qualifierClass; |
| if (qualifier == null) { |
| qualifierClass = |
| ClassUtils.getContainingClass(initialMethodExpression); |
| if (ignoreUntypedCollections) { |
| final PsiClassType type = (PsiClassType)variable.getType(); |
| final PsiType[] parameters = type.getParameters(); |
| if (parameters.length == 0) { |
| return false; |
| } |
| } |
| } |
| else { |
| final PsiType qualifierType = qualifier.getType(); |
| if (!(qualifierType instanceof PsiClassType)) { |
| return false; |
| } |
| final PsiClassType classType = (PsiClassType)qualifierType; |
| qualifierClass = classType.resolve(); |
| if (ignoreUntypedCollections) { |
| final PsiClassType type = (PsiClassType)variable.getType(); |
| final PsiType[] parameters = type.getParameters(); |
| final PsiType[] parameters1 = classType.getParameters(); |
| if (parameters.length == 0 && parameters1.length == 0) { |
| return false; |
| } |
| } |
| } |
| if (qualifierClass == null) { |
| return false; |
| } |
| if (!InheritanceUtil.isInheritor(qualifierClass, |
| CommonClassNames.JAVA_LANG_ITERABLE) && |
| !InheritanceUtil.isInheritor(qualifierClass, |
| CommonClassNames.JAVA_UTIL_COLLECTION)) { |
| return false; |
| } |
| final PsiExpression condition = forStatement.getCondition(); |
| if (!isHasNext(condition, variable)) { |
| return false; |
| } |
| final PsiStatement update = forStatement.getUpdate(); |
| if (update != null && !(update instanceof PsiEmptyStatement)) { |
| return false; |
| } |
| final PsiStatement body = forStatement.getBody(); |
| if (body == null) { |
| return false; |
| } |
| if (calculateCallsToIteratorNext(variable, body) != 1) { |
| return false; |
| } |
| if (isIteratorMethodCalled(variable, body)) { |
| return false; |
| } |
| return !VariableAccessUtils.variableIsReturned(variable, body) && |
| !VariableAccessUtils.variableIsAssigned(variable, body) && |
| !VariableAccessUtils.variableIsPassedAsMethodArgument(variable, body); |
| } |
| |
| private static int calculateCallsToIteratorNext(PsiVariable iterator, |
| PsiStatement body) { |
| if (body == null) { |
| return 0; |
| } |
| final NumCallsToIteratorNextVisitor visitor = |
| new NumCallsToIteratorNextVisitor(iterator); |
| body.accept(visitor); |
| return visitor.getNumCallsToIteratorNext(); |
| } |
| |
| private static boolean isIteratorMethodCalled(PsiVariable iterator, |
| PsiStatement body) { |
| final IteratorMethodCallVisitor visitor = |
| new IteratorMethodCallVisitor(iterator); |
| body.accept(visitor); |
| return visitor.isMethodCalled(); |
| } |
| |
| private static boolean isHasNext(PsiExpression condition, |
| PsiVariable iterator) { |
| if (!(condition instanceof PsiMethodCallExpression)) { |
| return false; |
| } |
| final PsiMethodCallExpression call = |
| (PsiMethodCallExpression)condition; |
| final PsiExpressionList argumentList = call.getArgumentList(); |
| final PsiExpression[] arguments = argumentList.getExpressions(); |
| if (arguments.length != 0) { |
| return false; |
| } |
| final PsiReferenceExpression methodExpression = |
| call.getMethodExpression(); |
| final String methodName = methodExpression.getReferenceName(); |
| if (!HardcodedMethodConstants.HAS_NEXT.equals(methodName)) { |
| return false; |
| } |
| final PsiExpression qualifier = |
| methodExpression.getQualifierExpression(); |
| if (qualifier == null) { |
| return true; |
| } |
| if (!(qualifier instanceof PsiReferenceExpression)) { |
| return false; |
| } |
| final PsiReferenceExpression referenceExpression = |
| (PsiReferenceExpression)qualifier; |
| final PsiElement target = referenceExpression.resolve(); |
| return iterator.equals(target); |
| } |
| |
| @Nullable |
| private static PsiReferenceExpression getVariableReferenceFromCondition(PsiExpression condition, |
| PsiVariable variable, |
| PsiElement secondDeclaredElement) { |
| condition = ParenthesesUtils.stripParentheses(condition); |
| if (!(condition instanceof PsiBinaryExpression)) { |
| return null; |
| } |
| final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)condition; |
| final IElementType tokenType = binaryExpression.getOperationTokenType(); |
| final PsiExpression lhs = ParenthesesUtils.stripParentheses(binaryExpression.getLOperand()); |
| final PsiExpression rhs = ParenthesesUtils.stripParentheses(binaryExpression.getROperand()); |
| if (rhs == null) { |
| return null; |
| } |
| PsiReferenceExpression referenceExpression; |
| if (tokenType.equals(JavaTokenType.LT)) { |
| if (!VariableAccessUtils.evaluatesToVariable(lhs, variable) || !(rhs instanceof PsiReferenceExpression)) { |
| return null; |
| } |
| referenceExpression = (PsiReferenceExpression)rhs; |
| } |
| else if (tokenType.equals(JavaTokenType.GT)) { |
| if (!VariableAccessUtils.evaluatesToVariable(rhs, variable) || !(lhs instanceof PsiReferenceExpression)) { |
| return null; |
| } |
| referenceExpression = (PsiReferenceExpression)lhs; |
| } |
| else { |
| return null; |
| } |
| if (!expressionIsArrayLengthLookup(referenceExpression)) { |
| final PsiElement target = referenceExpression.resolve(); |
| if (secondDeclaredElement != null && !secondDeclaredElement.equals(target)) { |
| return null; |
| } |
| if (target instanceof PsiVariable) { |
| final PsiVariable maxVariable = (PsiVariable)target; |
| final PsiCodeBlock context = PsiTreeUtil.getParentOfType(maxVariable, PsiCodeBlock.class); |
| if (context == null) { |
| return null; |
| } |
| if (VariableAccessUtils.variableIsAssigned(maxVariable, context)) { |
| return null; |
| } |
| final PsiExpression expression = ParenthesesUtils.stripParentheses(maxVariable.getInitializer()); |
| if (!(expression instanceof PsiReferenceExpression)) { |
| return null; |
| } |
| referenceExpression = (PsiReferenceExpression)expression; |
| if (!expressionIsArrayLengthLookup(referenceExpression)) { |
| return null; |
| } |
| } |
| } |
| else { |
| if (secondDeclaredElement != null) { |
| return null; |
| } |
| } |
| final PsiExpression qualifierExpression = referenceExpression.getQualifierExpression(); |
| if (qualifierExpression instanceof PsiReferenceExpression) { |
| return (PsiReferenceExpression)qualifierExpression; |
| } |
| else if (qualifierExpression instanceof PsiThisExpression || |
| qualifierExpression instanceof PsiSuperExpression || |
| qualifierExpression == null) { |
| return referenceExpression; |
| } |
| else { |
| return null; |
| } |
| } |
| |
| @Nullable |
| private static Holder getCollectionFromSizeComparison(PsiExpression condition, PsiVariable variable, PsiElement secondDeclaredElement) { |
| condition = ParenthesesUtils.stripParentheses(condition); |
| if (!(condition instanceof PsiBinaryExpression)) { |
| return null; |
| } |
| final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)condition; |
| final IElementType tokenType = binaryExpression.getOperationTokenType(); |
| final PsiExpression rhs = binaryExpression.getROperand(); |
| final PsiExpression lhs = binaryExpression.getLOperand(); |
| if (tokenType.equals(JavaTokenType.LT)) { |
| if (!VariableAccessUtils.evaluatesToVariable(lhs, variable)) { |
| return null; |
| } |
| return getCollectionFromListMethodCall(rhs, HardcodedMethodConstants.SIZE, secondDeclaredElement); |
| } |
| else if (tokenType.equals(JavaTokenType.GT)) { |
| if (!VariableAccessUtils.evaluatesToVariable(rhs, variable)) { |
| return null; |
| } |
| return getCollectionFromListMethodCall(lhs, HardcodedMethodConstants.SIZE, secondDeclaredElement); |
| } |
| return null; |
| } |
| |
| static boolean expressionIsListGetLookup(PsiExpression expression) { |
| expression = ParenthesesUtils.stripParentheses(expression); |
| if (!(expression instanceof PsiMethodCallExpression)) { |
| return false; |
| } |
| final PsiMethodCallExpression reference = |
| (PsiMethodCallExpression)expression; |
| final PsiReferenceExpression methodExpression = |
| reference.getMethodExpression(); |
| final PsiElement resolved = methodExpression.resolve(); |
| if (!(resolved instanceof PsiMethod)) { |
| return false; |
| } |
| final PsiMethod method = (PsiMethod)resolved; |
| if (!HardcodedMethodConstants.GET.equals(method.getName())) { |
| return false; |
| } |
| final PsiClass aClass = method.getContainingClass(); |
| return InheritanceUtil.isInheritor(aClass, |
| CommonClassNames.JAVA_UTIL_LIST); |
| } |
| |
| @Nullable |
| private static Holder getCollectionFromListMethodCall(PsiExpression expression, String methodName, PsiElement secondDeclaredElement) { |
| expression = ParenthesesUtils.stripParentheses(expression); |
| if (expression instanceof PsiReferenceExpression) { |
| final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression; |
| final PsiElement target = referenceExpression.resolve(); |
| if (secondDeclaredElement != null && !secondDeclaredElement.equals(target)) { |
| return null; |
| } |
| if (!(target instanceof PsiVariable)) { |
| return null; |
| } |
| final PsiVariable variable = (PsiVariable)target; |
| final PsiCodeBlock context = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class); |
| if (context == null) { |
| return null; |
| } |
| if (VariableAccessUtils.variableIsAssigned(variable, context)) { |
| return null; |
| } |
| expression = ParenthesesUtils.stripParentheses(variable.getInitializer()); |
| } |
| else if (secondDeclaredElement != null) { |
| return null; |
| } |
| if (!(expression instanceof PsiMethodCallExpression)) { |
| return null; |
| } |
| final PsiMethodCallExpression methodCallExpression = |
| (PsiMethodCallExpression)expression; |
| final PsiReferenceExpression methodExpression = |
| methodCallExpression.getMethodExpression(); |
| final String referenceName = methodExpression.getReferenceName(); |
| if (!methodName.equals(referenceName)) { |
| return null; |
| } |
| final PsiMethod method = methodCallExpression.resolveMethod(); |
| if (method == null) { |
| return null; |
| } |
| final PsiClass containingClass = method.getContainingClass(); |
| if (!InheritanceUtil.isInheritor(containingClass, |
| CommonClassNames.JAVA_UTIL_LIST)) { |
| return null; |
| } |
| final PsiExpression qualifierExpression = |
| ParenthesesUtils.stripParentheses( |
| methodExpression.getQualifierExpression()); |
| if (qualifierExpression == null || |
| qualifierExpression instanceof PsiThisExpression || |
| qualifierExpression instanceof PsiSuperExpression) { |
| return Holder.DUMMY; |
| } |
| if (!(qualifierExpression instanceof PsiReferenceExpression)) { |
| return null; |
| } |
| final PsiReferenceExpression referenceExpression = |
| (PsiReferenceExpression)qualifierExpression; |
| final PsiElement target = referenceExpression.resolve(); |
| if (!(target instanceof PsiVariable)) { |
| return null; |
| } |
| final PsiVariable variable = (PsiVariable)target; |
| return new Holder(variable); |
| } |
| |
| private static boolean expressionIsArrayLengthLookup(PsiExpression expression) { |
| expression = ParenthesesUtils.stripParentheses(expression); |
| if (!(expression instanceof PsiReferenceExpression)) { |
| return false; |
| } |
| final PsiReferenceExpression reference = |
| (PsiReferenceExpression)expression; |
| final String referenceName = reference.getReferenceName(); |
| if (!HardcodedMethodConstants.LENGTH.equals(referenceName)) { |
| return false; |
| } |
| final PsiExpression qualifier = reference.getQualifierExpression(); |
| if (!(qualifier instanceof PsiReferenceExpression)) { |
| return false; |
| } |
| final PsiType type = qualifier.getType(); |
| return type != null && type.getArrayDimensions() > 0; |
| } |
| |
| @Override |
| @NotNull |
| public String getID() { |
| return "ForLoopReplaceableByForEach"; |
| } |
| |
| @Override |
| @NotNull |
| public String getDisplayName() { |
| return InspectionGadgetsBundle.message( |
| "for.can.be.foreach.display.name"); |
| } |
| |
| @Override |
| public boolean isEnabledByDefault() { |
| return true; |
| } |
| |
| @Override |
| @NotNull |
| protected String buildErrorString(Object... infos) { |
| return InspectionGadgetsBundle.message( |
| "for.can.be.foreach.problem.descriptor"); |
| } |
| |
| @Override |
| public BaseInspectionVisitor buildVisitor() { |
| return new ForCanBeForeachVisitor(); |
| } |
| |
| private static class NumCallsToIteratorNextVisitor |
| extends JavaRecursiveElementVisitor { |
| |
| private int numCallsToIteratorNext = 0; |
| private final PsiVariable iterator; |
| |
| NumCallsToIteratorNextVisitor(PsiVariable iterator) { |
| this.iterator = iterator; |
| } |
| |
| @Override |
| public void visitMethodCallExpression( |
| @NotNull PsiMethodCallExpression callExpression) { |
| super.visitMethodCallExpression(callExpression); |
| final PsiReferenceExpression methodExpression = |
| callExpression.getMethodExpression(); |
| final String methodName = methodExpression.getReferenceName(); |
| if (!HardcodedMethodConstants.NEXT.equals(methodName)) { |
| return; |
| } |
| final PsiExpression qualifier = |
| methodExpression.getQualifierExpression(); |
| if (qualifier == null) { |
| return; |
| } |
| if (!(qualifier instanceof PsiReferenceExpression)) { |
| return; |
| } |
| final PsiReferenceExpression referenceExpression = |
| (PsiReferenceExpression)qualifier; |
| final PsiElement target = referenceExpression.resolve(); |
| if (!iterator.equals(target)) { |
| return; |
| } |
| numCallsToIteratorNext++; |
| } |
| |
| public int getNumCallsToIteratorNext() { |
| return numCallsToIteratorNext; |
| } |
| } |
| |
| private static class IteratorMethodCallVisitor |
| extends JavaRecursiveElementVisitor { |
| |
| private boolean methodCalled = false; |
| private final PsiVariable iterator; |
| |
| IteratorMethodCallVisitor(PsiVariable iterator) { |
| this.iterator = iterator; |
| } |
| |
| @Override |
| public void visitElement(@NotNull PsiElement element) { |
| if (!methodCalled) { |
| super.visitElement(element); |
| } |
| } |
| |
| @Override |
| public void visitMethodCallExpression( |
| @NotNull PsiMethodCallExpression expression) { |
| if (methodCalled) { |
| return; |
| } |
| super.visitMethodCallExpression(expression); |
| final PsiReferenceExpression methodExpression = |
| expression.getMethodExpression(); |
| final String name = methodExpression.getReferenceName(); |
| if (HardcodedMethodConstants.NEXT.equals(name)) { |
| return; |
| } |
| final PsiExpression qualifier = |
| methodExpression.getQualifierExpression(); |
| if (!(qualifier instanceof PsiReferenceExpression)) { |
| return; |
| } |
| final PsiReferenceExpression referenceExpression = |
| (PsiReferenceExpression)qualifier; |
| final PsiElement target = referenceExpression.resolve(); |
| if (iterator.equals(target)) { |
| methodCalled = true; |
| } |
| } |
| |
| public boolean isMethodCalled() { |
| return methodCalled; |
| } |
| } |
| |
| private static class VariableOnlyUsedAsIndexVisitor |
| extends JavaRecursiveElementVisitor { |
| |
| private boolean indexVariableUsedOnlyAsIndex = true; |
| private final PsiVariable arrayVariable; |
| private final PsiVariable indexVariable; |
| |
| VariableOnlyUsedAsIndexVisitor(PsiVariable arrayVariable, |
| PsiVariable indexVariable) { |
| this.arrayVariable = arrayVariable; |
| this.indexVariable = indexVariable; |
| } |
| |
| @Override |
| public void visitElement(@NotNull PsiElement element) { |
| if (indexVariableUsedOnlyAsIndex) { |
| super.visitElement(element); |
| } |
| } |
| |
| @Override |
| public void visitReferenceExpression( |
| @NotNull PsiReferenceExpression reference) { |
| if (!indexVariableUsedOnlyAsIndex) { |
| return; |
| } |
| super.visitReferenceExpression(reference); |
| final PsiElement element = reference.resolve(); |
| if (!indexVariable.equals(element)) { |
| return; |
| } |
| final PsiElement parent = reference.getParent(); |
| if (!(parent instanceof PsiArrayAccessExpression)) { |
| indexVariableUsedOnlyAsIndex = false; |
| return; |
| } |
| final PsiArrayAccessExpression arrayAccessExpression = |
| (PsiArrayAccessExpression)parent; |
| final PsiExpression arrayExpression = |
| arrayAccessExpression.getArrayExpression(); |
| if (!(arrayExpression instanceof PsiReferenceExpression)) { |
| indexVariableUsedOnlyAsIndex = false; |
| return; |
| } |
| final PsiReferenceExpression referenceExpression = |
| (PsiReferenceExpression)arrayExpression; |
| final PsiExpression qualifier = |
| referenceExpression.getQualifierExpression(); |
| if (qualifier != null && !(qualifier instanceof PsiThisExpression) |
| && !(qualifier instanceof PsiSuperExpression)) { |
| indexVariableUsedOnlyAsIndex = false; |
| return; |
| } |
| final PsiElement target = referenceExpression.resolve(); |
| if (!arrayVariable.equals(target)) { |
| indexVariableUsedOnlyAsIndex = false; |
| return; |
| } |
| final PsiElement arrayExpressionContext = |
| arrayAccessExpression.getParent(); |
| if (arrayExpressionContext instanceof PsiAssignmentExpression) { |
| final PsiAssignmentExpression assignment = |
| (PsiAssignmentExpression)arrayExpressionContext; |
| final PsiExpression lhs = assignment.getLExpression(); |
| if (lhs.equals(arrayAccessExpression)) { |
| indexVariableUsedOnlyAsIndex = false; |
| } |
| } |
| } |
| |
| public boolean isIndexVariableUsedOnlyAsIndex() { |
| return indexVariableUsedOnlyAsIndex; |
| } |
| } |
| |
| private static class VariableOnlyUsedAsListIndexVisitor |
| extends JavaRecursiveElementVisitor { |
| |
| private boolean indexVariableUsedOnlyAsIndex = true; |
| private boolean listGetCalled = false; |
| private final PsiVariable indexVariable; |
| private final Holder collection; |
| |
| VariableOnlyUsedAsListIndexVisitor( |
| @NotNull Holder collection, |
| @NotNull PsiVariable indexVariable) { |
| this.collection = collection; |
| this.indexVariable = indexVariable; |
| } |
| |
| @Override |
| public void visitElement(@NotNull PsiElement element) { |
| if (indexVariableUsedOnlyAsIndex) { |
| super.visitElement(element); |
| } |
| } |
| |
| @Override |
| public void visitReferenceExpression( |
| @NotNull PsiReferenceExpression reference) { |
| if (!indexVariableUsedOnlyAsIndex) { |
| return; |
| } |
| super.visitReferenceExpression(reference); |
| final PsiElement element = reference.resolve(); |
| if (indexVariable.equals(element)) { |
| if (!isListIndexExpression(reference)) { |
| indexVariableUsedOnlyAsIndex = false; |
| } |
| else { |
| listGetCalled = true; |
| } |
| } |
| else if (collection == Holder.DUMMY) { |
| if (isListNonGetMethodCall(reference)) { |
| indexVariableUsedOnlyAsIndex = false; |
| } |
| } |
| else if (collection.getVariable().equals(element) && |
| !isListReferenceInIndexExpression(reference)) { |
| indexVariableUsedOnlyAsIndex = false; |
| } |
| } |
| |
| public boolean isIndexVariableUsedOnlyAsIndex() { |
| return indexVariableUsedOnlyAsIndex && listGetCalled; |
| } |
| |
| private boolean isListNonGetMethodCall( |
| PsiReferenceExpression reference) { |
| final PsiElement parent = reference.getParent(); |
| if (!(parent instanceof PsiMethodCallExpression)) { |
| return false; |
| } |
| final PsiMethodCallExpression methodCallExpression = |
| (PsiMethodCallExpression)parent; |
| final PsiMethod method = |
| methodCallExpression.resolveMethod(); |
| if (method == null) { |
| return false; |
| } |
| final PsiClass parentClass = PsiTreeUtil.getParentOfType( |
| methodCallExpression, PsiClass.class); |
| final PsiClass containingClass = method.getContainingClass(); |
| if (!InheritanceUtil.isInheritorOrSelf(parentClass, |
| containingClass, true)) { |
| return false; |
| } |
| return !isListGetExpression(methodCallExpression); |
| } |
| |
| private boolean isListIndexExpression(PsiReferenceExpression reference) { |
| final PsiElement referenceParent = reference.getParent(); |
| if (!(referenceParent instanceof PsiExpressionList)) { |
| return false; |
| } |
| final PsiExpressionList expressionList = |
| (PsiExpressionList)referenceParent; |
| final PsiElement parent = expressionList.getParent(); |
| if (!(parent instanceof PsiMethodCallExpression)) { |
| return false; |
| } |
| final PsiMethodCallExpression methodCallExpression = |
| (PsiMethodCallExpression)parent; |
| return isListGetExpression(methodCallExpression); |
| } |
| |
| private boolean isListReferenceInIndexExpression( |
| PsiReferenceExpression reference) { |
| final PsiElement parent = reference.getParent(); |
| if (!(parent instanceof PsiReferenceExpression)) { |
| return false; |
| } |
| final PsiElement grandParent = parent.getParent(); |
| if (!(grandParent instanceof PsiMethodCallExpression)) { |
| return false; |
| } |
| final PsiMethodCallExpression methodCallExpression = |
| (PsiMethodCallExpression)grandParent; |
| final PsiElement greatGrandParent = |
| methodCallExpression.getParent(); |
| if (greatGrandParent instanceof PsiExpressionStatement) { |
| return false; |
| } |
| return isListGetExpression(methodCallExpression); |
| } |
| |
| private boolean isListGetExpression( |
| PsiMethodCallExpression methodCallExpression) { |
| if (methodCallExpression == null) { |
| return false; |
| } |
| final PsiReferenceExpression methodExpression = |
| methodCallExpression.getMethodExpression(); |
| final PsiExpression qualifierExpression = |
| methodExpression.getQualifierExpression(); |
| if (!(qualifierExpression instanceof PsiReferenceExpression)) { |
| if (collection == Holder.DUMMY && |
| (qualifierExpression == null || |
| qualifierExpression instanceof PsiThisExpression || |
| qualifierExpression instanceof PsiSuperExpression)) { |
| return expressionIsListGetLookup(methodCallExpression); |
| } |
| return false; |
| } |
| final PsiReferenceExpression reference = |
| (PsiReferenceExpression)qualifierExpression; |
| final PsiExpression qualifier = reference.getQualifierExpression(); |
| if (qualifier != null && !(qualifier instanceof PsiThisExpression) |
| && !(qualifier instanceof PsiSuperExpression)) { |
| return false; |
| } |
| final PsiElement target = reference.resolve(); |
| if (collection == Holder.DUMMY || |
| !collection.getVariable().equals(target)) { |
| return false; |
| } |
| return expressionIsListGetLookup(methodCallExpression); |
| } |
| } |
| |
| private static class Holder { |
| |
| public static final Holder DUMMY = new Holder(); |
| |
| private final PsiVariable variable; |
| |
| public Holder(@NotNull PsiVariable variable) { |
| this.variable = variable; |
| } |
| |
| private Holder() { |
| variable = null; |
| } |
| |
| public PsiVariable getVariable() { |
| return variable; |
| } |
| } |
| |
| private class ForCanBeForeachVisitor extends BaseInspectionVisitor { |
| |
| @Override |
| public void visitForStatement(@NotNull PsiForStatement forStatement) { |
| super.visitForStatement(forStatement); |
| if (!PsiUtil.isLanguageLevel5OrHigher(forStatement)) { |
| return; |
| } |
| if (isArrayLoopStatement(forStatement) || isCollectionLoopStatement(forStatement, ignoreUntypedCollections) || |
| REPORT_INDEXED_LOOP && isIndexedListLoopStatement(forStatement, ignoreUntypedCollections)) { |
| registerStatementError(forStatement); |
| } |
| } |
| } |
| } |