restore classes merge with fix of foreach processing

This commit is contained in:
Anna Kozlova
2014-05-31 01:02:05 +04:00
parent fedbba7f92
commit 248d75225f
3 changed files with 138 additions and 282 deletions
@@ -22,123 +22,45 @@ import com.siyeh.ig.psiutils.ExpectedTypeUtils;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
class CollectionQueryCalledVisitor extends JavaRecursiveElementVisitor {
class CollectionQueryUpdateCalledVisitor extends JavaRecursiveElementVisitor {
private static final HashSet<String> COLLECTIONS_QUERIES =
ContainerUtil.newHashSet("binarySearch", "disjoint", "frequency", "indexOfSubList", "lastIndexOfSubList", "max", "min", "nCopies",
"unmodifiableList", "unmodifiableMap", "unmodifiableNavigableMap", "unmodifiableNavigableSet",
"unmodifiableSet", "unmodifiableSortedMap", "unmodifiableSortedSet");
static final HashSet<String> COLLECTIONS_TRANSFORMS =
private static final HashSet<String> COLLECTIONS_TRANSFORMS =
ContainerUtil.newHashSet("asLifoQueue", "checkedCollection", "checkedList", "checkedMap", "checkedNavigableMap", "checkedNavigableSet",
"checkedQueue", "checkedSet", "checkedSortedMap", "checkedSortedSet", "enumeration", "newSetFromMap",
"synchronizedCollection", "singleton", "singletonList", "singletonMap", "singletonSpliterator",
"synchronizedList", "synchronizedMap", "synchronizedNavigableMap", "synchronizedNavigableSet",
"synchronizedSet", "synchronizedSortedMap", "synchronizedSortedSet", "unmodifiableCollection");
@NonNls private final Set<String> myQueryUpdateNames;
private final boolean myCheckForQuery;
@NonNls private final Set<String> queryNames;
private boolean queried = false;
private boolean myQueriedUpdated = false;
private final PsiVariable variable;
CollectionQueryCalledVisitor(PsiVariable variable, Set<String> queryNames) {
CollectionQueryUpdateCalledVisitor(@Nullable PsiVariable variable, Set<String> queryUpdateNames, boolean checkForQuery) {
this.variable = variable;
this.queryNames = queryNames;
myQueryUpdateNames = queryUpdateNames;
myCheckForQuery = checkForQuery;
}
@Override
public void visitElement(@NotNull PsiElement element) {
if (!queried) {
if (!myQueriedUpdated) {
super.visitElement(element);
}
}
@Override
public void visitForeachStatement(@NotNull PsiForeachStatement statement) {
if (queried) {
return;
}
super.visitForeachStatement(statement);
final PsiExpression qualifier = statement.getIteratedValue();
checkExpression(qualifier);
}
@Override
public void visitMethodCallExpression(
@NotNull PsiMethodCallExpression call) {
if (queried) {
return;
}
super.visitMethodCallExpression(call);
final PsiReferenceExpression methodExpression =
call.getMethodExpression();
final boolean isStatement = call.getParent() instanceof PsiExpressionStatement;
if (isStatement && !isQueryMethodName(methodExpression.getReferenceName())) {
return;
}
final PsiExpression qualifier = methodExpression.getQualifierExpression();
checkExpression(qualifier);
}
@Override
public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
if (queried) return;
final String methodName = expression.getReferenceName();
if (!isQueryMethodName(methodName)) {
final PsiElement target = expression.resolve();
if (!(target instanceof PsiMethod)) {
return;
}
final PsiMethod method = (PsiMethod)target;
final PsiType returnType = method.getReturnType();
if (PsiType.VOID.equals(returnType)) {
return;
}
final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false);
if (!(expectedType instanceof PsiClassType)) {
return;
}
final PsiClassType classType = (PsiClassType)expectedType;
final PsiClass aClass = classType.resolve();
if (aClass == null || LambdaHighlightingUtil.checkInterfaceFunctional(aClass) != null) {
return;
}
final List<HierarchicalMethodSignature> candidates = LambdaUtil.findFunctionCandidates(aClass);
if (candidates == null || candidates.size() != 1) {
return;
}
final HierarchicalMethodSignature signature = candidates.get(0);
final PsiMethod functionalMethod = signature.getMethod();
if (PsiType.VOID.equals(functionalMethod.getReturnType())) {
return;
}
}
checkExpression(expression.getQualifierExpression());
}
private boolean isQueryMethodName(String methodName) {
if (methodName == null) {
return false;
}
if (queryNames.contains(methodName)) {
return true;
}
for (String queryName : queryNames) {
if (methodName.startsWith(queryName)) {
return true;
}
}
return false;
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
@@ -154,19 +76,36 @@ class CollectionQueryCalledVisitor extends JavaRecursiveElementVisitor {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent;
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
final String name = methodExpression.getReferenceName();
if (COLLECTIONS_QUERIES.contains(name) || COLLECTIONS_TRANSFORMS.contains(name)) {
if (methodCallExpression.getParent() instanceof PsiExpressionStatement) {
return;
if (myCheckForQuery) {
if (COLLECTIONS_QUERIES.contains(name) || COLLECTIONS_TRANSFORMS.contains(name)) {
if (methodCallExpression.getParent() instanceof PsiExpressionStatement) {
return;
}
}
}
else if ("addAll".equals(name) || "copy".equals(name) || "fill".equals(name) || "replaceAll".equals(name)) {
final PsiExpression[] arguments = expressionList.getExpressions();
if (arguments.length < 2 || PsiTreeUtil.isAncestor(arguments[0], expression, false)) {
else if ("addAll".equals(name) || "copy".equals(name) || "fill".equals(name) || "replaceAll".equals(name)) {
final PsiExpression[] arguments = expressionList.getExpressions();
if (arguments.length < 2 || PsiTreeUtil.isAncestor(arguments[0], expression, false)) {
return;
}
}
else {
return;
}
}
else {
return;
if ("addAll".equals(name) || "fill".equals(name) || "copy".equals(name) || "replaceAll".equals(name)) {
if (!PsiTreeUtil.isAncestor(expressionList.getExpressions()[0], expression, false)) {
return;
}
}
else if (COLLECTIONS_TRANSFORMS.contains(name)) {
if (methodCallExpression.getParent() instanceof PsiExpressionStatement) {
return;
}
}
else {
return;
}
}
final PsiMethod method = methodCallExpression.resolveMethod();
if (method == null) {
@@ -183,11 +122,96 @@ class CollectionQueryCalledVisitor extends JavaRecursiveElementVisitor {
checkExpression(expression);
}
private void checkExpression(PsiExpression expression) {
if (queried) {
@Override
public void visitForeachStatement(@NotNull PsiForeachStatement statement) {
super.visitForeachStatement(statement);
if (myQueriedUpdated || !myCheckForQuery) {
return;
}
if (expression instanceof PsiReferenceExpression) {
final PsiExpression qualifier = statement.getIteratedValue();
checkExpression(qualifier);
}
@Override
public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
super.visitMethodReferenceExpression(expression);
if (myQueriedUpdated) {
return;
}
final String methodName = expression.getReferenceName();
if (!isQueryUpdateMethodName(methodName)) {
if (myCheckForQuery) {
final PsiElement target = expression.resolve();
if (!(target instanceof PsiMethod)) {
return;
}
final PsiMethod method = (PsiMethod)target;
final PsiType returnType = method.getReturnType();
if (PsiType.VOID.equals(returnType)) {
return;
}
final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false);
if (!(expectedType instanceof PsiClassType)) {
return;
}
final PsiClassType classType = (PsiClassType)expectedType;
final PsiClass aClass = classType.resolve();
if (aClass == null || LambdaHighlightingUtil.checkInterfaceFunctional(aClass) != null) {
return;
}
final List<HierarchicalMethodSignature> candidates = LambdaUtil.findFunctionCandidates(aClass);
if (candidates == null || candidates.size() != 1) {
return;
}
final HierarchicalMethodSignature signature = candidates.get(0);
final PsiMethod functionalMethod = signature.getMethod();
if (PsiType.VOID.equals(functionalMethod.getReturnType())) {
return;
}
}
else {
return;
}
}
checkExpression(expression.getQualifierExpression());
}
@Override
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression call) {
if (myQueriedUpdated) {
return;
}
super.visitMethodCallExpression(call);
final PsiReferenceExpression methodExpression =
call.getMethodExpression();
final boolean isStatement = call.getParent() instanceof PsiExpressionStatement;
if ((!myCheckForQuery || isStatement) && !isQueryUpdateMethodName(methodExpression.getReferenceName())) {
return;
}
final PsiExpression qualifier = methodExpression.getQualifierExpression();
checkExpression(qualifier);
}
private boolean isQueryUpdateMethodName(String methodName) {
if (methodName == null) {
return false;
}
if (myQueryUpdateNames.contains(methodName)) {
return true;
}
for (String updateName : myQueryUpdateNames) {
if (methodName.startsWith(updateName)) {
return true;
}
}
return false;
}
private void checkExpression(PsiExpression expression) {
if (myQueriedUpdated) {
return;
}
if (variable != null && expression instanceof PsiReferenceExpression) {
final PsiReferenceExpression referenceExpression =
(PsiReferenceExpression)expression;
final PsiElement referent = referenceExpression.resolve();
@@ -195,7 +219,7 @@ class CollectionQueryCalledVisitor extends JavaRecursiveElementVisitor {
return;
}
if (referent.equals(variable)) {
queried = true;
myQueriedUpdated = true;
}
}
else if (expression instanceof PsiParenthesizedExpression) {
@@ -209,12 +233,18 @@ class CollectionQueryCalledVisitor extends JavaRecursiveElementVisitor {
final PsiExpression thenExpression =
conditionalExpression.getThenExpression();
checkExpression(thenExpression);
final PsiExpression elseExpression = conditionalExpression.getElseExpression();
final PsiExpression elseExpression =
conditionalExpression.getElseExpression();
checkExpression(elseExpression);
}
else if (variable == null) {
if (expression == null || expression instanceof PsiThisExpression || expression instanceof PsiSuperExpression) {
myQueriedUpdated = true;
}
}
}
public boolean isQueried() {
return queried;
public boolean isQueriedUpdated() {
return myQueriedUpdated;
}
}
@@ -1,174 +0,0 @@
/*
* Copyright 2003-2011 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.
* 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.bugs;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
class CollectionUpdateCalledVisitor extends JavaRecursiveElementVisitor {
@NonNls private final Set<String> updateNames;
private boolean updated = false;
private final PsiVariable variable;
CollectionUpdateCalledVisitor(@Nullable PsiVariable variable, Set<String> updateNames) {
this.variable = variable;
this.updateNames = updateNames;
}
@Override
public void visitElement(@NotNull PsiElement element) {
if (!updated) {
super.visitElement(element);
}
}
@Override
public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
super.visitMethodReferenceExpression(expression);
if (updated) return;
final String methodName = expression.getReferenceName();
if (checkMethodName(methodName)) return;
checkQualifier(expression.getQualifierExpression());
}
@Override
public void visitMethodCallExpression(
@NotNull PsiMethodCallExpression call) {
super.visitMethodCallExpression(call);
if (updated) {
return;
}
final PsiReferenceExpression methodExpression =
call.getMethodExpression();
final String methodName = methodExpression.getReferenceName();
if (checkMethodName(methodName)) return;
final PsiExpression qualifier = methodExpression.getQualifierExpression();
checkQualifier(qualifier);
}
private boolean checkMethodName(String methodName) {
if (methodName == null) {
return true;
}
if (!updateNames.contains(methodName)) {
boolean found = false;
for (String updateName : updateNames) {
if (!methodName.startsWith(updateName)) {
continue;
}
found = true;
break;
}
if (!found) {
return true;
}
}
return false;
}
private void checkQualifier(PsiExpression expression) {
if (updated) {
return;
}
if (variable != null && expression instanceof PsiReferenceExpression) {
final PsiReferenceExpression referenceExpression =
(PsiReferenceExpression)expression;
final PsiElement referent = referenceExpression.resolve();
if (referent == null) {
return;
}
if (referent.equals(variable)) {
updated = true;
}
}
else if (expression instanceof PsiParenthesizedExpression) {
final PsiParenthesizedExpression parenthesizedExpression =
(PsiParenthesizedExpression)expression;
checkQualifier(parenthesizedExpression.getExpression());
}
else if (expression instanceof PsiConditionalExpression) {
final PsiConditionalExpression conditionalExpression =
(PsiConditionalExpression)expression;
final PsiExpression thenExpression =
conditionalExpression.getThenExpression();
checkQualifier(thenExpression);
final PsiExpression elseExpression =
conditionalExpression.getElseExpression();
checkQualifier(elseExpression);
}
else if (variable == null) {
if (expression == null || expression instanceof PsiThisExpression || expression instanceof PsiSuperExpression) {
updated = true;
}
}
}
public boolean isUpdated() {
return updated;
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
final PsiElement parent = ParenthesesUtils.getParentSkipParentheses(expression);
if (!(parent instanceof PsiExpressionList)) {
return;
}
final PsiExpressionList expressionList = (PsiExpressionList)parent;
final PsiElement grandParent = expressionList.getParent();
if (!(grandParent instanceof PsiMethodCallExpression)) {
return;
}
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent;
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
final String name = methodExpression.getReferenceName();
if ("addAll".equals(name) || "fill".equals(name) || "copy".equals(name) || "replaceAll".equals(name)) {
if (!PsiTreeUtil.isAncestor(expressionList.getExpressions()[0], expression, false)) {
return;
}
}
else if (CollectionQueryCalledVisitor.COLLECTIONS_TRANSFORMS.contains(name)) {
if (methodCallExpression.getParent() instanceof PsiExpressionStatement) {
return;
}
}
else {
return;
}
final PsiMethod method = methodCallExpression.resolveMethod();
if (method == null) {
return;
}
final PsiClass aClass = method.getContainingClass();
if (aClass == null) {
return;
}
final String qualifiedName = aClass.getQualifiedName();
if (!"java.util.Collections".equals(qualifiedName)) {
return;
}
checkQualifier(expression);
}
}
@@ -273,16 +273,16 @@ public class MismatchedCollectionQueryUpdateInspectionBase extends BaseInspectio
return collectionQueriedByAssignment(variable, context);
}
private boolean collectionQueryCalled(PsiVariable variable, PsiElement context) {
final CollectionQueryCalledVisitor visitor = new CollectionQueryCalledVisitor(variable, queryNames);
private boolean collectionQueryCalled(PsiVariable variable, PsiElement context) {
final CollectionQueryUpdateCalledVisitor visitor = new CollectionQueryUpdateCalledVisitor(variable, queryNames, true);
context.accept(visitor);
return visitor.isQueried();
return visitor.isQueriedUpdated();
}
private boolean collectionUpdateCalled(@Nullable PsiVariable variable, PsiElement context) {
final CollectionUpdateCalledVisitor visitor = new CollectionUpdateCalledVisitor(variable, updateNames);
final CollectionQueryUpdateCalledVisitor visitor = new CollectionQueryUpdateCalledVisitor(variable, updateNames, false);
context.accept(visitor);
return visitor.isUpdated();
return visitor.isQueriedUpdated();
}
}
}