IDEA-132897 (False positive on inspection "mismatched query and update of collection" when using BlockingQueue.drainTo)

This commit is contained in:
Bas Leijdekkers
2015-05-06 13:13:58 +02:00
parent 8d1233d209
commit 8d638087bf
6 changed files with 78 additions and 28 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2015 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.
@@ -17,8 +17,10 @@ package com.siyeh.ig.bugs;
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.intellij.util.Processor;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
@@ -236,7 +238,7 @@ public class MismatchedCollectionQueryUpdateInspectionBase extends BaseInspectio
}
private boolean collectionContentsAreUpdated(PsiVariable variable, PsiElement context) {
if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, UPDATE_EXCLUDES, context) ||
if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, context, new UpdateCallProcessor()) ||
collectionUpdateCalled(variable, context)) {
return true;
}
@@ -262,7 +264,7 @@ public class MismatchedCollectionQueryUpdateInspectionBase extends BaseInspectio
}
private boolean collectionContentsAreQueried(PsiVariable variable, PsiElement context) {
if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, QUERY_EXCLUDES, context) ||
if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, context, new QueryCallProcessor()) ||
collectionQueryCalled(variable, context)) {
return true;
}
@@ -285,4 +287,31 @@ public class MismatchedCollectionQueryUpdateInspectionBase extends BaseInspectio
return visitor.isQueriedUpdated();
}
}
private static class QueryCallProcessor implements Processor<PsiCall> {
@Override
public boolean process(PsiCall call) {
final PsiMethod method = call.resolveMethod();
if (method == null) {
return false;
}
final PsiClass aClass = method.getContainingClass();
return aClass != null && QUERY_EXCLUDES.contains(aClass.getQualifiedName());
}
}
private static class UpdateCallProcessor implements Processor<PsiCall> {
@Override
public boolean process(PsiCall call) {
final PsiMethod method = call.resolveMethod();
if (method == null) {
return false;
}
final PsiClass aClass = method.getContainingClass();
if (aClass == null || !UPDATE_EXCLUDES.contains(aClass.getQualifiedName())) {
return false;
}
return !"drainTo".equals(method.getName()) || !InheritanceUtil.isInheritor(aClass, "java.util.concurrent.BlockingQueue");
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2014 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2015 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.
@@ -20,6 +20,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
@@ -205,10 +206,20 @@ public class StringBufferReplaceableByStringBuilderInspection extends BaseInspec
if (VariableAccessUtils.variableIsReturned(variable, context, true)) {
return false;
}
if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, excludes, context, true)) {
if (VariableAccessUtils.variableIsUsedInInnerClass(variable, context)) {
return false;
}
if (VariableAccessUtils.variableIsUsedInInnerClass(variable, context)) {
if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, context, true, new Processor<PsiCall>() {
@Override
public boolean process(PsiCall call) {
final PsiMethod method = call.resolveMethod();
if (method == null) {
return false;
}
final PsiClass aClass = method.getContainingClass();
return aClass != null && excludes.contains(aClass.getQualifiedName());
}
})) {
return false;
}
return true;
@@ -17,6 +17,7 @@ package com.siyeh.ig.psiutils;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -51,16 +52,18 @@ public class VariableAccessUtils {
return visitor.isPassed();
}
public static boolean variableIsPassedAsMethodArgument(@NotNull PsiVariable variable, Set<String> excludes, @Nullable PsiElement context) {
return variableIsPassedAsMethodArgument(variable, excludes, context, false);
public static boolean variableIsPassedAsMethodArgument(@NotNull PsiVariable variable, @Nullable PsiElement context,
Processor<PsiCall> callProcessor) {
return variableIsPassedAsMethodArgument(variable, context, false, callProcessor);
}
public static boolean variableIsPassedAsMethodArgument(@NotNull PsiVariable variable, Set<String> excludes,
@Nullable PsiElement context, boolean builderPattern) {
public static boolean variableIsPassedAsMethodArgument(@NotNull PsiVariable variable, @Nullable PsiElement context,
boolean builderPattern, Processor<PsiCall> callProcessor) {
if (context == null) {
return false;
}
final VariablePassedAsArgumentExcludedVisitor visitor = new VariablePassedAsArgumentExcludedVisitor(variable, excludes, builderPattern);
final VariablePassedAsArgumentExcludedVisitor visitor =
new VariablePassedAsArgumentExcludedVisitor(variable, builderPattern, callProcessor);
context.accept(visitor);
return visitor.isPassed();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2014 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2015 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.
@@ -16,22 +16,22 @@
package com.siyeh.ig.psiutils;
import com.intellij.psi.*;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
class VariablePassedAsArgumentExcludedVisitor extends JavaRecursiveElementVisitor {
@NotNull
private final PsiVariable variable;
private final Set<String> excludes;
private final Processor<PsiCall> myCallProcessor;
private final boolean myBuilderPattern;
private boolean passed = false;
public VariablePassedAsArgumentExcludedVisitor(@NotNull PsiVariable variable, @NotNull Set<String> excludes, boolean builderPattern) {
public VariablePassedAsArgumentExcludedVisitor(@NotNull PsiVariable variable, boolean builderPattern,
@NotNull Processor<PsiCall> callProcessor) {
this.variable = variable;
this.excludes = excludes;
myCallProcessor = callProcessor;
myBuilderPattern = builderPattern;
}
@@ -70,17 +70,10 @@ class VariablePassedAsArgumentExcludedVisitor extends JavaRecursiveElementVisito
if (!VariableAccessUtils.mayEvaluateToVariable(argument, variable, myBuilderPattern)) {
continue;
}
final PsiMethod method = call.resolveMethod();
if (method != null) {
final PsiClass aClass = method.getContainingClass();
if (aClass != null) {
final String name = aClass.getQualifiedName();
if (excludes.contains(name)) {
continue;
}
}
if (!myCallProcessor.process(call)) {
passed = true;
break;
}
passed = true;
}
}
@@ -2,6 +2,7 @@ package com.siyeh.igtest.bugs.mismatched_collection_query_update;
import java.util.*;
import java.io.FileInputStream;
import java.util.concurrent.BlockingQueue;
public class MismatchedCollectionQueryUpdate {
private Set foo = new HashSet();
@@ -268,6 +269,15 @@ class CollectionsUser {
interface Supplier<T> {
T get();
}
void draining(BlockingQueue<Object> queue) {
List<Object> objects = new ArrayList<>();
queue.drainTo(objects);
// ...
for (Object obj : objects) {
// ...
}
}
}
class SimpleAdd {
@@ -25,7 +25,11 @@ public class MismatchedCollectionQueryUpdateInspectionTest extends LightInspecti
"package java.util.concurrent;" +
"public class LinkedBlockingDeque<E> implements BlockingDeque {}",
"package java.lang;" +
"public class InterruptedException extends Exception {}"
"public class InterruptedException extends Exception {}",
"package java.util.concurrent;" +
"public interface BlockingQueue<E> {" +
" int drainTo(java.util.Collection<? super E> c);" +
"}"
};
}