Make "manual array copy" and "manual array to collection copy" applicable in more...
authorBas Leijdekkers <leijdekkers@carp-technologies.nl>
Wed, 15 Sep 2010 12:53:22 +0000 (14:53 +0200)
committerBas Leijdekkers <leijdekkers@carp-technologies.nl>
Wed, 15 Sep 2010 12:53:22 +0000 (14:53 +0200)
plugins/InspectionGadgets/src/com/siyeh/ig/performance/ManualArrayCopyInspection.java
plugins/InspectionGadgets/src/com/siyeh/ig/performance/ManualArrayToCollectionCopyInspection.java
plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ExpressionUtils.java
plugins/InspectionGadgets/test/com/siyeh/igtest/performance/manual_array_copy/ManualArrayCopy.java

index 7660e5956b68226d05fceb29c44b320fceffe62e..8d0d7ac2da51536b6b38a93bdc3f6d46ddcfe774 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2009 Dave Griffith, Bas Leijdekkers
+ * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@ import com.intellij.codeInspection.ProblemDescriptor;
 import com.intellij.openapi.project.Project;
 import com.intellij.psi.*;
 import com.intellij.psi.tree.IElementType;
-import com.intellij.psi.util.PsiUtil;
 import com.intellij.util.IncorrectOperationException;
 import com.siyeh.InspectionGadgetsBundle;
 import com.siyeh.ig.BaseInspection;
@@ -78,7 +77,7 @@ public class ManualArrayCopyInspection extends BaseInspection {
             final PsiElement forElement = descriptor.getPsiElement();
             final PsiForStatement forStatement =
                     (PsiForStatement)forElement.getParent();
-            final String newExpression = getSystemArrayCopyText(forStatement);
+            final String newExpression = buildSystemArrayCopyText(forStatement);
             if (newExpression == null) {
                 return;
             }
@@ -86,19 +85,21 @@ public class ManualArrayCopyInspection extends BaseInspection {
         }
 
         @Nullable
-        private static String getSystemArrayCopyText(
+        private static String buildSystemArrayCopyText(
                 PsiForStatement forStatement)
                 throws IncorrectOperationException {
             final PsiExpression condition = forStatement.getCondition();
             final PsiBinaryExpression binaryExpression =
-                    (PsiBinaryExpression)PsiUtil.deparenthesizeExpression(
+                    (PsiBinaryExpression)ParenthesesUtils.stripParentheses(
                             condition);
             if (binaryExpression == null) {
                 return null;
             }
+            final IElementType tokenType =
+                    binaryExpression.getOperationTokenType();
             final PsiExpression limit;
-            if (JavaTokenType.LT.equals(
-                    binaryExpression.getOperationTokenType()))  {
+            if (JavaTokenType.LT.equals(tokenType) ||
+                    JavaTokenType.LE.equals(tokenType)) {
                 limit = binaryExpression.getROperand();
             } else {
                 limit = binaryExpression.getLOperand();
@@ -126,24 +127,18 @@ public class ManualArrayCopyInspection extends BaseInspection {
                 return null;
             }
             final PsiLocalVariable variable = (PsiLocalVariable)declaredElement;
-            final String lengthText = getLengthText(limit, variable);
-            final PsiExpressionStatement body = getBody(forStatement);
-            if (body == null) {
-                return null;
-            }
-            final PsiAssignmentExpression assignment =
-                    (PsiAssignmentExpression)body.getExpression();
-            final PsiExpression lExpression = assignment.getLExpression();
-            final PsiArrayAccessExpression lhs = (PsiArrayAccessExpression)
-                    PsiUtil.deparenthesizeExpression(lExpression);
+            final String lengthText = buildLengthText(limit, variable,
+                    JavaTokenType.LE.equals(tokenType) ||
+                            JavaTokenType.GE.equals(tokenType));
+            final PsiArrayAccessExpression lhs =
+                    getLhsArrayAccessExpression(forStatement);
             if (lhs == null) {
                 return null;
             }
             final PsiExpression lArray = lhs.getArrayExpression();
             final String toArrayText = lArray.getText();
-            final PsiExpression rExpression = assignment.getRExpression();
-            final PsiArrayAccessExpression rhs = (PsiArrayAccessExpression)
-                    PsiUtil.deparenthesizeExpression(rExpression);
+            final PsiArrayAccessExpression rhs =
+                    getRhsArrayAccessExpression(forStatement);
             if (rhs == null) {
                 return null;
             }
@@ -151,14 +146,14 @@ public class ManualArrayCopyInspection extends BaseInspection {
             final String fromArrayText = rArray.getText();
             final PsiExpression rhsIndexExpression = rhs.getIndexExpression();
             final PsiExpression strippedRhsIndexExpression =
-                    PsiUtil.deparenthesizeExpression(rhsIndexExpression);
+                    ParenthesesUtils.stripParentheses(rhsIndexExpression);
             final String fromOffsetText =
-                    getOffsetText(strippedRhsIndexExpression, variable);
+                    buildOffsetText(strippedRhsIndexExpression, variable);
             final PsiExpression lhsIndexExpression = lhs.getIndexExpression();
             final PsiExpression strippedLhsIndexExpression =
-                    PsiUtil.deparenthesizeExpression(lhsIndexExpression);
+                    ParenthesesUtils.stripParentheses(lhsIndexExpression);
             final String toOffsetText =
-                    getOffsetText(strippedLhsIndexExpression, variable);
+                    buildOffsetText(strippedLhsIndexExpression, variable);
             @NonNls final StringBuilder buffer = new StringBuilder(60);
             buffer.append("System.arraycopy(");
             buffer.append(fromArrayText);
@@ -174,16 +169,108 @@ public class ManualArrayCopyInspection extends BaseInspection {
             return buffer.toString();
         }
 
+        private static PsiArrayAccessExpression getLhsArrayAccessExpression(
+                PsiForStatement forStatement) {
+            PsiStatement body = forStatement.getBody();
+            while (body instanceof PsiBlockStatement) {
+                final PsiBlockStatement blockStatement =
+                        (PsiBlockStatement) body;
+                final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
+                final PsiStatement[] statements = codeBlock.getStatements();
+                if (statements.length == 2) {
+                    body = statements[1];
+                } else if (statements.length == 1) {
+                    body = statements[0];
+                } else {
+                    return null;
+                }
+            }
+            if (!(body instanceof PsiExpressionStatement)) {
+                return null;
+            }
+            final PsiExpressionStatement expressionStatement =
+                    (PsiExpressionStatement) body;
+            final PsiExpression expression =
+                    expressionStatement.getExpression();
+            if (!(expression instanceof PsiAssignmentExpression)) {
+                return null;
+            }
+            final PsiAssignmentExpression assignmentExpression =
+                    (PsiAssignmentExpression) expression;
+            final PsiExpression lhs = assignmentExpression.getLExpression();
+
+            final PsiExpression deparenthesizedExpression =
+                    ParenthesesUtils.stripParentheses(lhs);
+            if (!(deparenthesizedExpression instanceof
+                    PsiArrayAccessExpression)) {
+                return null;
+            }
+            return (PsiArrayAccessExpression) deparenthesizedExpression;
+        }
+
+        private static PsiArrayAccessExpression getRhsArrayAccessExpression(
+                PsiForStatement forStatement) {
+            PsiStatement body = forStatement.getBody();
+            while (body instanceof PsiBlockStatement) {
+                final PsiBlockStatement blockStatement =
+                        (PsiBlockStatement) body;
+                final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
+                final PsiStatement[] statements = codeBlock.getStatements();
+                if (statements.length == 1 || statements.length == 2) {
+                    body = statements[0];
+                } else {
+                    return null;
+                }
+            }
+            final PsiExpression arrayAccessExpression;
+            if (body instanceof PsiDeclarationStatement) {
+                final PsiDeclarationStatement declarationStatement =
+                        (PsiDeclarationStatement) body;
+                final PsiElement[] declaredElements =
+                        declarationStatement.getDeclaredElements();
+                if (declaredElements.length != 1) {
+                    return null;
+                }
+                final PsiElement declaredElement = declaredElements[0];
+                if (!(declaredElement instanceof PsiVariable)) {
+                    return null;
+                }
+                final PsiVariable variable = (PsiVariable) declaredElement;
+                arrayAccessExpression = variable.getInitializer();
+            } else if (body instanceof PsiExpressionStatement) {
+                final PsiExpressionStatement expressionStatement =
+                        (PsiExpressionStatement) body;
+                final PsiExpression expression =
+                        expressionStatement.getExpression();
+                if (!(expression instanceof PsiAssignmentExpression)) {
+                    return null;
+                }
+                final PsiAssignmentExpression assignmentExpression =
+                        (PsiAssignmentExpression) expression;
+                arrayAccessExpression = assignmentExpression.getRExpression();
+            } else {
+                return null;
+            }
+            final PsiExpression unparenthesizedExpression =
+                    ParenthesesUtils.stripParentheses(arrayAccessExpression);
+            if (!(unparenthesizedExpression instanceof
+                    PsiArrayAccessExpression)) {
+                return null;
+            }
+            return (PsiArrayAccessExpression) unparenthesizedExpression;
+        }
+
         @NonNls @Nullable
-        private static String getLengthText(PsiExpression expression,
-                                            PsiVariable variable) {
-            expression =
-                    PsiUtil.deparenthesizeExpression(expression);
+        private static String buildLengthText(PsiExpression expression,
+                                            PsiVariable variable,
+                                            boolean plusOne) {
+            expression = ParenthesesUtils.stripParentheses(expression);
             if (expression == null) {
                 return null;
             }
+            final String expressionText =
+                    buildExpressionText(expression, plusOne);
             final PsiExpression initializer = variable.getInitializer();
-            final String expressionText = expression.getText();
             if (initializer == null) {
                 return expressionText;
             }
@@ -192,7 +279,7 @@ public class ManualArrayCopyInspection extends BaseInspection {
             }
             final int precedence = ParenthesesUtils.getPrecedence(initializer);
             final String initializerText;
-            if (precedence >= ParenthesesUtils.ADDITIVE_PRECEDENCE) {
+            if (precedence > ParenthesesUtils.ADDITIVE_PRECEDENCE) {
                 initializerText = '(' + initializer.getText() + ')';
             } else {
                 initializerText = initializer.getText();
@@ -200,8 +287,34 @@ public class ManualArrayCopyInspection extends BaseInspection {
             return expressionText + '-' + initializerText;
         }
 
+        private static String buildExpressionText(PsiExpression expression,
+                                                  boolean plusOne) {
+            if (!plusOne) {
+                return expression.getText();
+            }
+            if (expression instanceof PsiBinaryExpression) {
+                final PsiBinaryExpression binaryExpression =
+                        (PsiBinaryExpression) expression;
+                final IElementType tokenType =
+                        binaryExpression.getOperationTokenType();
+                if (tokenType == JavaTokenType.MINUS)  {
+                    final PsiExpression rhs =
+                            binaryExpression.getROperand();
+                    if (ExpressionUtils.isOne(rhs)) {
+                        return binaryExpression.getLOperand().getText();
+                    }
+                }
+            }
+            final int precedence = ParenthesesUtils.getPrecedence(expression);
+            if (precedence > ParenthesesUtils.ADDITIVE_PRECEDENCE) {
+                return '(' + expression.getText() + ")+1";
+            } else {
+                return expression.getText() + "+1";
+            }
+        }
+
         @NonNls @Nullable
-        private static String getOffsetText(PsiExpression expression,
+        private static String buildOffsetText(PsiExpression expression,
                                             PsiLocalVariable variable)
                 throws IncorrectOperationException {
             if (expression == null) {
@@ -220,18 +333,18 @@ public class ManualArrayCopyInspection extends BaseInspection {
                 final PsiBinaryExpression binaryExpression =
                         (PsiBinaryExpression)expression;
                 final PsiExpression lhs = binaryExpression.getLOperand();
-                final String lhsText = getOffsetText(lhs, variable);
                 final PsiExpression rhs = binaryExpression.getROperand();
-                final String rhsText = getOffsetText(rhs, variable);
+                final String rhsText = buildOffsetText(rhs, variable);
                 final PsiJavaToken sign = binaryExpression.getOperationSign();
                 final IElementType tokenType = sign.getTokenType();
-                if (lhsText == null || lhsText.equals("0")) {
+                if (ExpressionUtils.isZero(lhs)) {
                     if (tokenType.equals(JavaTokenType.MINUS)) {
                         return '-' + rhsText;
                     }
                     return rhsText;
                 }
-                if (rhsText == null || rhsText.equals("0")) {
+                final String lhsText = buildOffsetText(lhs, variable);
+                if (ExpressionUtils.isZero(rhs)) {
                     return lhsText;
                 }
                 return collapseConstant(lhsText + sign.getText() + rhsText,
@@ -257,20 +370,6 @@ public class ManualArrayCopyInspection extends BaseInspection {
                 return expressionText;
             }
         }
-
-        @Nullable
-        private static PsiExpressionStatement getBody(
-                PsiForStatement forStatement) {
-            PsiStatement body = forStatement.getBody();
-            while (body instanceof PsiBlockStatement) {
-                final PsiBlockStatement blockStatement =
-                        (PsiBlockStatement)body;
-                final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
-                final PsiStatement[] statements = codeBlock.getStatements();
-                body = statements[0];
-            }
-            return (PsiExpressionStatement)body;
-        }
     }
 
     private static class ManualArrayCopyVisitor extends BaseInspectionVisitor {
@@ -278,8 +377,7 @@ public class ManualArrayCopyInspection extends BaseInspection {
         @Override public void visitForStatement(
                 @NotNull PsiForStatement statement) {
             super.visitForStatement(statement);
-            final PsiStatement initialization =
-                    statement.getInitialization();
+            final PsiStatement initialization = statement.getInitialization();
             if (!(initialization instanceof PsiDeclarationStatement)) {
                 return;
             }
@@ -308,35 +406,68 @@ public class ManualArrayCopyInspection extends BaseInspection {
                 return;
             }
             final PsiStatement body = statement.getBody();
-            if (!bodyIsArrayCopy(body, variable)) {
+            if (!bodyIsArrayCopy(body, variable, null)) {
                 return;
             }
             registerStatementError(statement);
         }
 
-        private static boolean bodyIsArrayCopy(PsiStatement body,
-                                               PsiLocalVariable variable) {
+        private static boolean bodyIsArrayCopy(
+                PsiStatement body, PsiVariable variable,
+                @Nullable PsiVariable variable2) {
             if (body instanceof PsiExpressionStatement) {
                 final PsiExpressionStatement exp =
                         (PsiExpressionStatement)body;
                 final PsiExpression expression = exp.getExpression();
-                return expressionIsArrayCopy(expression, variable);
+                return expressionIsArrayCopy(expression, variable, variable2);
             } else if (body instanceof PsiBlockStatement) {
                 final PsiBlockStatement blockStatement =
                         (PsiBlockStatement)body;
                 final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
                 final PsiStatement[] statements = codeBlock.getStatements();
-                return statements.length == 1 &&
-                        bodyIsArrayCopy(statements[0], variable);
+                if (statements.length == 1) {
+                    return bodyIsArrayCopy(statements[0], variable, variable2);
+                } else if (statements.length == 2) {
+                    final PsiStatement statement = statements[0];
+                    if (!(statement instanceof PsiDeclarationStatement)) {
+                        System.out.println(1);
+                        return false;
+                    }
+                    final PsiDeclarationStatement declarationStatement =
+                            (PsiDeclarationStatement) statement;
+                    final PsiElement[] declaredElements =
+                            declarationStatement.getDeclaredElements();
+                    if (declaredElements.length != 1) {
+                        System.out.println(2);
+                        return false;
+                    }
+                    final PsiElement declaredElement = declaredElements[0];
+                    if (!(declaredElement instanceof PsiVariable)) {
+                        System.out.println(3);
+                        return false;
+                    }
+                    final PsiVariable localVariable =
+                            (PsiVariable) declaredElement;
+                    final PsiExpression initializer =
+                            localVariable.getInitializer();
+                    if (!ExpressionUtils.isOffsetArrayAccess(initializer,
+                            variable)) {
+                        System.out.println(4);
+                        return false;
+                    }
+                    return bodyIsArrayCopy(statements[1], variable,
+                            localVariable);
+                }
             }
             return false;
         }
 
         private static boolean expressionIsArrayCopy(
                 @Nullable PsiExpression expression,
-                @NotNull PsiVariable variable) {
+                @NotNull PsiVariable variable,
+                @Nullable PsiVariable variable2) {
             final PsiExpression strippedExpression =
-                    PsiUtil.deparenthesizeExpression(expression);
+                    ParenthesesUtils.stripParentheses(expression);
             if (strippedExpression == null) {
                 return false;
             }
@@ -377,7 +508,11 @@ public class ManualArrayCopyInspection extends BaseInspection {
                     return false;
                 }
             }
-            return ExpressionUtils.isOffsetArrayAccess(rhs, variable);
+            if (variable2 == null) {
+                return ExpressionUtils.isOffsetArrayAccess(rhs, variable);
+            } else {
+                return VariableAccessUtils.evaluatesToVariable(rhs, variable2);
+            }
         }
 
         private static boolean areExpressionsCopyable(
index 87efcf51c2582021a2f849f2d1441da6d8c36804..14e1d85ccc1c87e3a93d0e8330aff96523978764 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006-2009 Bas Leijdekkers
+ * Copyright 2006-2010 Bas Leijdekkers
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,16 +19,12 @@ import com.intellij.codeInspection.ProblemDescriptor;
 import com.intellij.openapi.project.Project;
 import com.intellij.psi.*;
 import com.intellij.psi.tree.IElementType;
-import com.intellij.psi.util.PsiUtil;
 import com.intellij.util.IncorrectOperationException;
 import com.siyeh.InspectionGadgetsBundle;
 import com.siyeh.ig.BaseInspection;
 import com.siyeh.ig.BaseInspectionVisitor;
 import com.siyeh.ig.InspectionGadgetsFix;
-import com.siyeh.ig.psiutils.ClassUtils;
-import com.siyeh.ig.psiutils.ExpressionUtils;
-import com.siyeh.ig.psiutils.SideEffectChecker;
-import com.siyeh.ig.psiutils.VariableAccessUtils;
+import com.siyeh.ig.psiutils.*;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -97,14 +93,16 @@ public class ManualArrayToCollectionCopyInspection
 
         @Nullable
         private static String getCollectionsAddAllText(
-                PsiForeachStatement forStatement)
+                PsiForeachStatement foreachStatement)
                 throws IncorrectOperationException {
-            final PsiExpressionStatement body = getBody(forStatement);
-            if (body == null) {
+            final PsiStatement body = getBody(foreachStatement);
+            if (!(body instanceof PsiExpressionStatement)) {
                 return null;
             }
+            final PsiExpressionStatement expressionStatement =
+                    (PsiExpressionStatement) body;
             final PsiMethodCallExpression methodCallExpression =
-                    (PsiMethodCallExpression)body.getExpression();
+                    (PsiMethodCallExpression)expressionStatement.getExpression();
             final PsiReferenceExpression methodExpression =
                     methodCallExpression.getMethodExpression();
             final PsiElement collection = methodExpression.getQualifier();
@@ -113,7 +111,8 @@ public class ManualArrayToCollectionCopyInspection
                 return null;
             }
             final String collectionText = collection.getText();
-            final PsiExpression iteratedValue = forStatement.getIteratedValue();
+            final PsiExpression iteratedValue =
+                    foreachStatement.getIteratedValue();
             if (iteratedValue == null) {
                 return null;
             }
@@ -132,7 +131,7 @@ public class ManualArrayToCollectionCopyInspection
                 throws IncorrectOperationException {
             final PsiExpression expression = forStatement.getCondition();
             final PsiBinaryExpression condition =
-                    (PsiBinaryExpression)PsiUtil.deparenthesizeExpression(
+                    (PsiBinaryExpression)ParenthesesUtils.stripParentheses(
                             expression);
             if (condition == null) {
                 return null;
@@ -157,59 +156,42 @@ public class ManualArrayToCollectionCopyInspection
                 return null;
             }
             final PsiLocalVariable variable = (PsiLocalVariable)declaredElement;
-            final PsiExpressionStatement body = getBody(forStatement);
-            if (body == null) {
-                return null;
-            }
-            final PsiMethodCallExpression methodCallExpression =
-                    (PsiMethodCallExpression)body.getExpression();
-            final PsiReferenceExpression methodExpression =
-                    methodCallExpression.getMethodExpression();
-            final PsiElement collection = methodExpression.getQualifier();
-            if (collection == null) {
-                // fixme for when the array is added to 'this'
-                return null;
-            }
-            final String collectionText = collection.getText();
-            final PsiExpressionList argumentList =
-                    methodCallExpression.getArgumentList();
-            final PsiExpression argument = argumentList.getExpressions()[0];
-            final PsiExpression deparenthesizedArgument =
-                    PsiUtil.deparenthesizeExpression(argument);
-            if (!(deparenthesizedArgument instanceof
-                    PsiArrayAccessExpression)) {
+            final String collectionText = buildCollectionText(forStatement);
+            final PsiArrayAccessExpression arrayAccessExpression =
+                    getArrayAccessExpression(forStatement);
+            if (arrayAccessExpression == null) {
                 return null;
             }
-            final PsiArrayAccessExpression arrayAccessExpression =
-                    (PsiArrayAccessExpression)
-                            deparenthesizedArgument;
             final PsiExpression arrayExpression =
                     arrayAccessExpression.getArrayExpression();
             final String arrayText = arrayExpression.getText();
             final PsiExpression indexExpression =
                     arrayAccessExpression.getIndexExpression();
             final String fromOffsetText =
-                    getStartOffsetText(indexExpression, variable);
+                    buildFromOffsetText(indexExpression, variable);
             if (fromOffsetText == null) {
                 return null;
             }
-            PsiExpression limit;
-            if (condition.getOperationTokenType() == JavaTokenType.LT)  {
+            final PsiExpression limit;
+            final IElementType tokenType = condition.getOperationTokenType();
+            if (tokenType == JavaTokenType.LT ||
+                    tokenType == JavaTokenType.LE)  {
                 limit = condition.getROperand();
             } else {
                 limit = condition.getLOperand();
             }
-            limit = PsiUtil.deparenthesizeExpression(limit);
-            if (limit == null) {
-                return null;
-            }
-            @NonNls final String toOffsetText = limit.getText();
+            @NonNls final String toOffsetText =
+                    buildToOffsetText(limit, tokenType == JavaTokenType.LE ||
+                            tokenType == JavaTokenType.GE);
             if (toOffsetText == null) {
                 return null;
             }
-            @NonNls final StringBuilder buffer = new StringBuilder(60);
-            buffer.append(collectionText);
-            buffer.append(".addAll(java.util.Arrays.asList(");
+            @NonNls final StringBuilder buffer = new StringBuilder();
+            if (collectionText.length() > 0) {
+                buffer.append(collectionText);
+                buffer.append('.');
+            }
+            buffer.append("addAll(java.util.Arrays.asList(");
             buffer.append(arrayText);
             buffer.append(')');
             if (!fromOffsetText.equals("0") ||
@@ -224,12 +206,94 @@ public class ManualArrayToCollectionCopyInspection
             return buffer.toString();
         }
 
+        private static PsiArrayAccessExpression getArrayAccessExpression(
+                PsiForStatement forStatement) {
+            final PsiStatement body = getBody(forStatement);
+            if (body == null) {
+                return null;
+            }
+            final PsiExpression arrayAccessExpression;
+            if (body instanceof PsiExpressionStatement) {
+                final PsiExpressionStatement expressionStatement =
+                        (PsiExpressionStatement) body;
+                final PsiExpression expression =
+                        expressionStatement.getExpression();
+                if (!(expression instanceof PsiMethodCallExpression)) {
+                    return null;
+                }
+                final PsiMethodCallExpression methodCallExpression =
+                        (PsiMethodCallExpression)expression;
+                final PsiExpressionList argumentList =
+                        methodCallExpression.getArgumentList();
+                arrayAccessExpression = argumentList.getExpressions()[0];
+            } else if (body instanceof PsiDeclarationStatement) {
+                final PsiDeclarationStatement declarationStatement =
+                        (PsiDeclarationStatement) body;
+                final PsiElement[] declaredElements =
+                        declarationStatement.getDeclaredElements();
+                if (declaredElements.length != 1) {
+                    return null;
+                }
+                final PsiElement declaredElement = declaredElements[0];
+                if (!(declaredElement instanceof PsiVariable)) {
+                    return null;
+                }
+                final PsiVariable variable = (PsiVariable) declaredElement;
+                arrayAccessExpression = variable.getInitializer();
+            } else {
+                return null;
+            }
+            final PsiExpression deparenthesizedArgument =
+                    ParenthesesUtils.stripParentheses(arrayAccessExpression);
+            if (!(deparenthesizedArgument instanceof
+                    PsiArrayAccessExpression)) {
+                return null;
+            }
+            return (PsiArrayAccessExpression) deparenthesizedArgument;
+        }
+
+        public static String buildCollectionText(PsiForStatement forStatement) {
+            PsiStatement body = forStatement.getBody();
+            while (body instanceof PsiBlockStatement) {
+                final PsiBlockStatement blockStatement =
+                        (PsiBlockStatement) body;
+                final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
+                final PsiStatement[] statements = codeBlock.getStatements();
+                if (statements.length == 2) {
+                    body = statements[1];
+                } else if (statements.length == 1) {
+                    body = statements[0];
+                } else {
+                    return null;
+                }
+            }
+            if (!(body instanceof PsiExpressionStatement)) {
+                return null;
+            }
+            final PsiExpressionStatement expressionStatement =
+                    (PsiExpressionStatement) body;
+            final PsiExpression expression =
+                    expressionStatement.getExpression();
+            if (!(expression instanceof PsiMethodCallExpression)) {
+                return null;
+            }
+            final PsiMethodCallExpression methodCallExpression =
+                    (PsiMethodCallExpression) expression;
+            final PsiReferenceExpression methodExpression =
+                    methodCallExpression.getMethodExpression();
+            final PsiElement qualifier = methodExpression.getQualifier();
+            if (qualifier == null) {
+                // fixme for when the array is added to 'this'
+                return null;
+            }
+            return qualifier.getText();
+        }
+
         @Nullable
-        private static String getStartOffsetText(PsiExpression expression,
+        private static String buildFromOffsetText(PsiExpression expression,
                                                  PsiLocalVariable variable)
                 throws IncorrectOperationException {
-            expression =
-                    PsiUtil.deparenthesizeExpression(expression);
+            expression = ParenthesesUtils.stripParentheses(expression);
             if (expression == null) {
                 return null;
             }
@@ -247,7 +311,7 @@ public class ManualArrayToCollectionCopyInspection
                         (PsiBinaryExpression)expression;
                 final PsiExpression lhs = binaryExpression.getLOperand();
                 final PsiExpression rhs = binaryExpression.getROperand();
-                final String rhsText = getStartOffsetText(rhs, variable);
+                final String rhsText = buildFromOffsetText(rhs, variable);
                 final PsiJavaToken sign = binaryExpression.getOperationSign();
                 final IElementType tokenType = sign.getTokenType();
                 if (ExpressionUtils.isZero(lhs)) {
@@ -256,7 +320,7 @@ public class ManualArrayToCollectionCopyInspection
                     }
                     return rhsText;
                 }
-                final String lhsText = getStartOffsetText(lhs, variable);
+                final String lhsText = buildFromOffsetText(lhs, variable);
                 if (ExpressionUtils.isZero(rhs)) {
                     return lhsText;
                 }
@@ -266,6 +330,36 @@ public class ManualArrayToCollectionCopyInspection
             return collapseConstant(expression.getText(), variable);
         }
 
+        private static String buildToOffsetText(PsiExpression expression,
+                                                boolean plusOne) {
+            expression = ParenthesesUtils.stripParentheses(expression);
+            if (expression == null) {
+                return null;
+            }
+            if (!plusOne) {
+                return expression.getText();
+            }
+            if (expression instanceof PsiBinaryExpression) {
+                final PsiBinaryExpression binaryExpression =
+                        (PsiBinaryExpression) expression;
+                final IElementType tokenType =
+                        binaryExpression.getOperationTokenType();
+                if (tokenType == JavaTokenType.MINUS)  {
+                    final PsiExpression rhs =
+                            binaryExpression.getROperand();
+                    if (ExpressionUtils.isOne(rhs)) {
+                        return binaryExpression.getLOperand().getText();
+                    }
+                }
+            }
+            final int precedence = ParenthesesUtils.getPrecedence(expression);
+            if (precedence > ParenthesesUtils.ADDITIVE_PRECEDENCE) {
+                return '(' + expression.getText() + ")+1";
+            } else {
+                return expression.getText() + "+1";
+            }
+        }
+
         private static String collapseConstant(String expressionText,
                                                PsiElement context)
                 throws IncorrectOperationException {
@@ -285,8 +379,7 @@ public class ManualArrayToCollectionCopyInspection
         }
 
         @Nullable
-        private static PsiExpressionStatement getBody(
-                PsiLoopStatement forStatement) {
+        private static PsiStatement getBody(PsiLoopStatement forStatement) {
             PsiStatement body = forStatement.getBody();
             while (body instanceof PsiBlockStatement) {
                 final PsiBlockStatement blockStatement =
@@ -295,7 +388,7 @@ public class ManualArrayToCollectionCopyInspection
                 final PsiStatement[] statements = codeBlock.getStatements();
                 body = statements[0];
             }
-            return (PsiExpressionStatement)body;
+            return body;
         }
     }
 
@@ -384,9 +477,36 @@ public class ManualArrayToCollectionCopyInspection
                         (PsiBlockStatement)body;
                 final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
                 final PsiStatement[] statements = codeBlock.getStatements();
-                return statements.length == 1 &&
-                        bodyIsArrayToCollectionCopy(statements[0], variable,
-                                shouldBeOffsetArrayAccess);
+                if (statements.length == 1) {
+                    return bodyIsArrayToCollectionCopy(statements[0], variable,
+                            shouldBeOffsetArrayAccess);
+                } else if (statements.length == 2) {
+                    final PsiStatement statement = statements[0];
+                    if (!(statement instanceof PsiDeclarationStatement)) {
+                        return false;
+                    }
+                    final PsiDeclarationStatement declarationStatement =
+                            (PsiDeclarationStatement) statement;
+                    final PsiElement[] declaredElements =
+                            declarationStatement.getDeclaredElements();
+                    if (declaredElements.length != 1) {
+                        return false;
+                    }
+                    final PsiElement declaredElement = declaredElements[0];
+                    if (!(declaredElement instanceof PsiVariable)) {
+                        return false;
+                    }
+                    final PsiVariable localVariable =
+                            (PsiVariable) declaredElement;
+                    final PsiExpression initializer =
+                            localVariable.getInitializer();
+                    if (!ExpressionUtils.isOffsetArrayAccess(initializer,
+                            variable)) {
+                        return false;
+                    }
+                    return bodyIsArrayToCollectionCopy(statements[1],
+                            localVariable, false);
+                }
             }
             return false;
         }
@@ -394,8 +514,7 @@ public class ManualArrayToCollectionCopyInspection
         private static boolean expressionIsArrayToCollectionCopy(
                 PsiExpression expression, PsiVariable variable,
                 boolean shouldBeOffsetArrayAccess) {
-            expression =
-                    PsiUtil.deparenthesizeExpression(expression);
+            expression = ParenthesesUtils.stripParentheses(expression);
             if (expression == null) {
                 return false;
             }
index 4a2e6bc5ae6e97b6e461f6dacb40dd4b81601b87..ebe4bf0f425c533d613572ffe482675d01b34e3c 100644 (file)
@@ -104,9 +104,9 @@ public class ExpressionUtils {
         if (expression instanceof PsiParenthesizedExpression) {
             final PsiParenthesizedExpression parenthesizedExpression =
                     (PsiParenthesizedExpression)expression;
-            final PsiExpression unparenthesizedExpression =
+            final PsiExpression deparenthesizedExpression =
                     parenthesizedExpression.getExpression();
-            return isEvaluatedAtCompileTime(unparenthesizedExpression);
+            return isEvaluatedAtCompileTime(deparenthesizedExpression);
         }
         if (expression instanceof PsiConditionalExpression) {
             final PsiConditionalExpression conditionalExpression =
@@ -309,11 +309,13 @@ public class ExpressionUtils {
                 (PsiBinaryExpression)expression;
         final PsiJavaToken sign = binaryExpression.getOperationSign();
         final IElementType tokenType = sign.getTokenType();
-        if (tokenType.equals(JavaTokenType.LT)) {
+        if (tokenType.equals(JavaTokenType.LT) ||
+                tokenType.equals(JavaTokenType.LE)) {
             PsiExpression lhs = binaryExpression.getLOperand();
             lhs = ParenthesesUtils.stripParentheses(lhs);
             return VariableAccessUtils.evaluatesToVariable(lhs, variable);
-        } else if (tokenType.equals(JavaTokenType.GT)) {
+        } else if (tokenType.equals(JavaTokenType.GT) ||
+                tokenType.equals(JavaTokenType.GE)) {
             PsiExpression rhs = binaryExpression.getROperand();
             rhs = ParenthesesUtils.stripParentheses(rhs);
             return VariableAccessUtils.evaluatesToVariable(rhs, variable);
index 34810e44b7552b5ccecf430cad2b639faccb6744..2843ac934fbfebbe381387463da48b90a3de97ab 100644 (file)
@@ -80,4 +80,14 @@ public class ManualArrayCopy
         for (int i = 0; i < sp; i++)
             varcov[i][i] = eval[i + kp];
     }
+
+    void isntThatCute() {
+        String[] data = new String[100];
+        int index = 10;
+        for (int i = data.length - 1; i >= 0; i--)
+        {
+            data[i] = data[i - 1];
+        }
+
+    }
 }