summaryrefslogtreecommitdiff
path: root/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/WhileCanBeForeachInspectionBase.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/WhileCanBeForeachInspectionBase.java')
-rw-r--r--plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/WhileCanBeForeachInspectionBase.java337
1 files changed, 337 insertions, 0 deletions
diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/WhileCanBeForeachInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/WhileCanBeForeachInspectionBase.java
new file mode 100644
index 000000000000..50ade6b36a09
--- /dev/null
+++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/WhileCanBeForeachInspectionBase.java
@@ -0,0 +1,337 @@
+/*
+ * 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.siyeh.ig.migration;
+
+import com.intellij.psi.*;
+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.TypeUtils;
+import com.siyeh.ig.psiutils.VariableAccessUtils;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class WhileCanBeForeachInspectionBase extends BaseInspection {
+ @Nullable
+ public static PsiStatement getPreviousStatement(PsiElement context) {
+ final PsiElement prevStatement = PsiTreeUtil.skipSiblingsBackward(context, PsiWhiteSpace.class, PsiComment.class);
+ if (!(prevStatement instanceof PsiStatement)) {
+ return null;
+ }
+ return (PsiStatement)prevStatement;
+ }
+
+ @Override
+ @NotNull
+ public String getID() {
+ return "WhileLoopReplaceableByForEach";
+ }
+
+ @Override
+ @NotNull
+ public String getDisplayName() {
+ return InspectionGadgetsBundle.message("while.can.be.foreach.display.name");
+ }
+
+ @Override
+ @NotNull
+ protected String buildErrorString(Object... infos) {
+ return InspectionGadgetsBundle.message("while.can.be.foreach.problem.descriptor");
+ }
+
+ @Override
+ public boolean isEnabledByDefault() {
+ return true;
+ }
+
+ @Override
+ public BaseInspectionVisitor buildVisitor() {
+ return new WhileCanBeForeachVisitor();
+ }
+
+ private static class WhileCanBeForeachVisitor extends BaseInspectionVisitor {
+
+ @Override
+ public void visitWhileStatement(@NotNull PsiWhileStatement whileStatement) {
+ super.visitWhileStatement(whileStatement);
+ if (!PsiUtil.isLanguageLevel5OrHigher(whileStatement)) {
+ return;
+ }
+ if (!isCollectionLoopStatement(whileStatement)) {
+ return;
+ }
+ registerStatementError(whileStatement);
+ }
+
+ private static boolean isCollectionLoopStatement(PsiWhileStatement whileStatement) {
+ final PsiStatement initialization = getPreviousStatement(whileStatement);
+ 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 PsiExpressionList argumentList = initialCall.getArgumentList();
+ final PsiExpression[] argument = argumentList.getExpressions();
+ if (argument.length != 0) {
+ return false;
+ }
+ final PsiReferenceExpression initialMethodExpression = initialCall.getMethodExpression();
+ @NonNls final String initialCallName = initialMethodExpression.getReferenceName();
+ if (!"iterator".equals(initialCallName) && !"listIterator".equals(initialCallName)) {
+ return false;
+ }
+ final PsiExpression qualifier = initialMethodExpression.getQualifierExpression();
+ if (qualifier instanceof PsiSuperExpression) {
+ return false;
+ }
+ final PsiClass qualifierClass;
+ if (qualifier != null) {
+ final PsiType qualifierType = qualifier.getType();
+ if (!(qualifierType instanceof PsiClassType)) {
+ return false;
+ }
+ qualifierClass = ((PsiClassType)qualifierType).resolve();
+ }
+ else {
+ qualifierClass = PsiTreeUtil.getParentOfType(whileStatement, PsiClass.class);
+ }
+ if (qualifierClass == null) {
+ return false;
+ }
+ if (!InheritanceUtil.isInheritor(qualifierClass, CommonClassNames.JAVA_LANG_ITERABLE)) {
+ return false;
+ }
+ final PsiExpression condition = whileStatement.getCondition();
+ if (!isHasNextCalled(variable, condition)) {
+ return false;
+ }
+ final PsiStatement body = whileStatement.getBody();
+ if (body == null) {
+ return false;
+ }
+ if (calculateCallsToIteratorNext(variable, body) != 1) {
+ return false;
+ }
+ if (isIteratorRemoveCalled(variable, body)) {
+ return false;
+ }
+ //noinspection SimplifiableIfStatement
+ if (isIteratorHasNextCalled(variable, body)) {
+ return false;
+ }
+ if (VariableAccessUtils.variableIsAssigned(variable, body)) {
+ return false;
+ }
+ if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, body)) {
+ return false;
+ }
+ PsiElement nextSibling = whileStatement.getNextSibling();
+ while (nextSibling != null) {
+ if (VariableAccessUtils.variableValueIsUsed(variable, nextSibling)) {
+ return false;
+ }
+ nextSibling = nextSibling.getNextSibling();
+ }
+ return true;
+ }
+
+ private static boolean isHasNextCalled(PsiVariable iterator, PsiExpression condition) {
+ 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();
+ @NonNls 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);
+ }
+
+ private static int calculateCallsToIteratorNext(PsiVariable iterator, PsiElement context) {
+ final NumberCallsToIteratorNextVisitor visitor = new NumberCallsToIteratorNextVisitor(iterator);
+ context.accept(visitor);
+ return visitor.getNumCallsToIteratorNext();
+ }
+
+ private static boolean isIteratorRemoveCalled(PsiVariable iterator, PsiElement context) {
+ final IteratorMethodCallVisitor visitor = new IteratorMethodCallVisitor(iterator);
+ context.accept(visitor);
+ return visitor.isMethodCalled();
+ }
+
+ private static boolean isIteratorHasNextCalled(PsiVariable iterator, PsiElement context) {
+ final IteratorHasNextVisitor visitor = new IteratorHasNextVisitor(iterator);
+ context.accept(visitor);
+ return visitor.isHasNextCalled();
+ }
+ }
+
+ private static class NumberCallsToIteratorNextVisitor extends JavaRecursiveElementVisitor {
+
+ private int numCallsToIteratorNext = 0;
+ private final PsiVariable iterator;
+
+ private NumberCallsToIteratorNextVisitor(PsiVariable iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public void visitMethodCallExpression(@NotNull PsiMethodCallExpression callExpression) {
+ super.visitMethodCallExpression(callExpression);
+ final PsiReferenceExpression methodExpression = callExpression.getMethodExpression();
+ @NonNls final String methodName = methodExpression.getReferenceName();
+ if (!HardcodedMethodConstants.NEXT.equals(methodName)) {
+ 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)) {
+ 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 IteratorHasNextVisitor extends JavaRecursiveElementVisitor {
+
+ private boolean hasNextCalled = false;
+ private final PsiVariable iterator;
+
+ private IteratorHasNextVisitor(PsiVariable iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public void visitElement(@NotNull PsiElement element) {
+ if (!hasNextCalled) {
+ super.visitElement(element);
+ }
+ }
+
+ @Override
+ public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
+ super.visitMethodCallExpression(expression);
+ final PsiReferenceExpression methodExpression = expression.getMethodExpression();
+ @NonNls final String name = methodExpression.getReferenceName();
+ if (!HardcodedMethodConstants.HAS_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)) {
+ hasNextCalled = true;
+ }
+ }
+
+ public boolean isHasNextCalled() {
+ return hasNextCalled;
+ }
+ }
+}