Bug 410218 - Optional warning for arguments of "unexpected" types to Map#get(Object), Collection#remove(Object) et al.
Summary: Optional warning for arguments of "unexpected" types to Map#get(Object), Coll...
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.3   Edit
Hardware: PC Windows 7
: P3 enhancement with 1 vote (vote)
Target Milestone: 4.7 M6   Edit
Assignee: Till Brychcy CLA
QA Contact: Stephan Herrmann CLA
URL:
Whiteboard:
Keywords:
: 358209 389014 415306 451645 (view as bug list)
Depends on:
Blocks: 461999
  Show dependency tree
 
Reported: 2013-06-07 12:50 EDT by Markus Keller CLA
Modified: 2020-04-05 16:55 EDT (History)
14 users (show)

See Also:


Attachments
draft (20.70 KB, patch)
2015-03-10 19:38 EDT, Stephan Herrmann CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Markus Keller CLA 2013-06-07 12:50:53 EDT
Add an optional compiler warning if arguments of "unexpected" types are passed to java.util.Map#get/remove(Object), Collection#contains/remove(Object), Collection#retain/remove/containsAll(Collection<?>), List#indexOf(Object), etc.

Example:

	Set<Short> set = new HashSet<>();
	short one = 1;
	set.add(one);

	set.remove("ONE"); // bad (wrong type)
	set.remove(1); // bad (tries to remove "Integer 1")
	System.out.println(set); // shows that the "Short 1" is still in!

	set.remove(one); // ok
	System.out.println(set);

The calls marked as "// bad" compile without error, although they often hide a programming error that would have been caught at compile time if these accessor methods would not accept all types, but just the type variable E (or K or Collection<? extends E> etc.).

Unfortunately, the affected accessor methods cannot be determined in a generic way, so we need an explicit list of special methods and their "expected" argument types. For method invocations of a special method, the compiler can then also check whether a variant with the "expected" signature would also be applicable. If it's not applicable, then a warning should be issued.


Some background:
http://stackoverflow.com/questions/857420/what-are-the-reasons-why-map-getobject-key-is-not-fully-generic
http://stackoverflow.com/questions/104799/why-arent-java-collections-remove-methods-generic
http://smallwig.blogspot.ch/2007/12/why-does-setcontains-take-object-not-e.html
Comment 1 Stephan Herrmann CLA 2013-06-07 14:41:55 EDT
I wholeheartedly agree.

I assigned the bug to me, but I won't start right away, so if s.o. else
has cycles to spare and is interested in this bug please let me know.
Comment 2 Markus Keller CLA 2013-06-11 09:07:41 EDT
*** Bug 358209 has been marked as a duplicate of this bug. ***
Comment 3 Markus Keller CLA 2013-06-11 09:08:31 EDT
*** Bug 389014 has been marked as a duplicate of this bug. ***
Comment 4 Srikanth Sankaran CLA 2014-04-08 04:54:51 EDT
Stephan, do you expect to work on this for Luna ? ISTM, this is best addressed
in the start of Mars ? If you agree, please retarget. Thanks.
Comment 5 Stephan Herrmann CLA 2014-04-12 09:53:46 EDT
Since an optional warning requires additional API it is too late already for 4.4, right?

(Other than that, implementation _might_ be straight forward and simple).
Comment 6 Stephan Herrmann CLA 2014-11-21 13:51:26 EST
Linking to bug 415306, which requests special/similar warnings for equals() of certain well known types.
Comment 7 Stephan Herrmann CLA 2015-03-08 18:54:36 EDT
I have a draft implementation in my workspace.

An example warning message would be:

----------
1. WARNING in X.java (at line 8)
	if (set.contains("ONE")) // bad
	                 ^^^^^
Discouraged invocation of method contains(Object), this argument should have type Short to match the declaring type Collection<Short>
----------


Here's another example I used for experiments:

void test(Map<? extends Number, Number> m) {
	if (m.containsKey("ONE")) // bad
		m.remove("ONE"); // bad
}

Recognizing this as bad is easy, too. Question is: what would a user have to write to avoid the new warning? Literally, the "expected" type would be a capture, a value of which is quite difficult to obtain.

My initial feeling was, that we may want to silence the warning if the mismatch between "expected" and actual type is only a matter of captures. OTOH, if those methods were declared using a type variable, the strict interpretation would be mandatory, of course. But users will not like the warning if it creates situations that are hard to get right and may still do the right thing.

What do you think: is it a relevant concern to avoid warnings involving captures?


More questions: 

What should be the default value for this new diagnostic? Warning? Ignore?

I think we want to make it suppressable with @SW, right? Which token? Should we introduce a new token? (Bug 389014 proposed "rawtypes" :-/).

Bug 358209 requests a distinction of potentially incompatible vs. incompatible. Should we? I don't have a strong opinion on this, but surfacing both levels as separate options in the UI feels like overkill to me.
Comment 8 Ben Davis CLA 2015-03-08 20:05:01 EDT
(In reply to Stephan Herrmann from comment #7)
> Here's another example I used for experiments:
> 
> void test(Map<? extends Number, Number> m) {
> 	if (m.containsKey("ONE")) // bad
> 		m.remove("ONE"); // bad
> }
> 
> Recognizing this as bad is easy, too. Question is: what would a user have to
> write to avoid the new warning? Literally, the "expected" type would be a
> capture, a value of which is quite difficult to obtain.

This particular example is obviously wrong because a String cannot ever be a Number. If you replaced "ONE" with 1, then it becomes more interesting, because the ? could be Integer for some invocations. Still, it seems unclean for a generic method to include specific functionality, so I wouldn't object to inflicting some SuppressWarnings pain on people who are doing this.

I'm curious, what are the rules about casting - would a compile-time cast from the capture type [or equivalent type variable] to Integer be accepted? What about the other way? This is relevant if the 'incompatible' vs 'potentially incompatible' distinction is preserved (see further down).

> My initial feeling was, that we may want to silence the warning if the
> mismatch between "expected" and actual type is only a matter of captures.
> OTOH, if those methods were declared using a type variable, the strict
> interpretation would be mandatory, of course. But users will not like the
> warning if it creates situations that are hard to get right and may still do
> the right thing.
> 
> What do you think: is it a relevant concern to avoid warnings involving
> captures?

Personally I would say not, because this (your example tweaked to use only Numbers):

void test(Map<? extends Number, Number> m) {
	if (m.containsKey(1)) // [potentially?] bad
		m.remove(1); // [potentially?] bad
}

has exactly the same level of potential wrongness as this:

void test(Map<Short, Number> m) {
	if (m.containsKey(1)) // bad
		m.remove(1); // bad
}

And in fact, it's less obvious that the first example is wrong, so perhaps the warning is even more useful there.

In a real use case, perhaps test() should be taking the '1' as an extra argument (and using a type parameter):

void test<T extends Number>(Map<T, Number> m, T removeMe)

> More questions: 
> 
> What should be the default value for this new diagnostic? Warning? Ignore?

Warning!!! It's so rare to need to pass a strange type deliberately. Oh by the way though, perhaps if the method is just a wrapper like the following, then you might want to suppress the warning (I don't see any need to make this optional).

class MySet<T> implements Set<T> {
    public bool contains(Object t) { //implements Set<T>
        return wrappedList.contains(t); //bad but unavoidable
    }
}

> I think we want to make it suppressable with @SW, right? Which token? Should
> we introduce a new token? (Bug 389014 proposed "rawtypes" :-/).

Not rawtypes :) Those methods were declared in terms of Object instead of T for a non-rawtypes-related reason: for example, HashSet<T>.equals(TreeSet<T>) can return true, and therefore so can HashSet<HashSet<T>>.contains(TreeSet<T>). It's just that those cases are much rarer than the common case of passing the wrong key by accident.

I can't see an existing token that's correct - not even "unchecked" since this has nothing to do with heap pollution. Meanwhile, "serial" and "sync-override" are very specific, so it looks as if it's fine to make a new very specific one. Perhaps something like "keytypes"?

> Bug 358209 requests a distinction of potentially incompatible vs.
> incompatible. Should we? I don't have a strong opinion on this, but
> surfacing both levels as separate options in the UI feels like overkill to
> me.

That was my proposal, and it's to do with the HashSet<T>.equals(TreeSet<T>) case above.

So if you write this:
    HashSet<HashSet<T>>.Contains(TreeSet<T>)
then the new warning will kick in because a TreeSet<T> cannot be a HashSet<T>. However, my reasoning was, if you cast it like this:
    HashSet<HashSet<T>>.Contains((Set<T>)TreeSet<T>)
then it can be classed as only 'potentially incompatible', and perhaps users will disable the 'potentially' warning if they need to do this.

Surfacing the two options might not be overkill if you do it using an indented checkbox:

Incorrect key type passed to Map.get et al [Ignore/Warning/Error]
  [X] Allow arguments that could be cast to the correct type

Then it still looks like a single concept and doesn't use up too much space.

Here's another thought actually: perhaps we don't even need the 'potentially' case, i.e. if the argument could be cast to the correct key type then no warning. That would also save you having to write a special case for wrappers.
Comment 9 Stephan Herrmann CLA 2015-03-10 19:38:59 EDT
Created attachment 251444 [details]
draft

(In reply to Missing name from comment #8)

Thanks, Anonymous, for detailed comments.

I see that my considerations about captures were a bit off. Just because it's hard to supply an argument of the required type doesn't make for a good excuse :)

Instead I now included a distinction between
 - argument can be casted to the "expected" type -> softly discouraged
 - argument cannot be casted to the "expected" type -> strongly discouraged
In deviation from standard casting rules I'm admitting a boxing operation before the compatibility check.


> > What should be the default value for this new diagnostic? Warning? Ignore?
> 
> Warning!!! It's so rare to need to pass a strange type deliberately.

That's my take, too. Let's see if anyone objects.
 
> Oh by
> the way though, perhaps if the method is just a wrapper like the following,
> then you might want to suppress the warning (I don't see any need to make
> this optional).
>
> class MySet<T> implements Set<T> {
>     public bool contains(Object t) { //implements Set<T>
>         return wrappedList.contains(t); //bad but unavoidable
>     }
> }

That's actually a situation for which we need a good story, if we want to enable the warning by default.

 * It doesn't make much sense to insert an unchecked cast "(T) t".

 * @SW would be the natural answer, if this occurs infrequently.

 * We could also treat j.l.Object as a tolerable catch-all (In that case users should know that they don't know what they are doing). Would that be part 3 of the option??

 
> > I think we want to make it suppressable with @SW, right? Which token? Should
> > we introduce a new token? (Bug 389014 proposed "rawtypes" :-/).
> 
> Not rawtypes :) Those methods were declared in terms of Object instead of T
> for a non-rawtypes-related reason: for example,
> HashSet<T>.equals(TreeSet<T>) can return true, and therefore so can
> HashSet<HashSet<T>>.contains(TreeSet<T>). It's just that those cases are
> much rarer than the common case of passing the wrong key by accident.
> 
> I can't see an existing token that's correct - not even "unchecked" since
> this has nothing to do with heap pollution. Meanwhile, "serial" and
> "sync-override" are very specific, so it looks as if it's fine to make a new
> very specific one. Perhaps something like "keytypes"?

I agree that none of the existing tokens really match. "keytypes" would cover only the K from Map, not the V and T arguments. Still taking bets.

Let me brainstorm a bit what it really *is*, before condensing into a short token: the library methods are "over generalized", resulting in "dangerous signatures", which falsely admit arguments that semantically don't match.

"problem-signature"
 
> That was my proposal, and it's to do with the HashSet<T>.equals(TreeSet<T>)
> case above.
> 
> So if you write this:
>     HashSet<HashSet<T>>.Contains(TreeSet<T>)
> then the new warning will kick in because a TreeSet<T> cannot be a
> HashSet<T>. However, my reasoning was, if you cast it like this:
>     HashSet<HashSet<T>>.Contains((Set<T>)TreeSet<T>)
> then it can be classed as only 'potentially incompatible', and perhaps users
> will disable the 'potentially' warning if they need to do this.

We're slightly mixing two things here: the reason that both types are admissible is in the implementation of their equals methods. The fact that both have a non-trivial common super type has little to do with this (except, that Set<T> indeed happens to establish this contract of equality).

Obviously, inserting a cast to j.l.Object is the escape door, allowing to soften all warnings to "potential". That's in fact similar to how nested casts can circumvent the checks for uncastable types.

 
> Surfacing the two options might not be overkill if you do it using an
> indented checkbox:
> 
> Incorrect key type passed to Map.get et al [Ignore/Warning/Error]
>   [X] Allow arguments that could be cast to the correct type
> 
> Then it still looks like a single concept and doesn't use up too much space.

Looks good.

At the bottom line, to me the wording seems to be the hard part. This concerns the @SW token and also the warning messages. 

Tests in the attachment show the following messages in action:

1200 = Discouraged argument for method {0}, should be of type {1} to match the declaring type {2}
1201 = Discouraged argument for method {0}, provided type {1} is definitely incompatible to type {2}. Declaring type is {3}


Here's an alternative pair of proposals:

1200 = Invoking method {0} with an argument of type {1} is discouraged. Should use {2} to meet the semantics of declaring type {3}
1201 = Invoking method {0} with an argument of type {1} cannot meet the semantics of declaring type {3}. Recommended type: {2}


I really want to show: (a) what's wrong, (b) what should it be instead, and (c) why. For the "why" I'm including the declaring type (that's where we see the effective parameterization).
Comment 10 Ben Davis CLA 2015-03-11 16:50:58 EDT
(In reply to Stephan Herrmann from comment #9)
> Instead I now included a distinction between
>  - argument can be casted to the "expected" type -> softly discouraged
>  - argument cannot be casted to the "expected" type -> strongly discouraged
> In deviation from standard casting rules I'm admitting a boxing operation
> before the compatibility check.

Sounds good on the face of it; I'm sure you've thought about boxing in more depth than I have. By the way, not sure if you used it in any messages, but we just say 'cast', not 'casted' (it's irregular).

> > class MySet<T> implements Set<T> {
> >     public bool contains(Object t) { //implements Set<T>
> >         return wrappedList.contains(t); //bad but unavoidable
> >     }
> > }
> 
> That's actually a situation for which we need a good story, if we want to
> enable the warning by default.
> 
>  * It doesn't make much sense to insert an unchecked cast "(T) t".
> 
>  * @SW would be the natural answer, if this occurs infrequently.
> 
>  * We could also treat j.l.Object as a tolerable catch-all (In that case
> users should know that they don't know what they are doing). Would that be
> part 3 of the option??

The Object solution would affect people who have Map<String,Object> - which does happen, though it's not that common. Still, what I had in mind is you could check if the argument is a field access expression which resolves to an affected parameter of an affected method. If that's not too onerous then I think it is better. I can't think of any cases it doesn't catch; can you?

> I agree that none of the existing tokens really match. "keytypes" would
> cover only the K from Map, not the V and T arguments. Still taking bets.

Good point.

> Let me brainstorm a bit what it really *is*, before condensing into a short
> token: the library methods are "over generalized", resulting in "dangerous
> signatures", which falsely admit arguments that semantically don't match.
> 
> "problem-signature"

Too general I'd say, and it also draws attention to the signature (not in the user's control) rather than the invocation (which is).

Generalising directly from "keytypes", "argtypes" is closer but it's still too general.

What these methods all have in common is that they never store the Object you pass in, they just use it for comparisons. Not sure if that helps us at all.

I just had a read of http://stackoverflow.com/questions/857420/what-are-the-reasons-why-map-getobject-key-is-not-fully-generic too in the hope of some ideas, perhaps some terminology we haven't thought of. I haven't found a great deal, but terms like liberal, restrictive...

"liberal-arg-type"?

Another way we could look at it is, while there is a reason why these strange argument types are accepted, they are "unlikely" to be correct.

"unlikely-arg-type"?

If more cases are found in the future that have nothing to do with this but are still examples of someone passing an argument of a type that seems unlikely to be right, then I think it could share @SW quite validly with this one.

Aside: it's reassuring to see how many people have upvoted the comments we agree with :)

> > So if you write this:
> >     HashSet<HashSet<T>>.Contains(TreeSet<T>)
> > then the new warning will kick in because a TreeSet<T> cannot be a
> > HashSet<T>. However, my reasoning was, if you cast it like this:
> >     HashSet<HashSet<T>>.Contains((Set<T>)TreeSet<T>)
> > then it can be classed as only 'potentially incompatible', and perhaps users
> > will disable the 'potentially' warning if they need to do this.
> 
> We're slightly mixing two things here: the reason that both types are
> admissible is in the implementation of their equals methods. The fact that
> both have a non-trivial common super type has little to do with this
> (except, that Set<T> indeed happens to establish this contract of equality).

Not sure I understand which two things we're mixing. I think the Set<T> example is merely to explain why someone decided to make these methods take Object instead of T in the first place. It's also definitely the case that 'type cannot be implicitly cast' and 'ditto explicitly' are heuristics, and that they don't take Set<T> into account. (We could do something messy like check if Set<T>.equals() exists and HashSet<T>.equals() doesn't, but that's not foolproof since HashSet<T>.equals() might exist to optimise the 'two HashSets' case without changing semantics, for example. I think our heuristics plus @SW are good enough.)

> Obviously, inserting a cast to j.l.Object is the escape door, allowing to
> soften all warnings to "potential". That's in fact similar to how nested
> casts can circumvent the checks for uncastable types.

Yep :) but it's messy if it happens a lot, so @SW is still useful - someone might have a valid reason to use it on a whole method or class.

> > Surfacing the two options might not be overkill if you do it using an
> > indented checkbox:
> > 
> > Incorrect key type passed to Map.get et al [Ignore/Warning/Error]
> >   [X] Allow arguments that could be cast to the correct type
> > 
> > Then it still looks like a single concept and doesn't use up too much space.
> 
> Looks good.

Cool :) Though now I'm thinking, 'Unlikely key type' may be better?

> At the bottom line, to me the wording seems to be the hard part. This
> concerns the @SW token and also the warning messages. 
> 
> Tests in the attachment show the following messages in action:
> 
> 1200 = Discouraged argument for method {0}, should be of type {1} to match
> the declaring type {2}
> 1201 = Discouraged argument for method {0}, provided type {1} is definitely
> incompatible to type {2}. Declaring type is {3}

These are much easier to understand with examples, so I guess that would be:

1200 = Discouraged argument for method get(Object), should be of type String to match the declaring type Map<String, Integer>
1201 = Discouraged argument for method get(Object), provided type Class is definitely incompatible to type String. Declaring type is Map<String, Integer>

> Here's an alternative pair of proposals:
> 
> 1200 = Invoking method {0} with an argument of type {1} is discouraged.
> Should use {2} to meet the semantics of declaring type {3}
> 1201 = Invoking method {0} with an argument of type {1} cannot meet the
> semantics of declaring type {3}. Recommended type: {2}

1200 = Invoking method get(Object) with an argument of type Object is discouraged. Should use String to meet the semantics of declaring type Map<String, Integer>
1201 = Invoking method get(Object) with an argument of type Class cannot meet the semantics of declaring type Map<String, Integer>. Recommended type: String

> I really want to show: (a) what's wrong, (b) what should it be instead, and
> (c) why. For the "why" I'm including the declaring type (that's where we see
> the effective parameterization).

Yes, it makes sense. I like the second proposal for 1200 the most (since it includes the current argument type), and would perhaps tweak 1201 so we can get the recommended type out before the declaring type. Here's a new proposal (sorry for leaving the examples in, it just makes it easier to discuss and refine):

1200 = Invoking method get(Object) with an argument of type Object is discouraged. Should use String to meet the semantics of declaring type Map<String, Integer>
1201 = Invoking method get(Object) with an argument of type Class is unlikely to be correct. Should use String to meet the semantics of declaring type Map<String, Integer>, or consider adding a cast to Object* if correct

* That's the most specific common supertype, and obviously if they do it then they'll get warning 1200 if enabled.

By the way, if someone does this:
  class MyVerySpecificMap implements Map<String, Integer>
then that will obscure the message, so it may be worth degenerating the 'declaring type' (for message purposes) to the built-in collection interface that originally declared the problematic method - i.e. the message says Map<String, Integer> instead of MyVerySpecificMap.

Oh, in the interests of succinctness, you could also remove the words 'declaring type' and 'method' if you want - it's pretty obvious once it's got examples.

Hope that helps :)
Comment 11 Ben Davis CLA 2015-03-11 16:58:27 EDT
> > > Incorrect key type passed to Map.get et al [Ignore/Warning/Error]
> > >   [X] Allow arguments that could be cast to the correct type
> 
> Cool :) Though now I'm thinking, 'Unlikely key type' may be better?

Make that 'Unlikely argument type'!
Comment 12 Ben Davis CLA 2015-03-11 16:59:16 EDT
(In reply to Missing name from comment #11)
> > > > Incorrect key type passed to Map.get et al [Ignore/Warning/Error]
> > > >   [X] Allow arguments that could be cast to the correct type
> > 
> > Cool :) Though now I'm thinking, 'Unlikely key type' may be better?
> 
> Make that 'Unlikely argument type'!

And 'to the expected type' perhaps?
Comment 13 Stephan Herrmann CLA 2015-03-11 19:23:17 EDT
(In reply to Ben Davis from comment #10)
> By the way, if someone does this:
>   class MyVerySpecificMap implements Map<String, Integer>
> then that will obscure the message, so it may be worth degenerating the
> 'declaring type' (for message purposes) to the built-in collection interface
> that originally declared the problematic method - i.e. the message says
> Map<String, Integer> instead of MyVerySpecificMap.

That's exactly what the implementation in attachment 251444 [details] does :)
I don't yet have tests matching your exact situation, but for all tests using Set<Short> the message mentions the 'declaring type Collection<Short>'. This is done by searching the super type that is a parameterization of one of the well-known trouble makers (Collection and Map, so far)

 
> Oh, in the interests of succinctness, you could also remove the words
> 'declaring type' and 'method' if you want - it's pretty obvious once it's
> got examples.

And the previous point is exactly the reason why I say 'declaring type'. Still users will complain: why is the message talking about Map<String,Integer>? I don't see this type anywhere in the flagged code. I want to give at least some hint where to look.
Comment 14 Stephan Herrmann CLA 2015-03-12 08:55:47 EDT
(In reply to Ben Davis from comment #10)
> >  * We could also treat j.l.Object as a tolerable catch-all (In that case
> > users should know that they don't know what they are doing). Would that be
> > part 3 of the option??
> 
> The Object solution would affect people who have Map<String,Object>.

I'm not sure which effect you have in mind? For such map asking containsValue(x) is always OK, with or without any special rule for j.l.Object, but anyway I wouldn't include this rule just now.

> Still, what I had in mind is you
> could check if the argument is a field access expression which resolves to
> an affected parameter of an affected method. If that's not too onerous then
> I think it is better.

too complex, IMHO.
 
> Another way we could look at it is, while there is a reason why these
> strange argument types are accepted, they are "unlikely" to be correct.
> 
> "unlikely-arg-type"?

I like this best, so far.

Accordingly, the UI proposal would now be s.t. like

Unlikely argument type for Map.get() et al [Ignore/Warning/Error]
  [X] Allow arguments that could be cast to the expected type

(I don't think we want to say "argument type passed to").

Maybe "allow" has too strong indication of legality wrt JLS? "Ignore" might be more correct, but also more confusing. I think "accept" would strike a good balance.

I'd leave the following as a possible future addition:
  [X] Accept arguments of type java.lang.Object


As for the warning messages, we seem to agree on:

1200 = Invoking method {0} with an argument of type {1} is discouraged. Should use {2} to meet the semantics of declaring type {3}

For the stronger message, my initial thought was that uncastable types are definitely wrong. As the Set example shows, this isn't even true. Hence, "uncastability" is only an even stronger indication of an error, not a proof.

From that, 1201 could either just emphasize like in "strongly discouraged", or harp more on a connection to the UI option, perhaps by saying:

1201 = Discouraged invocation of method {0}. Argument type {1} cannot be cast to the likely type {2} according to the declaring type {3}.

If this agreeable, we could assimilate 1200 by saying

1200 = Discouraged invocation of method {0}. Argument type {1} is incompatible to the likely type {2} according to the declaring type {3}.

I'll add tests, which spell these out with examples in my next patch.

I was also playing with different orders of parts of the message, but I think the current order succeeds in putting the most important parts first.


> 1201 = Invoking method get(Object) with an argument of type Class is
> unlikely to be correct. Should use String to meet the semantics of declaring
> type Map<String, Integer>, or consider adding a cast to Object* if correct
> 
> * That's the most specific common supertype, and obviously if they do it
> then they'll get warning 1200 if enabled.

The "or" part is too much, IMHO. I'm not even sure if we want to *recommend* that workaround using a cast.
Comment 15 Ben Davis CLA 2015-03-12 09:35:28 EDT
(Replying to both posts at once)

> > Map<String, Integer> instead of MyVerySpecificMap.
> 
> That's exactly what the implementation in attachment 251444 [details] does :)

Great, I hadn't checked :)

> I don't yet have tests matching your exact situation, but for all tests
> using Set<Short> the message mentions the 'declaring type
> Collection<Short>'. This is done by searching the super type that is a
> parameterization of one of the well-known trouble makers (Collection and
> Map, so far)

I'm sure it will work.
 
> > Oh, in the interests of succinctness, you could also remove the words
> > 'declaring type' and 'method' if you want - it's pretty obvious once it's
> > got examples.
> 
> And the previous point is exactly the reason why I say 'declaring type'.
> Still users will complain: why is the message talking about
> Map<String,Integer>? I don't see this type anywhere in the flagged code. I
> want to give at least some hint where to look.

You're right, it won't entirely solve the problem you're trying to solve. But I think most people will get it with either version (come on, they KNOW about the Map<A,B> they're using that the warning mentioned), and my feelings about keeping/removing the words are as weak as gravity :)

> > The Object solution would affect people who have Map<String,Object>.
> 
> I'm not sure which effect you have in mind? For such map asking
> containsValue(x) is always OK, with or without any special rule for
> j.l.Object, but anyway I wouldn't include this rule just now.

I was thinking of the case where someone accidentally passes a value to a function that expects a key. Since the values are Objects, they'd miss out on a warning. Granted they're already subject to the opposite risk (passing a key where a value was expected), but there's no sense in making it worse.

> > Still, what I had in mind is you
> > could check if the argument is a field access expression which resolves to
> > an affected parameter of an affected method. If that's not too onerous then
> > I think it is better.
> 
> too complex, IMHO.

Shrug. Wouldn't stop me, but OK :)

> > Another way we could look at it is, while there is a reason why these
> > strange argument types are accepted, they are "unlikely" to be correct.
> > 
> > "unlikely-arg-type"?
> 
> I like this best, so far.
> 
> Accordingly, the UI proposal would now be s.t. like
> 
> Unlikely argument type for Map.get() et al [Ignore/Warning/Error]
>   [X] Allow arguments that could be cast to the expected type
> 
> (I don't think we want to say "argument type passed to").

More succinctness - good :)

> Maybe "allow" has too strong indication of legality wrt JLS? "Ignore" might
> be more correct, but also more confusing. I think "accept" would strike a
> good balance.

I noticed that too but was too tired (and felt I'd spammed enough) to explore it. I agree - change it to 'Accept'.

> I'd leave the following as a possible future addition:
>   [X] Accept arguments of type java.lang.Object

Hardcoded to accept those for now? Mainly so we don't screw container wrappers up.

> As for the warning messages, we seem to agree on:
> 
> 1200 = Invoking method {0} with an argument of type {1} is discouraged.
> Should use {2} to meet the semantics of declaring type {3}
> 
> For the stronger message, my initial thought was that uncastable types are
> definitely wrong. As the Set example shows, this isn't even true. Hence,
> "uncastability" is only an even stronger indication of an error, not a proof.
> 
> From that, 1201 could either just emphasize like in "strongly discouraged",
> or harp more on a connection to the UI option, perhaps by saying:
> 
> 1201 = Discouraged invocation of method {0}. Argument type {1} cannot be
> cast to the likely type {2} according to the declaring type {3}.
> 
> If this agreeable, we could assimilate 1200 by saying
> 
> 1200 = Discouraged invocation of method {0}. Argument type {1} is
> incompatible to the likely type {2} according to the declaring type {3}.
> 
> I'll add tests, which spell these out with examples in my next patch.

That all sounds good, except grammatically it needs to be 'incompatible with'. Or if you want to be clearer, I think 'not convertible to' would convey the right meaning.

> I was also playing with different orders of parts of the message, but I
> think the current order succeeds in putting the most important parts first.

Agreed - that was what motivated my suggestion for 1201.

> The "or" part is too much, IMHO. I'm not even sure if we want to *recommend*
> that workaround using a cast.

Fair point - leave it out. @SW is probably better than casting in such cases.

:)
Comment 16 Ben Davis CLA 2015-03-12 09:39:20 EDT
Playing further with the 'not convertible' thing, we could consider saying 'not convertible' for one and 'not convertible even with a cast' for the other?

So you'd have

1200 = Discouraged invocation of method {0}. Argument type {1} is not convertible to the likely type {2} according to the declaring type {3}.

1201 = Discouraged invocation of method {0}. Argument type {1} is not convertible to the likely type {2} according to the declaring type {3}, even with a cast.

Nah, don't like it. Stick with what you've got. (Posting anyway just in case you do want to explore this.)
Comment 17 Eclipse Genie CLA 2015-03-12 11:48:10 EDT
New Gerrit change created: https://git.eclipse.org/r/43735
Comment 18 Stephan Herrmann CLA 2015-03-12 11:57:10 EDT
Shifted from linguistics back to engineering :)

(In reply to Eclipse Genie from comment #17)
> New Gerrit change created: https://git.eclipse.org/r/43735

@Markus, do you want to comment on added API (constants in JavaCore and IProblem - plus new @SW token) and message wording?

I also Cc'd you on bug 461999, marked that one as M7, though.
Comment 19 Stephan Herrmann CLA 2015-03-12 12:05:50 EDT
For better accessibility, here's the list of methods recognized by the change:

Collection.contains(Object)
Collection.remove(Object)

Collection.containsAll(Collection<?>)
Collection.removeAll(Collection<?>)
Collection.retainAll(Collection<?>)

List.indexOf(Object)
List.lastIndexOf(Object)

Map.containsKey(Object)
Map.get(Object)

Map.containsValue(Object)


Methods are also recognized via any sub-type of the above.
Comment 20 Stephan Herrmann CLA 2015-03-12 15:06:37 EDT
A failure in BatchCompilerTest reminded me, that hasTypeBit() may trigger super type traversal, thus making lookup more eager than strictly necessary. I reduced this effect to the minimum by first comparing selectors and only then checking the receiver type.
Comment 22 Stephan Herrmann CLA 2015-03-12 19:01:31 EDT
Released for 4.5 M6.

Let me know if I missed s.t. relevant.
Comment 23 Eclipse Genie CLA 2015-03-14 10:30:24 EDT
New Gerrit change created: https://git.eclipse.org/r/43851
Comment 24 Stephan Herrmann CLA 2015-03-14 10:35:30 EDT
(In reply to Eclipse Genie from comment #23)
> New Gerrit change created: https://git.eclipse.org/r/43851

This changes one of the options from [E/W/I] to [Enable/Disable] to better support the intended way to surface this in the options/preferences UI.

We still use two IProblems, just the way they are controlled via JavaCore options is changed.
Comment 25 Stephan Herrmann CLA 2015-03-14 12:12:39 EDT
(In reply to Stephan Herrmann from comment #24)
> (In reply to Eclipse Genie from comment #23)
> > New Gerrit change created: https://git.eclipse.org/r/43851
> 
> This changes one of the options from [E/W/I] to [Enable/Disable] to better
> support the intended way to surface this in the options/preferences UI.
> 
> We still use two IProblems, just the way they are controlled via JavaCore
> options is changed.

Pushed directly via http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=27e2724327c75ddbe7646447a4af212947b149ed to accommodate tiny javadoc corrections.
Comment 26 Eclipse Genie CLA 2015-03-15 12:12:19 EDT
New Gerrit change created: https://git.eclipse.org/r/43884
Comment 27 Stephan Herrmann CLA 2015-03-15 12:27:51 EDT
(In reply to Eclipse Genie from comment #26)
> New Gerrit change created: https://git.eclipse.org/r/43884

No, Genie. That commit is not for this bug, it just mentions this bug's number in its title.
Comment 29 Stephan Herrmann CLA 2015-03-15 20:11:38 EDT
(In reply to Eclipse Genie from comment #28)
> Gerrit change https://git.eclipse.org/r/43884 was merged to [master].
> Commit:
> http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/
> ?id=92a55fb8c61c817a37bdd13d873f8eba50e25d7d

No, Genie. That commit is not for this bug, it just mentions this bug's number in its title.
Comment 30 Markus Keller CLA 2015-03-16 13:05:14 EDT
We should not enable new experimental options by default and let users be the guinea pigs.

The jdt.core repo contains about 20 such problems, and jdt.ui over 100. We first have scrutinize at least these invocations and be sure they are really problematic. That will also tell us if we should provide Quick Fixes for common problem patterns.

Stephan, can you please disable the main option for M6?
Comment 31 Stephan Herrmann CLA 2015-03-16 16:41:37 EDT
Some analysis of reported warnings:

org.eclipse.jdt.core
total: 8 warnings
- 2 real bugs
- 6 cases pass a value of a super type of the expected type
  This is a (probably unconscious) shorthand for an instanceof check plus cast


Let's check if just flipping the sub-option gives an even better ratio:

- 2 warning are of the severe form "cannot be cast"
- 6 warnings say "is incompatible with"

Unfortunately, only 1 of the 2 bugs is in the "uncastable" group.

The other "uncastable" problem concerns a removeAll() invocation, where we could possibly relax the warning by comparing element types instead of collection types.

OTOH, we have a bug that escapes the weaker form of the warning. This happens, because an interface is involved, and hence types are considered as castable although totally unrelated.

That would mean for the small sample of o.e.j.core by flipping the sub-option we could reduce the number of reports to 1, with 0 false positives but 1 false negative.


It probably depends on how you look at the 6 cases that are not strictly bugs (from a cursory look most of the warnings in jdt.ui also fit into this group). I personally think those reports are still interesting, but I know that many users will not agree.

=====

When I started working on this bug I didn't expect this to be controversial (otherwise I would have started earlier), but seeing the request to disable by default I will simply revert this feature for Mars and start a discussion early after Mars. This will give us time to sharpen the analysis for the best possible hit-ratio.


I don't want to publish yet another warning that is off-by-default and is wasted to most users. Remember, switching the default after initial release doesn't reach any users that already persisted the preferences in their projects.
Comment 32 Eclipse Genie CLA 2015-03-16 16:44:15 EDT
New Gerrit change created: https://git.eclipse.org/r/43973
Comment 33 Eclipse Genie CLA 2015-03-16 16:55:40 EDT
New Gerrit change created: https://git.eclipse.org/r/43974
Comment 34 Eclipse Genie CLA 2015-03-16 17:30:06 EDT
New Gerrit change created: https://git.eclipse.org/r/43976
Comment 35 Ben Davis CLA 2015-03-16 17:40:06 EDT
(In reply to Stephan Herrmann from comment #31)
> The other "uncastable" problem concerns a removeAll() invocation, where we
> could possibly relax the warning by comparing element types instead of
> collection types.

Absolutely - it's very common for example to go Set<T>.removeAll(List<T>), e.g. if we were iterating over the set and couldn't modify it until we finished. Definitely worth refining.

> OTOH, we have a bug that escapes the weaker form of the warning. This
> happens, because an interface is involved, and hence types are considered as
> castable although totally unrelated.

Presumably because the compiler can't rule out that the class may have a subtype that implements the interface. One possible, pretty simple, refinement could be, "Accept arguments that are a supertype of the expected type."

> I don't want to publish yet another warning that is off-by-default and is
> wasted to most users. Remember, switching the default after initial release
> doesn't reach any users that already persisted the preferences in their
> projects.

Agreed!
Comment 37 Stephan Herrmann CLA 2015-03-16 18:02:10 EDT
(In reply to Ben Davis from comment #35)
> (In reply to Stephan Herrmann from comment #31)
> > The other "uncastable" problem concerns a removeAll() invocation, where we
> > could possibly relax the warning by comparing element types instead of
> > collection types.
> 
> Absolutely - it's very common for example to go Set<T>.removeAll(List<T>),
> e.g. if we were iterating over the set and couldn't modify it until we
> finished. Definitely worth refining.

It's not about Set<T> vs. List<T> (both conform to Collection<T>), but about Collection<Super> vs. Collection<Sub>. A Collection<Super> is not castable to Collection<Sub>, but each contained element Super, is castable to Sub - which may be enough for the invocation to do s.t. useful.
Comment 38 Markus Keller CLA 2015-03-16 18:43:47 EDT
A fix to add when this is resurrected:

Javadoc of org.eclipse.jdt.core.JavaCore.COMPILER_PB_DISCOURAGED_INVOCATION_ACCEPT_CASTABLE_ARGUMENT had typos in "Possible values" and "Default":
Said e.g. "enable" instead of "enabled".
Comment 39 Till Brychcy CLA 2016-02-14 07:50:11 EST
(In reply to Stephan Herrmann from comment #31)
> by default I will simply revert this feature for Mars and start a discussion
> early after Mars. This will give us time to sharpen the analysis for the

The check in this ticket (including the corresponding "equals"-check) is for the main kind of easily avoidable, common and still hard to find bug that is currently not reported by eclipse (but is reported by intellij, netbeans and of course by findbugs, but this is awkward and slow by comparison to a build-in check in eclipse)

It's a shame this didn't make into Mars, but I guess its already past "early after Mars", so maybe you please start the discussion so it could hopefully make into the next milestone :-)

Maybe the default level could simply be the new "info"-level?
Comment 40 Stephan Herrmann CLA 2016-02-14 08:10:57 EST
(In reply to Till Brychcy from comment #39)
> ...so maybe you please start the discussion so it could hopefully
> make into the next milestone :-)

I'm all ears. I think I've put enough information regarding my understanding of the issue into previous comments.

Markus, are you still interested in this feature?
Comment 41 Jay Arthanareeswaran CLA 2016-02-15 07:56:36 EST
(In reply to Stephan Herrmann from comment #40)
> Markus, are you still interested in this feature?

Markus is on vacation for a week or so. So, it could be a while before we get a response.
Comment 42 Stephan Herrmann CLA 2016-02-25 07:42:02 EST
(In reply to Jay Arthanareeswaran from comment #41)
> (In reply to Stephan Herrmann from comment #40)
> > Markus, are you still interested in this feature?
> 
> Markus is on vacation for a week or so. So, it could be a while before we
> get a response.

Markus, are you back?
Comment 43 Markus Keller CLA 2016-02-25 08:46:20 EST
I'm back, but I'm afraid I won't have time for bigger feedback rounds or test/fix passes. If you think you have a solution that doesn't trigger many false positives, then I'm OK with releasing it with Info severity by default.

Please review it with a code base that actually uses typed collections. In jdt.core, many collections are still raw or the code uses arrays or internal data structures. The platform.text repo could be a good test case. jdt.ui still has unavoidableGenericTypeProblems=disabled and uses raw types from jdt.core and jface APIs.
Comment 44 Stephan Herrmann CLA 2016-02-25 12:46:11 EST
(In reply to Markus Keller from comment #43)
> I'm back, but I'm afraid I won't have time for bigger feedback rounds or
> test/fix passes. If you think you have a solution that doesn't trigger many
> false positives, then I'm OK with releasing it with Info severity by default.
> 
> Please review it with a code base that actually uses typed collections. In
> jdt.core, many collections are still raw or the code uses arrays or internal
> data structures. The platform.text repo could be a good test case. jdt.ui
> still has unavoidableGenericTypeProblems=disabled and uses raw types from
> jdt.core and jface APIs.

Thanks. Before I invest more time, I still need to check with you:

Raising problems only in situations that are certain not to be false positives and setting severity to Info will be lame, for two reasons:

(1) For a small fraction (not-castable types) we can be quite sure of detecting real bugs. Here anything below Warning severity would be wrong IMHO. (Programs could still work due to equals/hashCode hacks, but we have no chance to sift these cases out anyway).

(2) For the majority of situations we indeed cannot clearly distinguish "false positives" from useful reports.



I'm willing to continue work if we agree that

- not-castable types are reported as warnings

- at info level it's OK to raise "false positives"



Within the latter group, some fine tuning may still be possible, but each match in this group could be an intentional abbreviation for
   if (k instanceof ElementType)
      set.remove((ElementType) k);

If from this we would conclude, that "set.remove(k)" must not be flagged for castable types, then I don't think the whole thing would be worth the effort and we should close this as WONTFIX (which would only be the last resort if we can't agree better).
Comment 45 Stephan Herrmann CLA 2016-03-03 13:21:15 EST
I'm very sorry to say: considering the expectations expressed in comment 43 I am not able to provide an implementation of a very useful analysis.

This is a great pity after the work that has already gone into it and given that such a validation should be expected from any Java IDE.
Comment 46 Ben Davis CLA 2016-03-03 15:19:17 EST
What?! Resolved as WONTFIX? Design by committee at its finest.

Suuuuurely to goodness, an off-by-default warning is better than just giving up entirely?
Comment 47 Sasikanth Bharadwaj CLA 2016-03-16 05:37:37 EDT
Verified for 4.6 M6
Comment 48 Eclipse Genie CLA 2017-02-21 16:55:54 EST
New Gerrit change created: https://git.eclipse.org/r/91577
Comment 49 Till Brychcy CLA 2017-02-21 17:02:49 EST
(In reply to Eclipse Genie from comment #48)
> New Gerrit change created: https://git.eclipse.org/r/91577

@Stephan, as discussed at EclipseCon Ludwigsburg last year, I think this is badly missing in Eclipse and I have rebased your (great!) patch which already solved all the hard parts and worked on it to find "a solution that doesn't trigger many false positives".

(In reply to Stephan Herrmann from comment #31)
> 
> The other "uncastable" problem concerns a removeAll() invocation, where we
> could possibly relax the warning by comparing element types instead of
> collection types.
See 1)

> 
> OTOH, we have a bug that escapes the weaker form of the warning. This
> happens, because an interface is involved, and hence types are considered as
> castable although totally unrelated.
See 2)

Patch set 1 just contains the rebased patch with some conflicts resolved (bits & problem ids)

I've tested the patch against a few workspaces including a workspace with nearly 500 projects that are in the git repositories checked out in a eclipse.platform.releng.aggregator-build (details see below, more than 40000 classes) and another workspace with more than 800 projects and 30000 classes.

1) As Stephan already noted, applying the type compatibility check directly on the arguments is too restrictive for methods like removeAll
    because collection of a type is not a supertype of a collection of subtype, so it helps to compare the type arguments in this case, which needed a change of the message reported.
2) In a lot of projects, disabling "Accept arguments that could be cast to the expected type" gives too many false problem reports, e.g. because of collections of a subtype,  because of wildcards, or in classes that implement collection interfaces themselves. But as Stephan wrote, enabling "Accept arguments that could be cast to the expected type"  disables the check for all invocations, where the collection type is not a final class and the argument type is an "unrelated" interface. But many usages of this kind I have observed where bugs.

To improve the situation, I have played with the check for allowed types and looked at the actual bugs found .

Many bugs that were found fall into the following categories:
- confusion of a type and a "primary key" member of it. This kind of bug is sometimes not reported, if all castable classes are accepted.
- confusion of key and value types of maps  (or confusion of respective methods). This also happens in generic code where both types are type variables.
- confusion of arrays and their elements

So I have arrived at the following check, which leads to good results:
"Take the erasure of both types (unless both are true type variables, i.e. not captures) and then compare if they are compatible in either direction."

This is a bit similar, but different to "Accept arguments that could be cast to the expected type", so I have removed that option for now (we can still introduce a kind of "strict"-mode later)

With this new condition and the change for removeAll all actual bugs if have seen are still reported and only very few false alarms appear.

Some other things I have added:
- Implement the check for method references. This, and the change for removeAll etc. made it necessary to alter the wording of the problem messages (e.g. avoid the word "invocation")
- Implement the check for "equals", too (see bug 415306), including java.utils.Objects.equals(a,b). With the same check as described above,  many bugs are reported, but there are more false reports, as implementations of equals() that actually allow different argument types are rare, but not completely uncommon.

The message for collection methods is now:
"Unlikely argument type String for contains(Object) on a Collection<Short>"
"Unlikely argument type Set<String> for removeAll(Collection<?>) on a Collection<Number>

For equals, not the declaring type of the equals-method is reported but the instance/other argument type:
"Unlikely argument type for equals(): Integer seems to be unrelated to String"

All of this is contained in patch set 2.

The next section contains a detailed comparison of patch set 1 (with "Accept arguments that could be cast to the expected type" enabled) with patch set 2 in a workspace with most projects contained in the git repos checked out in a eclipse.platform.releng.aggregator-build (as of 12. Feb 2017, excluding platform-specific code, embedded test projects and some emf-related code, that need dependencies not contains in those git-repos) - only for the collection methods, not for equals().

I've grouped the reported problems into three groups.
A: Locations that are reported by patch set 2, but not by patch set 1.
B: Locations reported in patch set1, but are not in patch set 2. They are variations of removeAll. None of them seemed to be a bug for me.
C: Locations that are reported both by patch set 1 and patch set 2.

-----------Group A: New in patch set 2: 9 additional bugs found, 6 false alarms added--------------------
BUGS: (confusion of key and values or the respective methods):
ConsumerReportConvertor.java	/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/search	line 201	Java Problem
EventListeners.java	/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr	line 71	Java Problem
InternalSearchUI.java	/org.eclipse.search/new search/org/eclipse/search2/internal/ui	line 117	Java Problem
ManyToMany.java	/org.eclipse.jdt.compiler.apt/src/org/eclipse/jdt/internal/compiler/apt/util	line 314	Java Problem
ManyToMany.java	/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/util	line 315	Java Problem
RecentSettingsStore.java	/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javadocexport	line 96	Java Problem

BUGS: (other)
DragHost.java	/org.eclipse.e4.ui.workbench.addons.swt/src/org/eclipse/e4/ui/workbench/addons/dndaddon	line 138	Java Problem
- should remove from getWindows() not from getChildren()
ResourceToItemsMapper.java	/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport	line 125	Java Problem
- should remove resource, not list
ResourceToItemsMapper.java	/org.eclipse.search/search/org/eclipse/search/internal/ui	line 138	Java Problem
- should remove resource, not list

NOT BUGS:
ChangeCollector.java	/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy	line 371	Java Problem
- CompilationUnit extends Openable which extends JavaElement and implements IOpenable

FragmentExtractHelper.java	/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/model	line 53	Java Problem
ModelAssembler.java	/org.eclipse.e4.ui.workbench/src/org/eclipse/e4/ui/internal/workbench	line 407	Java Problem
- e.g. ApplicationElementImpl implements EObject and MApplicationElement

RuntimeClasspathViewer.java	/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/classpath	line 144	Java Problem
- ClasspathEntry extends AbstractClasspathEntry which implements IClasspathEntry and implements  IRuntimeClasspathEntry

SourceReferenceUtil.java	/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/reorg	line 54	Java Problem
- e.g. Member extends SourceRefElement implements IMember

TypeHierarchy.java	/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy	line 1045	Java Problem
- already guarded by instanceof ClassFile which extends Openable

--------- Group B: Reported by patch set 1, not any more in patch set 2----------------
NOT BUGS: removeAll etc for collections of subtypes/supertype
AbstractWorkingSetWizardPage.java	/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/workingsets	line 482	Java Problem
IdentityMapTest.java	/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding	line 342	Java Problem
IdentityMapTest.java	/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding	line 346	Java Problem
IdentityMapTest.java	/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding	line 383	Java Problem
IdentityMapTest.java	/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding	line 393	Java Problem
IndexerTest.java	/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/indexer	line 255	Java Problem
JarManifestWizardPage.java	/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/jarpackager	line 638	Java Problem
JarManifestWizardPage.java	/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/jarpackager	line 643	Java Problem
LaunchDelegateTests.java	/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/launching	line 178	Java Problem
LaunchModeTests.java	/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/launching	line 191	Java Problem
LaunchModeTests.java	/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/launching	line 204	Java Problem
MenuManagerRenderer.java	/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt	line 1084	Java Problem
MenuManagerRenderer.java	/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt	line 1091	Java Problem
MenuManagerRenderer.java	/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt	line 1098	Java Problem
ModuleResolver.java	/org.eclipse.osgi/container/src/org/eclipse/osgi/container	line 1140	Java Problem
ModuleResolver.java	/org.eclipse.osgi/container/src/org/eclipse/osgi/container	line 944	Java Problem
ModuleResolver.java	/org.eclipse.osgi/container/src/org/eclipse/osgi/container	line 945	Java Problem
PullUpRefactoringProcessor.java	/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure	line 947	Java Problem
RemoveFromBuildpathAction.java	/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths/newsourcepage	line 161	Java Problem
RemovedImportCommentReassigner.java	/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports	line 150	Java Problem
ReorgPolicyFactory.java	/org.eclipse.jdt.ui/core refactoring/org/eclipse/jdt/internal/corext/refactoring/reorg	line 2233	Java Problem
TargetLocationsGroup.java	/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/shared/target	line 381	Java Problem
ToolBarManagerRenderer.java	/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt	line 966	Java Problem
TypesConfigurationArea.java	/org.eclipse.ui.ide/src/org/eclipse/ui/internal/views/markers	line 426	Java Problem
WorkingSetDropAdapter.java	/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/packageview	line 260	Java Problem

--- Group C: Reported by both patch set 1 and patch set 2: 17 Bugs, 2 Non-Bugs--------------------
BUGS: (confusion of key and values or the respective methods):
BaseMessageRegistry.java	/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/nls	line 307	Java Problem
LogicalPackagesProvider.java	/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/browsing	line 129	Java Problem
ProvisioningContext.java	/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/p2/engine	line 168	Java Problem
SourceLookupFacilityTests.java	/org.eclipse.debug.tests/src/org/eclipse/debug/tests/sourcelookup	line 313	Java Problem
Test_org_eclipse_swt_program_Program.java	/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit	line 160	Java Problem

BUGS: other
AbstractPDELaunchConfiguration.java	/org.eclipse.pde.launching/src/org/eclipse/pde/launching	line 203	Java Problem
- string used as key

BaseMessageRegistry.java	/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/nls	line 231	Java Problem
- "this" is the PrivilegedAction, not the MessageConsumer 

NdNodeTypeRegistry.java	/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd	line 39	Java Problem
- should be this.types.containsKey(shortTypeId)

ProductFile.java	/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse	line 437	Java Problem
- string used as argument

RequiredPluginsClasspathContainer.java	/org.eclipse.pde.core/src/org/eclipse/pde/internal/core	line 380	Java Problem
- string used as argument

TestReporter.java	/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/search/tests	line 59	Java Problem
- argument is not a string. maybe String.valueof should be applied to it?

BUGS: confusion of array with array element
BoundSet.java	/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup	line 1259	Java Problem
- should be get(iv) instead of get(outerVariables)

CallHistory.java	/org.eclipse.ui.tests.harness/src/org/eclipse/ui/tests/harness/util	line 129	Java Problem
- should be methodName

BUGS: confusion of type with member of it:
LaunchConfigurationManager.java	/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/launchConfigurations	line 751	Java Problem
- should be types.contains(type.getIdentifier())
NewJavaProjectWizardPageTwo.java	/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/wizards	line 337	Java Problem
- should be fOrginalFolders.contains(child)
Product.java	/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/product	line 647	Java Problem
- should be fFeatures.contains(features[i])
VariableCreationDialog.java	/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/wizards/buildpaths	line 98	Java Problem
- should be remove(element)

NOT BUGS: (but actually interesting warnings)
ApiFiltersPropertyPage.java	/org.eclipse.pde.api.tools.ui/src/org/eclipse/pde/api/tools/ui/internal/properties	line 340	Java Problem
- not a bug, but surprising: CommentChange delegates equals to its filter member - an example for the kind of code that leads to false alarms for the equals()-check.

SetBindingTest.java	/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding	line 80	Java Problem
- it seems like the remove call intentionally tries to remove an object that cannot be contained in the test.

--------------------

Summary:
Patch set 2 finds 26 bugs and gives 8 false alarms. The results in my other workspaces were similar, with even fewer false alarms per bug found.

For equals(), I haven't done a detailed analysis, but the absolute number of bugs found was lower, and the ratio of false alarms was a bit higher.

I have set the default level for both to "INFO" (though i will set them to "WARNING" in my workspaces)

The test failures in BatchCompilerTest.test330_warn_options() and BatchCompilerTest.test331_warn_options() are because there is currently no way to disable "INFO"s for the batch compiler. I guess this should be discussed in bug Bug 496137.
Comment 50 Stephan Herrmann CLA 2017-02-26 12:48:37 EST
Thanks, Till for your huge efforts.

With all the real bugs the analysis detects, this may well become a huge success story.

With your improvements and the facts to back our claims, it would be a shame to release this with severity below WARNING. Remember we have a @SW token to silence any "unwanted" warnings.

@Markus, do you care to comment?
Comment 51 Stephan Herrmann CLA 2017-03-04 11:04:43 EST
Due to API freeze at M6 we need to make a call now.


(In reply to Till Brychcy from comment #49)
> So I have arrived at the following check, which leads to good results:
> "Take the erasure of both types (unless both are true type variables, i.e.
> not captures) and then compare if they are compatible in either direction."
> 
> This is a bit similar, but different to "Accept arguments that could be cast
> to the expected type", so I have removed that option for now (we can still
> introduce a kind of "strict"-mode later)
> 
> With this new condition and the change for removeAll all actual bugs if have
> seen are still reported and only very few false alarms appear.

I believe this to be good, just I don't understand *why* this exact way of comparison should be the winner. Is it purely heuristic or do you have a theory to explain why it makes sense?

I agree to deferring a potential sub-option to later.


> I have set the default level for both to "INFO" (though i will set them to
> "WARNING" in my workspaces)

I still believe much of the effect will be lost if defaulting to info.

Consider a big workspace with lots of existing code. In projects less disciplined than JDT, the number of warnings in the workspace will be overwhelming already. Nobody will ever look past that and into the info section.
What I typically do, if the problems view shows too much: switch to "Errors/Warnings on Project". Please see that this option does not show any infos at all. Users could configure their own options for the problems view, but I predict they won't. So in that context infos are only ever visible when you open the editor.
An analysis that finds existing bugs at the extent demonstrated here, should be more visible than that.

Many of those "unwanted" warnings are still interesting to document via  @SW.

Last year it was Markus, who requested defaulting to "info" at most. We haven't heard from him since. Let's make a decision tomorrow, if possible.
Comment 52 Till Brychcy CLA 2017-03-04 14:40:07 EST
(In reply to Stephan Herrmann from comment #51)
> I believe this to be good, just I don't understand *why* this exact way of
> comparison should be the winner. Is it purely heuristic or do you have a
> theory to explain why it makes sense?

"Purely heuristic" is a good description.

By taking the erasures, problems with captures are avoided. Not doing it for "true" type variables lets us still find bugs in generic code. Allowing compatibility in either direction is in the same sprit as allowing castable types, but narrower.

I've tried other variations (e.g. just uncapturing captures or by creating a variation of the "castable"-check that doesn't assume that there might be subclasses that implement another interface) but they were more complicated, harder to explain, didn't find any additional bugs and gave more false positives.


> I still believe much of the effect will be lost if defaulting to info.
> 
> [...]
> 
> Last year it was Markus, who requested defaulting to "info" at most. We
> haven't heard from him since. Let's make a decision tomorrow, if possible.

My opinion:
+1 for "WARNING" as default, at least for "collection methods"
I'm also leaning towards "WARNING" as default for "equals", even though there are a bit more false alarms.

Anyway, having these checks with default "INFO" is way better than not having them at all ;-)
Comment 53 Stephan Herrmann CLA 2017-03-04 16:32:14 EST
(In reply to Till Brychcy from comment #52)
> (In reply to Stephan Herrmann from comment #51)
> > I believe this to be good, just I don't understand *why* this exact way of
> > comparison should be the winner. Is it purely heuristic or do you have a
> > theory to explain why it makes sense?
> 
> "Purely heuristic" is a good description.
> 
> By taking the erasures, problems with captures are avoided. Not doing it for
> "true" type variables lets us still find bugs in generic code. Allowing
> compatibility in either direction is in the same sprit as allowing castable
> types, but narrower.
> 
> I've tried other variations (e.g. just uncapturing captures or by creating a
> variation of the "castable"-check that doesn't assume that there might be
> subclasses that implement another interface) but they were more complicated,
> harder to explain, didn't find any additional bugs and gave more false
> positives.

Sounds fair. A heuristic approach fits the goal to find "unlikely" arguments :)

OTOH, doesn't the use of such heuristics actually motivate having a strict variant as an option (off by default)? If people don't trust your heuristics they could still analyse their code in strict mode (maybe only temporarily switching modes).

Maybe people are more willing to perform the experiment of using strict mode while the feature is still new. This _could_ give valuable hints about cases that we are missing due to the heuristics.

How thoroughly have you removed this option? Can it easily be brought back to life? As to possible wordings of such option, a precise description is overkill, so we could paraphrase it either way as
 [x] Apply heuristic filters
or
 [ ] Perform strict analysis against the expected type
Or similar.

Do you still want to postpone this decision?
Comment 54 Till Brychcy CLA 2017-03-05 13:59:29 EST
(In reply to Stephan Herrmann from comment #53)
> Sounds fair. A heuristic approach fits the goal to find "unlikely" arguments
> :)
> 
> OTOH, doesn't the use of such heuristics actually motivate having a strict
> variant as an option (off by default)? If people don't trust your heuristics
> they could still analyse their code in strict mode (maybe only temporarily
> switching modes).
> 
> Maybe people are more willing to perform the experiment of using strict mode
> while the feature is still new. This _could_ give valuable hints about cases
> that we are missing due to the heuristics.
> 
> How thoroughly have you removed this option? Can it easily be brought back
> to life? [...]

Shouldn't be too hard, but i fear still too much work before the api freeze.

TODO would be: Add constants for the option, add ui, update all tests, invent an optimized error message. I don't think this option would to be available for the batch compiler.

> [...]
>  [ ] Perform strict analysis against the expected type

When we do that: I like the second option better.

> Or similar.
> 
> Do you still want to postpone this decision?

I'm not against having it, but I think the feature is already extremely useful without it (and even with a default level of INFO), so we can add this later.
Comment 55 Till Brychcy CLA 2017-03-05 14:22:43 EST
(In reply to Stephan Herrmann from comment #53)
> Do you still want to postpone this decision?

Anyway I'll try if I can add the option within the next two hours!
Comment 56 Stephan Herrmann CLA 2017-03-05 14:36:52 EST
(In reply to Till Brychcy from comment #55)
> (In reply to Stephan Herrmann from comment #53)
> > Do you still want to postpone this decision?
> 
> Anyway I'll try if I can add the option within the next two hours!

cool, promise to quit when it takes longer? :)
Comment 57 Till Brychcy CLA 2017-03-05 15:31:33 EST
(In reply to Stephan Herrmann from comment #56)
> (In reply to Till Brychcy from comment #55)
> > (In reply to Stephan Herrmann from comment #53)
> > > Do you still want to postpone this decision?
> > 
> > Anyway I'll try if I can add the option within the next two hours!
> 
> cool, promise to quit when it takes longer? :)

jdt.core part done, maybe you can already have a look (especially at javadoc and naming) while I'm doing the jdt.ui part?
Comment 58 Till Brychcy CLA 2017-03-05 15:34:09 EST
(In reply to Till Brychcy from comment #57)
> jdt.core part done, maybe you can already have a look (especially at javadoc
> and naming) while I'm doing the jdt.ui part?

Note: I have NOT created a different message for the case when this option is on, I think the current one is ok.
Comment 59 Till Brychcy CLA 2017-03-05 15:51:48 EST
jdt.ui part done :-)
Comment 60 Stephan Herrmann CLA 2017-03-05 17:16:24 EST
(In reply to Till Brychcy from comment #57)
> (In reply to Stephan Herrmann from comment #56)
> > (In reply to Till Brychcy from comment #55)
> > > (In reply to Stephan Herrmann from comment #53)
> > > > Do you still want to postpone this decision?
> > > 
> > > Anyway I'll try if I can add the option within the next two hours!
> > 
> > cool, promise to quit when it takes longer? :)
> 
> jdt.core part done, maybe you can already have a look (especially at javadoc
> and naming) while I'm doing the jdt.ui part?

I've inspected the core part once more, change one option to warning by default (collection w/ heuristics) and made minor modifications here and there.
Will upload in a minute.
Comment 61 Stephan Herrmann CLA 2017-03-05 17:32:02 EST
Ah, and the test failure in #6 is also fixed in #7 :)

You may want to triple check updated documentation in JavaCore.

It seems we can await even one more jenkins build. My previous statement (in some bug) about the build schedule confused the timezones, next I-build should be at 8pm EST.
Comment 62 Stephan Herrmann CLA 2017-03-05 17:41:05 EST
(In reply to Markus Keller from comment #38)
> A fix to add when this is resurrected:
> 
> Javadoc of
> org.eclipse.jdt.core.JavaCore.COMPILER_PB_DISCOURAGED_INVOCATION_ACCEPT_CASTABLE_ARGUMENT
> had typos in "Possible values" and "Default":
> Said e.g. "enable" instead of "enabled".

This same mistake has crept into the new version of that option. I have it fixed locally, but don't want to interrupt / retrigger the jenkins job. Since it's a comment-only change, I'll probably push directly.
Comment 63 Till Brychcy CLA 2017-03-05 17:51:50 EST
(In reply to Stephan Herrmann from comment #61)
> Ah, and the test failure in #6 is also fixed in #7 :)
:-)

> 
> You may want to triple check updated documentation in JavaCore.

I've looked at all your changes, very good!

> 
> It seems we can await even one more jenkins build. My previous statement (in
> some bug) about the build schedule confused the timezones, next I-build
> should be at 8pm EST.

Good. Let's hope there are no random unrelated failures this time :-/

Could you please do the committing, It's getting too late for me...
Comment 64 Stephan Herrmann CLA 2017-03-05 18:11:29 EST
(In reply to Till Brychcy from comment #63)
> Good. Let's hope there are no random unrelated failures this time :-/

If there are random unrelated failures I'll just ignore them :)
(will push directly anyway)
 
> Could you please do the committing, It's getting too late for me...

This tonight, and I'll trigger the two UI changes tomorrow morning.
Comment 66 Stephan Herrmann CLA 2017-03-05 18:48:27 EST
Failure in APIDocumentationTests signalled that our javadoc had unexpected linebreaks. Fixed.
I ran the tests that were skipped on jenkins locally => green.

(In reply to Eclipse Genie from comment #65)
> Gerrit change https://git.eclipse.org/r/91577 was merged to [master].
> Commit:
> http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=e7d6d8a91612da3f3cae0b22ddd55df43d4ee554
> 

Released for 4.7 M6
Comment 67 Till Brychcy CLA 2017-03-06 16:01:23 EST
*** Bug 415306 has been marked as a duplicate of this bug. ***
Comment 68 Till Brychcy CLA 2017-03-07 15:32:35 EST
@Stephan, do you have time to make a N&N entry for this? If not, I can at least make a draft. Obviously you're way better at writing than I am...
Comment 69 Stephan Herrmann CLA 2017-03-07 16:00:57 EST
(In reply to Till Brychcy from comment #68)
> @Stephan, do you have time to make a N&N entry for this? If not, I can at
> least make a draft. Obviously you're way better at writing than I am...

After the deadline is before the deadline, right? :p
So, this is due tomorrow evening, right?

As time is short, maybe we can share the load: if you could provide a nice screenshot by tomorrow early evening, I'll write the text. Deal?
Comment 70 Till Brychcy CLA 2017-03-07 16:05:18 EST
(In reply to Stephan Herrmann from comment #69)
> (In reply to Till Brychcy from comment #68)
> > @Stephan, do you have time to make a N&N entry for this? If not, I can at
> > least make a draft. Obviously you're way better at writing than I am...
> 
> After the deadline is before the deadline, right? :p

:-)

> So, this is due tomorrow evening, right?
> 
> As time is short, maybe we can share the load: if you could provide a nice
> screenshot by tomorrow early evening, I'll write the text. Deal?

Deal!
Comment 71 Eclipse Genie CLA 2017-03-07 17:56:50 EST
New Gerrit change created: https://git.eclipse.org/r/92572
Comment 72 Till Brychcy CLA 2017-03-07 18:00:16 EST
(In reply to Eclipse Genie from comment #71)
> New Gerrit change created: https://git.eclipse.org/r/92572

I had to make multiple screenshots so I have also added a draft text to glue them together. Maybe it is useful, otherwise I won't feel offended if you scrap this and write it completely differently.
Comment 73 Jay Arthanareeswaran CLA 2017-03-08 03:25:54 EST
Verified for 4.7 M6 with build I20170307-2000.

Btw, thanks Stephan and Till for taking this through.
Comment 74 Jay Arthanareeswaran CLA 2017-03-08 11:56:50 EST
For record, I must clarify that I haven't verified the N&N.
Comment 75 Stephan Herrmann CLA 2017-03-08 14:54:55 EST
(In reply to Jay Arthanareeswaran from comment #74)
> For record, I must clarify that I haven't verified the N&N.

Sorry for being late, I'm reviewing / editing the proposed N&N entry only now.
Comment 77 Stephan Herrmann CLA 2017-03-08 17:14:33 EST
(In reply to Eclipse Genie from comment #76)
> Gerrit change https://git.eclipse.org/r/92572 was merged to [master].
> Commit:
> http://git.eclipse.org/c/www.eclipse.org/eclipse/news.git/commit/?id=f101ee5ca04ae5be6a429768e1e1a5efab142d4c
> 

You did ask for an essay, right? :)

https://www.eclipse.org/eclipse/news/4.7/M6/#unlikely-argument-types
Comment 78 Till Brychcy CLA 2017-03-09 01:01:39 EST
(In reply to Stephan Herrmann from comment #77)
> (In reply to Eclipse Genie from comment #76)
> > Gerrit change https://git.eclipse.org/r/92572 was merged to [master].
> > Commit:
> > http://git.eclipse.org/c/www.eclipse.org/eclipse/news.git/commit/?id=f101ee5ca04ae5be6a429768e1e1a5efab142d4c
> > 
> 
> You did ask for an essay, right? :)
> 
> https://www.eclipse.org/eclipse/news/4.7/M6/#unlikely-argument-types

Excellent!
Comment 79 Nobody - feel free to take it CLA 2017-04-07 06:06:06 EDT
I would welcome a second opinion on this 'unlikely argument' warning in the following line regarding a map 'get': https://git.eclipse.org/c/platform/eclipse.platform.ui.git/tree/bundles/org.eclipse.e4.ui.workbench/src/org/eclipse/e4/ui/internal/workbench/ModelAssembler.java#n407

Variable 'importObject' is declared as an EObject (interface) in line 403.
Variable 'importMaps' is declared as an Map<MApplicationElement,MApplicationElement> in line 384 just above (MAE is also an interface).

Now it's guaranteed that whatever is an MApplicationElement is also an EObject (all these guys are EMF classes implementing EObject). In fact if you try to cast 'importObject' to MApplicationElement it will tell you it is unnecessary.

Under these conditions my opinion is to just suppress this warning. Any alternative insight?
Comment 80 Ben Davis CLA 2017-04-07 06:19:26 EDT
The warning is intended for exactly this scenario, since you wouldn't have been able to add the 'EObject' to the map without casting it, and thus, the compiler doesn't know if you are accidentally passing the wrong argument to the 'get' method.

The warning about the cast being unnecessary is happening because the 'get' method is declared to take 'Object' (and this is determined without consideration of the new warning). Maybe it would make sense to suppress the 'unnecessary cast' warning instead in these scenarios?

Thanks by the way to the people who finally implemented this warning - I was one of the people originally begging for it :)
Comment 81 Stephan Herrmann CLA 2017-04-07 15:15:27 EDT
(In reply to Sopot Cela from comment #79)
> Now it's guaranteed that whatever is an MApplicationElement is also an EObject (all these guys are EMF classes implementing EObject).

The fact that all MApplicationElements are also EObjects is developer knowledge, that's not available to the compiler. Fair enough.

However, what you'd need is a guarantee that the EObject 'importObject' definitely is an MApplicationElement. Are you sure about that direction, too?

Otherwise, 'importObject' may not be a "legal" key to begin with. In this case you'd need an additional "if (importObject instanceof MApplicationElement)", as you don't want the newly added cast to blow up at runtime...

Only if you are really worried about one additional pair of instanceof & cast, then adding @SuppressWarnings("unlikely-arg-type") with a comment about your reasoning would be your escape hatchet.

(In reply to Ben Davis from comment #80)
> The warning about the cast being unnecessary is happening because the 'get'
> method is declared to take 'Object' (and this is determined without
> consideration of the new warning). Maybe it would make sense to suppress the
> 'unnecessary cast' warning instead in these scenarios?

=> bug 514956
Comment 82 Luke Usherwood CLA 2018-04-13 04:10:06 EDT
Thanks for the great work here all, this helped us catch a few subtle bugs now!

In Photon M5 I see a "test sources" feature has suddenly popped up. (Yay!) Making use of that could filter-out the remaining (60 or so) false-positives that we see from having this check enabled, as these all arise in test code.

I've logged bug 533534 for this enhancement idea.
Comment 83 Stephan Herrmann CLA 2020-04-05 16:55:25 EDT
*** Bug 451645 has been marked as a duplicate of this bug. ***