Bug 105668 - [quick fix] place @SuppressWarnings on variables
Summary: [quick fix] place @SuppressWarnings on variables
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P3 normal with 4 votes (vote)
Target Milestone: 3.6 RC1   Edit
Assignee: Raksha Vasisht CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 100444 190678 190701 299812 (view as bug list)
Depends on: 311849
Blocks:
  Show dependency tree
 
Reported: 2005-08-01 09:57 EDT by Adam Kiezun CLA
Modified: 2010-05-17 06:42 EDT (History)
11 users (show)

See Also:
raksha.vasisht: review+


Attachments
Patch (5.00 KB, patch)
2010-04-22 14:46 EDT, Raksha Vasisht CLA
no flags Details | Diff
Patch_v2 + tests (27.58 KB, patch)
2010-05-07 09:06 EDT, Raksha Vasisht CLA
no flags Details | Diff
Fix v3 (28.37 KB, patch)
2010-05-11 13:11 EDT, Markus Keller CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Adam Kiezun CLA 2005-08-01 09:57:58 EDT
3.1
add an option to place the @SuppressWarning annotation in the smallest scope you
can.

Currently, it never adds it to local variables or parameters but places them on
the method which is potentially dangerous because it hides all warnings in the
method
Comment 1 Dirk Baeumer CLA 2005-08-02 05:14:47 EDT
We add the suppress warning annotation to the local declaration if the warning
affects the declaration itself and not some code in the initialization. For example

	void foo() {
		int i;
	}

results in 

	void foo() {
		@SuppressWarnings("unused") int i;
	}

However,

	void foo() {
		int i= bar();
	}

with a problem on bar results in putting the supress warning annotation to the
method since you normally can't annotate expressions/statements. This is to make
this consistent with other cases where statements or expression cause problems.

Martin, additional comments ?
Comment 2 Martin Aeschlimann CLA 2005-08-02 06:47:29 EDT
This was done intentionally. The question is, should an annotion of a local
really include its initializer?
The spec that says something like: it covers the full source range of the
declaration. In an AST technical way, the initializer is part of the declaration,
so jdt-core included the initializer.

In our understanding, an annotation is most comparable to a modifier. A modifier
for a field like 'private', 'final', 'transient' doesn't relate to the
initializer, just to the declaration of the field.

final int i= j; doesn't mean that also j is final.

Should it really make a difference if your write:
 int i; i= j;
or
 int i= j;

As quick fix makes it easy to add new suppresswarnings, we don't want to support
'bad practice', thus we prefer to put the annotations to a place where whe think
they make more sense.
As soon as annotations are used more widely and spec'ed better we will change
this of course.


Comment 3 Martin Aeschlimann CLA 2007-06-04 04:44:31 EDT
*** Bug 190678 has been marked as a duplicate of this bug. ***
Comment 4 Martin Aeschlimann CLA 2007-06-04 04:46:10 EDT
*** Bug 190701 has been marked as a duplicate of this bug. ***
Comment 5 Genady Beryozkin CLA 2007-06-04 05:28:38 EDT
(In reply to comment #2)
> As soon as annotations are used more widely and spec'ed better we will change
> this of course.

Can it be reopened for 3.4? 

P.S. The bug subject should be "SuppressWarning*s*" (for better searchability).
Comment 6 Sebastian Davids CLA 2007-06-04 05:42:19 EDT
bug 105668 is about a class' static finals aka constants.

I do not consider it a "best practice" to turn off all warnings for a class just because one of it's constants is using deprecated API.

I see your reasoning for locals and methods but not for class-level constructs  (e.g. initializers.)

Also, most programmers are not aware that one can put the @SuppressWarnings annotation not only at the method/class level put also at field/local level.

I second the minimal scope approach.
Comment 7 Martin Aeschlimann CLA 2007-06-04 05:45:19 EDT
reopen for 3.4
Comment 8 Neil Bartlett CLA 2008-05-28 03:59:04 EDT
I think this is important to fix in the case of @SuppressWarnings("unchecked"), which should be added at the smallest possible scope. For example:

  public void foo(List<?> unknownList) {
    List<String> stringList = (List<String>) unknownList;
    // ...
  }

This correctly gives an "unchecked cast" warning on the cast to List<String>, which we can suppress if we know that unknownList really is of type List<String>. But the quick fix puts @SuppressWarnings("unchecked") on the whole foo method rather than on the declaration of the stringList variable. As a result, any other unchecked warnings in the foo method will be masked, including ones that highlight genuine type safety problems that should not be suppressed.

I.e. the correct quick fix should be:

  public void foo(List<?> unknownList) {
    @SuppressWarnings("unchecked")
    List<String> stringList = (List<String>) unknownList;
    // ...
  }

But the quick fix offered is:

  @SuppressWarnings("unchecked")
  public void foo(List<?> unknownList) {
    List<String> stringList = (List<String>) unknownList;
    // ...
  }
Comment 9 Ed Merks CLA 2008-08-06 10:43:10 EDT
I totally agree with Neil's comment.  The best possible fix should be suggested and in my opinion that's the one with the narrowest scope.  When migrating EMF 2.3 to Java 5.0 this shortcoming was annoying and in fact I didn't know until well into the process that I could put an annotation on any declaration, including one nested in a method body.
Comment 10 Markus Keller CLA 2009-09-09 08:36:17 EDT
I'll try to get this in for 3.6 (a patch would always be welcome ;-).
Comment 11 Markus Keller CLA 2009-09-24 10:44:39 EDT
*** Bug 100444 has been marked as a duplicate of this bug. ***
Comment 12 Markus Keller CLA 2009-09-24 10:59:50 EDT
I agree with the "narrowest scope" arguments. I have just one caveat for warnings in method parameter lists, e.g. with "Usage of raw types" set to "Warning":

    public void foo(ArrayList unknownList, Callable c) {
    }

=> Do you really want the @SuppressWarnings(..) for the raw types to be added for each parameter separately? Or would you expect two fix proposals, one for "foo()" and one for "unknownList" in this case? Other cases where you'd expect two proposals?

Note: Bug 290034 has changed the token for suppressing raw types from "unchecked" to "rawtypes".
Comment 13 Markus Keller CLA 2010-01-25 07:57:41 EST
*** Bug 299812 has been marked as a duplicate of this bug. ***
Comment 14 Markus Keller CLA 2010-01-25 07:59:00 EST
Bug 299812 has a patch, but the questions from comment 12 need to be answered first.
Comment 15 G. Ralph Kuntz, MD CLA 2010-01-25 08:12:04 EST
(In reply to comment #12)
> I agree with the "narrowest scope" arguments. I have just one caveat for
> warnings in method parameter lists, e.g. with "Usage of raw types" set to
> "Warning":
> 
>     public void foo(ArrayList unknownList, Callable c) {
>     }
> 
> => Do you really want the @SuppressWarnings(..) for the raw types to be added
> for each parameter separately? Or would you expect two fix proposals, one for
> "foo()" and one for "unknownList" in this case? Other cases where you'd expect
> two proposals?
> 
> Note: Bug 290034 has changed the token for suppressing raw types from
> "unchecked" to "rawtypes".

I would expect separate @SuppressWarnings(..) on each parameter. The annotation should cover the SMALLEST scope possible.
Comment 16 Markus Keller CLA 2010-01-25 09:43:59 EST
> I would expect separate @SuppressWarnings(..) on each parameter. The annotation
> should cover the SMALLEST scope possible.

I agree with that, but I think for problems in a method signature, we also need to keep the quick fix that adds @SuppressWarnings to the method. Otherwise, it e.g. becomes cumbersome if you have a method with several raw parameters and you want to suppress them all at once. If we ONLY offer the smallest scope, we take away the existing support for such scenarios.
Comment 17 G. Ralph Kuntz, MD CLA 2010-01-25 09:50:21 EST
(In reply to comment #16)
> > I would expect separate @SuppressWarnings(..) on each parameter. The annotation
> > should cover the SMALLEST scope possible.
> 
> I agree with that, but I think for problems in a method signature, we also need
> to keep the quick fix that adds @SuppressWarnings to the method. Otherwise, it
> e.g. becomes cumbersome if you have a method with several raw parameters and
> you want to suppress them all at once. If we ONLY offer the smallest scope, we
> take away the existing support for such scenarios.

I did not mean to imply that the old @SuppressWarnings at the method-level be removed, only that an alternative be added to do so at the smallest scope possible (both should be offered).
Comment 18 Markus Keller CLA 2010-01-25 10:10:28 EST
(In reply to comment #17)
> I did not mean to imply that the old @SuppressWarnings at the method-level be
> removed, only that an alternative be added to do so at the smallest scope
> possible (both should be offered).

OK, so we're on the same side.
Comment 19 Markus Keller CLA 2010-04-20 06:39:08 EDT
General rules:
- show a quick fix for the smallest possible scope
- show a quick fix for the smallest enclosing IMember
- if the 2 targets are not the same, make the smaller scope more relevant

Here's an example with expected quick fixes after each problem:

import java.util.*;

public class Try {
    List
// Add @SuppressWarnings 'rawtypes' to 'myField'
        myField = new ArrayList();
// Add @SuppressWarnings 'rawtypes' to 'myField'
    
    List<String> myList = new 
// Add @SuppressWarnings 'unchecked' to 'myList'
        ArrayList
// Add @SuppressWarnings 'rawtypes' to 'myList'
            ();
    
    
    List
// Add @SuppressWarnings 'rawtypes' to 'm'
    
        m(List
// Add @SuppressWarnings 'rawtypes' to 'param'
// Add @SuppressWarnings 'rawtypes' to 'm'
            param) {
        
                new ArrayList();
// Add @SuppressWarnings 'rawtypes' to 'm'
        
                ArrayList
// Add @SuppressWarnings 'rawtypes' to 'local'
// Add @SuppressWarnings 'rawtypes' to 'm'
        
                    local
// Add @SuppressWarnings 'unused' to 'local'
// Add @SuppressWarnings 'unused' to 'm'
            
                        = new ArrayList();
// Add @SuppressWarnings 'rawtypes' to 'local'
// Add @SuppressWarnings 'rawtypes' to 'm'
        
                param.add(null);
// Add @SuppressWarnings 'unchecked' to 'm'
        
                return param;
    }
}
Comment 20 Raksha Vasisht CLA 2010-04-22 14:46:35 EDT
Created attachment 165827 [details]
Patch

(In reply to comment #19)

Here'a patch(built from Olivier's patch in bug 299812) working with the smallest scope for local variables, field variables and method parameters as shown by the above example. I am having some problems running the tests, so attached here's just the fix. Will update with the tests soon.
Comment 21 Markus Keller CLA 2010-04-23 10:27:04 EDT
Tests that fail with Patch 1:
org.eclipse.jdt.ui.tests.quickfix.LocalCorrectionsQuickFixTest
org.eclipse.jdt.ui.tests.quickfix.ModifierCorrectionsQuickFixTest


The methods you added in ASTResolving are not good. You could do what they do with ASTResolving#findAncestor(ASTNode, int), so these implementations are redundant and should be removed.

Even worse is that they don't follow the pattern of the other #find*(..) methods: The existing methods all make sure that the traversal does not leave the scope of the given node. They e.g. return null if no parent could be found, and don't return an arbitrary node of the right type anywhere in the parent chain (e.g. if the given node is used somewhere inside an anonymous type inside a variable initializer).


For this bug, you should implement the traversal along the ASTNode#getParent() chain manually. At each level, check whether the node could carry an annotation.

Here are some more tricky cases:
    void m() {
	    final Object object= new Object() {
			{
				for (List l= new ArrayList(), x= new Vector(); ;) {
					// proposals for 'l' and for 'object'
					if (l == x)
						break;
				}
			}
		};
		final Runnable r= new Runnable() {
			public void run() {
				boolean b;
				for (b= new ArrayList().add(1); ;) {
					// proposals for 'run'
					if (b) return;
				}
			}
		};
    }
Comment 22 Raksha Vasisht CLA 2010-05-07 09:06:18 EDT
Created attachment 167465 [details]
Patch_v2 + tests

Added a check for BodyDeclaration inside the findParentVariableDeclaration method that returns null if its not any kind of variable declaration in the scope of the node ensuring that some arbitrary node in the parent chain of the right type is not returned. Also added code to show proposals for anonymous class declarations and expressions inside for loops and some tests for all cases. Markus, could you pls have a look?
Comment 23 Markus Keller CLA 2010-05-11 13:11:35 EDT
Created attachment 167978 [details]
Fix v3

Sorry, patch v2 was too complicated for me (too many cases to consider...).

Here's a version that keeps all the logic about what nodes can have SuppressWarnings annotations in one place (in the switch statement we already had). That will also make it easier to add new targets when jsr308 adds annotations in more places.

The tests are great! I only had to added a third case to
ModifierCorrectionsQuickFixTest.testSuppressWarningsAnonymousClass1(), since this patch always adds a proposal for the enclosing member and for all locals in between (patch v2 only added 2 proposals to the closest local variables).
Comment 24 Markus Keller CLA 2010-05-11 13:12:48 EDT
Raksha, could you please review Fix v3?
Comment 25 Raksha Vasisht CLA 2010-05-12 09:26:58 EDT
(In reply to comment #24)
> Raksha, could you please review Fix v3?

Yep, the code looks simpler after removing existing if statements for Variable declarations. Rest looks good. 
+1
Comment 26 Markus Keller CLA 2010-05-12 09:37:10 EDT
Fixed in HEAD.
Comment 27 Deepak Azad CLA 2010-05-17 06:42:27 EDT
Verified for 3.6 RC1 with build I20100516-0800.