private 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; } 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 expressionIsArrayToCollectionCopy( PsiExpression expression, PsiVariable variable, boolean shouldBeOffsetArrayAccess) { expression = ParenthesesUtils.stripParentheses(expression); if (expression == null) { return false; } if (!(expression instanceof PsiMethodCallExpression)) { return false; } final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) expression; final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); final PsiExpression[] arguments = argumentList.getExpressions(); if (arguments.length != 1) { return false; } final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); final PsiExpression qualifier = methodExpression.getQualifierExpression(); if (!(qualifier instanceof PsiReferenceExpression) && !(qualifier instanceof PsiThisExpression) && !(qualifier instanceof PsiSuperExpression)) { return false; } final PsiExpression argument = arguments[0]; final PsiType argumentType = argument.getType(); if (argumentType instanceof PsiPrimitiveType) { return false; } if (SideEffectChecker.mayHaveSideEffects(argument)) { return false; } if (shouldBeOffsetArrayAccess) { if (!ExpressionUtils.isOffsetArrayAccess(argument, variable)) { return false; } } else if (!VariableAccessUtils.evaluatesToVariable(argument, variable)) { return false; } final PsiMethod method = methodCallExpression.resolveMethod(); if (method == null) { return false; } @NonNls final String name = method.getName(); if (!name.equals("add")) { return false; } final PsiClass containingClass = method.getContainingClass(); return InheritanceUtil.isInheritor(containingClass, CommonClassNames.JAVA_UTIL_COLLECTION); }
@Override public void visitForStatement(@NotNull PsiForStatement statement) { super.visitForStatement(statement); final PsiStatement initialization = statement.getInitialization(); if (!(initialization instanceof PsiDeclarationStatement)) { return; } final PsiDeclarationStatement declaration = (PsiDeclarationStatement) initialization; final PsiElement[] declaredElements = declaration.getDeclaredElements(); if (declaredElements.length != 1) { return; } final PsiElement declaredElement = declaredElements[0]; if (!(declaredElement instanceof PsiLocalVariable)) { return; } final PsiLocalVariable variable = (PsiLocalVariable) declaredElement; final PsiExpression initialValue = variable.getInitializer(); if (initialValue == null) { return; } final PsiExpression condition = statement.getCondition(); if (!ExpressionUtils.isVariableLessThanComparison(condition, variable)) { return; } final PsiStatement update = statement.getUpdate(); if (!VariableAccessUtils.variableIsIncremented(variable, update)) { return; } final PsiArrayAccessExpression arrayAccessExpression = getArrayAccessExpression(statement); if (arrayAccessExpression == null) { return; } final PsiExpression arrayExpression = arrayAccessExpression.getArrayExpression(); final PsiType type = arrayExpression.getType(); if (!(type instanceof PsiArrayType) || type.getDeepComponentType() instanceof PsiPrimitiveType) { return; } final PsiStatement body = statement.getBody(); if (!bodyIsArrayToCollectionCopy(body, variable, true)) { return; } registerStatementError(statement); }
@Nullable private String createArrayIterationText(@NotNull PsiForStatement forStatement) { final PsiExpression condition = forStatement.getCondition(); final PsiBinaryExpression strippedCondition = (PsiBinaryExpression) ParenthesesUtils.stripParentheses(condition); if (strippedCondition == null) { return null; } final PsiExpression lhs = ParenthesesUtils.stripParentheses(strippedCondition.getLOperand()); if (lhs == null) { return null; } final PsiExpression rhs = ParenthesesUtils.stripParentheses(strippedCondition.getROperand()); if (rhs == null) { return null; } final IElementType tokenType = strippedCondition.getOperationTokenType(); final PsiReferenceExpression arrayLengthExpression; final String indexName; if (tokenType.equals(JavaTokenType.LT)) { arrayLengthExpression = (PsiReferenceExpression) ParenthesesUtils.stripParentheses(rhs); indexName = lhs.getText(); } else if (tokenType.equals(JavaTokenType.GT)) { arrayLengthExpression = (PsiReferenceExpression) ParenthesesUtils.stripParentheses(lhs); indexName = rhs.getText(); } else { return null; } if (arrayLengthExpression == null) { return null; } PsiReferenceExpression arrayReference = (PsiReferenceExpression) arrayLengthExpression.getQualifierExpression(); if (arrayReference == null) { final PsiElement target = arrayLengthExpression.resolve(); if (!(target instanceof PsiVariable)) { return null; } final PsiVariable variable = (PsiVariable) target; final PsiExpression initializer = variable.getInitializer(); if (!(initializer instanceof PsiReferenceExpression)) { return null; } final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) initializer; arrayReference = (PsiReferenceExpression) referenceExpression.getQualifierExpression(); if (arrayReference == null) { return null; } } final PsiType type = arrayReference.getType(); if (!(type instanceof PsiArrayType)) { return null; } final PsiArrayType arrayType = (PsiArrayType) type; final PsiType componentType = arrayType.getComponentType(); final String typeText = componentType.getCanonicalText(); final PsiElement target = arrayReference.resolve(); if (!(target instanceof PsiVariable)) { return null; } final PsiVariable arrayVariable = (PsiVariable) target; final PsiStatement body = forStatement.getBody(); final PsiStatement firstStatement = getFirstStatement(body); final boolean isDeclaration = isArrayElementDeclaration(firstStatement, arrayVariable, indexName); final String contentVariableName; @NonNls final String finalString; final PsiStatement statementToSkip; if (isDeclaration) { final PsiDeclarationStatement declarationStatement = (PsiDeclarationStatement) firstStatement; assert declarationStatement != null; final PsiElement[] declaredElements = declarationStatement.getDeclaredElements(); final PsiElement declaredElement = declaredElements[0]; if (!(declaredElement instanceof PsiVariable)) { return null; } final PsiVariable variable = (PsiVariable) declaredElement; if (VariableAccessUtils.variableIsAssigned(variable, forStatement)) { final String collectionName = arrayReference.getReferenceName(); contentVariableName = createNewVariableName(forStatement, componentType, collectionName); final Project project = forStatement.getProject(); final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(project); if (codeStyleSettings.GENERATE_FINAL_LOCALS) { finalString = "final "; } else { finalString = ""; } statementToSkip = null; } else { contentVariableName = variable.getName(); statementToSkip = declarationStatement; if (variable.hasModifierProperty(PsiModifier.FINAL)) { finalString = "final "; } else { finalString = ""; } } } else { final String collectionName = arrayReference.getReferenceName(); contentVariableName = createNewVariableName(forStatement, componentType, collectionName); final Project project = forStatement.getProject(); final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(project); if (codeStyleSettings.GENERATE_FINAL_LOCALS) { finalString = "final "; } else { finalString = ""; } statementToSkip = null; } @NonNls final StringBuilder out = new StringBuilder(); out.append("for("); out.append(finalString); out.append(typeText); out.append(' '); out.append(contentVariableName); out.append(": "); final String arrayName = arrayReference.getText(); out.append(arrayName); out.append(')'); if (body != null) { replaceArrayAccess( body, contentVariableName, arrayVariable, indexName, statementToSkip, out); } return out.toString(); }
private boolean checkCondition( @Nullable PsiExpression condition, @NotNull PsiStatement context, List<PsiExpression> notUpdated) { if (condition == null) { return false; } if (PsiUtil.isConstantExpression(condition) || PsiKeyword.NULL.equals(condition.getText())) { return true; } if (condition instanceof PsiInstanceOfExpression) { final PsiInstanceOfExpression instanceOfExpression = (PsiInstanceOfExpression) condition; final PsiExpression operand = instanceOfExpression.getOperand(); return checkCondition(operand, context, notUpdated); } else if (condition instanceof PsiParenthesizedExpression) { // catch stuff like "while ((x)) { ... }" final PsiExpression expression = ((PsiParenthesizedExpression) condition).getExpression(); return checkCondition(expression, context, notUpdated); } else if (condition instanceof PsiPolyadicExpression) { // while (value != x) { ... } // while (value != (x + y)) { ... } // while (b1 && b2) { ... } final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression) condition; for (PsiExpression operand : polyadicExpression.getOperands()) { if (!checkCondition(operand, context, notUpdated)) { return false; } } return true; } else if (condition instanceof PsiReferenceExpression) { final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) condition; final PsiElement element = referenceExpression.resolve(); if (element instanceof PsiField) { final PsiField field = (PsiField) element; final PsiType type = field.getType(); if (field.hasModifierProperty(PsiModifier.FINAL) && type.getArrayDimensions() == 0) { if (field.hasModifierProperty(PsiModifier.STATIC)) { return true; } final PsiExpression qualifier = referenceExpression.getQualifierExpression(); if (qualifier == null) { return true; } else if (checkCondition(qualifier, context, notUpdated)) { return true; } } } else if (element instanceof PsiVariable) { final PsiVariable variable = (PsiVariable) element; if (variable.hasModifierProperty(PsiModifier.FINAL)) { // final variables cannot be updated, don't bother to // flag them return true; } else if (element instanceof PsiLocalVariable || element instanceof PsiParameter) { if (!VariableAccessUtils.variableIsAssigned(variable, context)) { notUpdated.add(referenceExpression); return true; } } } } else if (condition instanceof PsiPrefixExpression) { final PsiPrefixExpression prefixExpression = (PsiPrefixExpression) condition; final IElementType tokenType = prefixExpression.getOperationTokenType(); if (JavaTokenType.EXCL.equals(tokenType) || JavaTokenType.PLUS.equals(tokenType) || JavaTokenType.MINUS.equals(tokenType)) { final PsiExpression operand = prefixExpression.getOperand(); return checkCondition(operand, context, notUpdated); } } else if (condition instanceof PsiArrayAccessExpression) { // Actually the contents of the array could change nevertheless // if it is accessed through a different reference like this: // int[] local_ints = new int[]{1, 2}; // int[] other_ints = local_ints; // while (local_ints[0] > 0) { other_ints[0]--; } // // Keep this check? final PsiArrayAccessExpression accessExpression = (PsiArrayAccessExpression) condition; final PsiExpression indexExpression = accessExpression.getIndexExpression(); return checkCondition(indexExpression, context, notUpdated) && checkCondition(accessExpression.getArrayExpression(), context, notUpdated); } else if (condition instanceof PsiConditionalExpression) { final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression) condition; final PsiExpression thenExpression = conditionalExpression.getThenExpression(); final PsiExpression elseExpression = conditionalExpression.getElseExpression(); if (thenExpression == null || elseExpression == null) { return false; } return checkCondition(conditionalExpression.getCondition(), context, notUpdated) && checkCondition(thenExpression, context, notUpdated) && checkCondition(elseExpression, context, notUpdated); } else if (condition instanceof PsiThisExpression) { return true; } else if (condition instanceof PsiMethodCallExpression && !ignoreIterators) { final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) condition; if (!IteratorUtils.isCallToHasNext(methodCallExpression)) { return false; } final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); if (qualifierExpression instanceof PsiReferenceExpression) { final PsiReferenceExpression referenceExpression = (PsiReferenceExpression) qualifierExpression; final PsiElement element = referenceExpression.resolve(); if (!(element instanceof PsiVariable)) { return false; } final PsiVariable variable = (PsiVariable) element; if (!IteratorUtils.containsCallToScannerNext(context, variable, true)) { notUpdated.add(qualifierExpression); return true; } } else { if (!IteratorUtils.containsCallToScannerNext(context, null, true)) { notUpdated.add(methodCallExpression); return true; } } } return false; }