Project
IntelliJ IDEA
Priority
Normal
Type
Bug
Fix versions
No Fix versions
State
Duplicate
Assignee
Alexey Kudravtsev
Subsystem
Code Analysis. Inspection
Affected versions
No Affected versions
Fixed in build
No Fixed in build
  • Created by   Gibson
    4 years ago (20 Feb 2007 19:03)
  • Updated by   root
    2 years ago (17 Jan 2010 20:42)
  • Jira: IDEADEV-16164
    (history, comments)
 
IDEA-37261 "Suspicious collections method calls" inspection is a bit over-zealous
0
Issue is visible to: All Users
  The issue is visible to the selected user group only
It shouldn't (at least in option, if not by default) warn about code like this:
    Map<Integer,String> map = getMap();
    Number n = new Integer(3);
    if (map.containsKey(n)) {
        ...
    } 
That is to say, no warning should be emitted if the type given is a supertype of the desired type. I suggest that this be the default behaviour, perhaps with a checkbox to give the current behaviour if desired.

Issue was resolved
Comments (9)
 
History
 
Linked Issues (?)
 
Eugene Vigdorchik
  Eugene Vigdorchik
20 Feb 2007 19:42
4 years ago
That would make the whole inspection useless for e.g. retrieving Objects.
Gibson
  Gibson
20 Feb 2007 20:10
4 years ago
That's why I suggest it maybe should be an option. Note that I'm suggesting that you tighten the rules a little to only warn if it's impossible for the call to ever succeed: if there is a possibility it might succeed, no warning should be emitted (or, as an option, also emit a warning in this case).

We have lots of code like this:
    public boolean accept(Map<Integer,String> map, Object key) {
        return map.containsKey(key);
    } 
Here I don't want Idea to warn me that the runtime type of "key" might not give any results: because the runtime type might be Integer. And I don't want to cast to Integer because I want my code to return false if a Double is passed in.

On the other hand, I do want to see a big warning in this case:
    public boolean accept(Map<Integer,String> map, String key) {
        return map.containsKey(key);
    } 
Gibson
  Gibson
21 Feb 2007 16:17
4 years ago
Just one quick last point: consider the consistency of this code:
Integer i = Integer.valueOf (0); 
Object o = i;
String s = "Test";
List<Integer> l = new ArrayList<Integer> ();
// No warning - good
if (o.equals (i)) { }
if (i.equals (o)) { }
// Warning "Suspicious call to List.contains()" - not good
if (l.contains (o)) { }
// Warning "equals between objects of inconvertible types" - good
if (s.equals (i)) { }
if (i.equals (s)) { }
// Warning "Suspicious call to List.contains()" - good
if (l.contains (s)) { }
Doesn't that feel a little bit wrong to you? Surely the various inspections of this type (that is, "Suspicious 'Collections.toArray()' call", "Suspicious 'System.arraycopy()' call", "Suspicious collections method calls" and "'equals()' between objects of inconvertible types" should be internally coherent (and not just the ones that are "Powered by InspectionGadgets") ;)
Gibson
  Gibson
21 Feb 2007 17:09
4 years ago
Also see IDEA-11410
AlexL
  AlexL
11 Mar 2007 06:44
4 years ago
At least for your original example, it looks suspicious to me, unless IDEA can use
dataflow analysis to make sure that the variable n is only ever assigned to an Integer.

Why do you not have Map<Number,String> or else if you want Map<Integer,String>
then why don't you have
Map<Integer,String> map = getMap();
    Integer n = new Integer(3);
    if (map.containsKey(n)) {
        ...
    }

Gibson
  Gibson
12 Mar 2007 12:34
4 years ago
I don't want to be warned when the code is "suspicious", just "wrong". Using an Object to retrieve from a map that takes Integer keys could be considered "suspicious" but for me it gives too many false positives to be useful. On the other hand, using a String to retrieve from a map that takes Integer keys is valid Java but is always going to be a bug.
Eugene Vigdorchik
  Eugene Vigdorchik
12 Apr 2007 00:26
4 years ago
This would render the inspection useless for the cases where either type parameter or argument of the method have interface type. I would still suggest you introduce the variable of concrete type, and the inspection for single-used local variable be patched to account for this case.
Gibson
  Gibson
13 Apr 2007 13:22
4 years ago
Eugene - good point about interface types, although I don't have that specific use case in my code. Maybe the inspection should always warn about this case (?).

I can't really see a nice general solution to this. For example, you suggest transforming this
public boolean accept(Map<Integer,String> map, Object key) {
    return map.containsKey(key);
}

into this
public boolean accept(Map<Integer,String> map, Object key) {
    if (key instanceof Integer)
        // Whoops, I get an unnecessary cast warning here :-/
        return map.containsKey((Integer) key);
    else
        return false;
}
or maybe this
public boolean accept (Map<Integer, String> map, Object key)
{
	if (key instanceof Integer)
	{
		Integer ikey = (Integer) key;
		return map.containsKey (ikey);
	} else
	{
		return false;
	}
}
which are both kind of ugly and much more verbose than the original; and it doesn't work with generics, for example, in this contrived example
public boolean accept(Map<Class<Integer>,String> map, Class<?> key) {
    if (key instanceof Class<Integer>)
        return map.containsKey((Integer) key);
    else
        return false;
}
Alexey Kudravtsev
  Alexey Kudravtsev
11 May 2007 18:47
4 years ago