Bug 337977 - [quick fix] Add quick fixes for null annotations
Summary: [quick fix] Add quick fixes for null annotations
Status: RESOLVED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.7   Edit
Hardware: All All
: P2 enhancement (vote)
Target Milestone: 4.3   Edit
Assignee: Stephan Herrmann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on: 186342
Blocks:
  Show dependency tree
 
Reported: 2011-02-23 09:48 EST by Stephan Herrmann CLA
Modified: 2013-05-15 11:10 EDT (History)
8 users (show)

See Also:


Attachments
prototype implementation (46.37 KB, patch)
2011-11-07 18:12 EST, Stephan Herrmann CLA
no flags Details | Diff
new quickfix: extract to null-checked local (35.16 KB, patch)
2012-06-16 21:10 EDT, Stephan Herrmann CLA
no flags Details | Diff
proposed fix for comment 19 (2.70 KB, patch)
2012-06-21 12:03 EDT, Stephan Herrmann CLA
no flags Details | Diff
fix for bug 331649 comment 47 (14.78 KB, patch)
2012-06-27 17:41 EDT, Stephan Herrmann CLA
no flags Details | Diff
fix for bug 331649 comment 47 - v2 (15.35 KB, patch)
2012-06-28 08:16 EDT, Stephan Herrmann CLA
no flags Details | Diff
proposed fix for comment 36 (2.06 KB, patch)
2012-11-13 12:26 EST, 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 Stephan Herrmann CLA 2011-02-23 09:48:48 EST
As work on my prototype re bug 186342 makes progress, quickfixes become
more and more important.

The prototype already contains an initial implementation of four distinct
quickfixes, as described in
 http://wiki.eclipse.org/JDT_Core/Null_Analysis#Cleaning_up

At this point I have some questions regarding the implementation:

1) I noticed that some cross-compilation-unit quickfixes only work if the
affected unit is open in an editor. Debugging the case where the unit
is not open I see the text change created correctly and applied to the
document but it has no visible effect. I took inspiration from classes like
UnresolvedElementsSubProcessor where 
ASTResolving.findCompilationUnitForBinding(..) is used to find the targetCU.
Am I missing anything relating to buffers/working copies etc. to ensure the
operation has an effect?

The relevant part of my code can be found at:
svn://dev.eclipse.org/svnroot/tools/org.eclipse.objectteams/trunk/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/RewriteOperations.java
Look for method findCUForMethod(..) and its applications, especially the 
first one.

2) When providing these fixes as MultiFixes I would like to do some more
checks only when creating the change. At that point I may detect that the
fix should not be applied (because an explicit conflicting annotation
already exists). Performing this check earlier would be (too?) expensive.
But when I don't create a change in the rewrite I see the following exception:
org.eclipse.core.runtime.CoreException: The fix 'Declare method parameter as @Nullable' generated a null change.
	at org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix.createChange(CompilationUnitRewriteOperationsFix.java:105)

Is there any recommended trick for late checks to veto performing a change?


I'd be happy to take comments regarding these proposed quickfixes and perhaps
ideas for more fixes yet to come.

Playing with these quickfixes on real code makes me wish that eventually
a wizard would exist similar to the infer generics wizard. But for now I'm
content with individual quickfixes, given they can be used as MultiFixes.

In my experiments when I applied @NonNullByDefault to the package
org.eclipse.jdt.internal.compiler.lookup the quickfixes let me reduce the
numbers of errors/warnings from 346/1045 to 89/809. Up-to this point applying
the quickfixes was mostly a no-brainer. In order to continue the cleanup
from here on knowledge about design intent is needed, but still quickfixes
can help a lot.
Comment 1 Markus Keller CLA 2011-02-24 11:24:55 EST
> The relevant part of my code can be found at:
For those who don't speak svn:// :
http://dev.eclipse.org/viewcvs/viewvc.cgi/trunk/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/RewriteOperations.java?root=TOOLS_OBJECTTEAMS&view=log

> 1) I noticed that some cross-compilation-unit quickfixes only work if the
> affected unit is open in an editor. Debugging the case where the unit
> is not open I see the text change created correctly and applied to the
> document but it has no visible effect. I took inspiration from classes like
> UnresolvedElementsSubProcessor where 
> ASTResolving.findCompilationUnitForBinding(..) is used to find the targetCU.
> Am I missing anything relating to buffers/working copies etc. to ensure the
> operation has an effect?

I would have to debug the whole thing to be sure what's happening, but I suspect the problem is that you don't properly capture all your changes in one Change object, but you directly manipulate documents outside of the CleanUp infrastructure. UnresolvedElementsSubProcessor is in a different situation, since it runs stand-alone.

Multi-fix Quick Fixes are technically CleanUps which are executed by the clean-up infrastructure. They must only operate on ASTs they get from the infrastructure (since overlapping clean-ups may require multiple passes, etc.). Up to now, I don't think we have any clean-ups that do cross-AST analysis, so I don't know how far you will get without changes to the infrastructure.

> 2) When providing these fixes as MultiFixes I would like to do some more
> checks only when creating the change. [..]
> Is there any recommended trick for late checks to veto performing a change?

I can't recommend anything, since this leads to a bad user experience (quick fix is available but doesn't work). But you can try a hack like org.eclipse.jdt.internal.corext.fix.UnimplementedCodeFix#createAddUnimplementedMethodsFix(..) (see the anonymous CompilationUnitChange).


> Playing with these quickfixes on real code makes me wish that eventually
> a wizard would exist similar to the infer generics wizard. But for now I'm
> content with individual quickfixes, given they can be used as MultiFixes.

You could turn the fixes into full-fledged CleanUps with their own page in the Source > Clean Up... wizard. But be conservative and only do mass-changes if you know that the changes are "right". Otherwise, you will cause MessUps ;-)
Comment 2 Stephan Herrmann CLA 2011-02-25 09:46:31 EST
(In reply to comment #1)
> > The relevant part of my code can be found at:
> For those who don't speak svn:// :

Sorry, my browser speaks svn:// so I didn't notice this could be an issue :)

> > 1) I noticed that some cross-compilation-unit quickfixes only work if the
> > affected unit is open in an editor. Debugging the case where the unit
> > is not open I see the text change created correctly and applied to the
> > document but it has no visible effect. I took inspiration from classes like
> > UnresolvedElementsSubProcessor where 
> > ASTResolving.findCompilationUnitForBinding(..) is used to find the targetCU.
> > Am I missing anything relating to buffers/working copies etc. to ensure the
> > operation has an effect?
> 
> I would have to debug the whole thing to be sure what's happening, but I
> suspect the problem is that you don't properly capture all your changes in one
> Change object, but you directly manipulate documents outside of the CleanUp
> infrastructure. UnresolvedElementsSubProcessor is in a different situation,
> since it runs stand-alone.

OK, my first mistake was in assuming that invoking Ctrl-1 in the editor
would make the fix a stand-alone one, but I now see that even in that mode
I get this call chain (top frame is my code):

RewriteOperations$ParameterAnnotationRewriteOperation.rewriteAST(CompilationUnitRewrite, LinkedProposalModel) line: 193	
CompilationUnitRewriteOperationsFix.createChange(IProgressMonitor) line: 100	
FixCorrectionProposal.createTextChange() line: 155

And indeed already here two different CUs are involved:
FixCorrectionProposal#fCompilationUnit refers to the unit where the problem
was found.
CompilationUnitRewriteOperationsFix#fCompilationUnit refers to the unit where
the change should be applied.

Since both classes are existing classes from JDT/UI I'm not sure what I did
wrong. Maybe it's may invocation of
CompilationUnitRewriteOperationsFix(String, CompilationUnit, CompilationUnitRewriteOperation[])

Does the framework assume that all operations refer to the same CU?
In that case this would indeed be a dead end.

 
> Multi-fix Quick Fixes are technically CleanUps which are executed by the
> clean-up infrastructure. They must only operate on ASTs they get from the
> infrastructure (since overlapping clean-ups may require multiple passes, etc.).
> Up to now, I don't think we have any clean-ups that do cross-AST analysis, so I
> don't know how far you will get without changes to the infrastructure.

So the assumption currently is that the change happens in the same unit
where the problem was found?

So would it be OK if I implement those fixes as standalone ones and ask
someone who knows the infrastructure if this can be converted into a multifix
later?
 
> > 2) When providing these fixes as MultiFixes I would like to do some more
> > checks only when creating the change. [..]
> > Is there any recommended trick for late checks to veto performing a change?
> 
> I can't recommend anything, since this leads to a bad user experience (quick
> fix is available but doesn't work). 

In my case the user would select - say - 10 problems for fixing and then
actually applying the fixes would find out that - say - 2 fixes should not
be applied (because we are trying to add an annotation to a location that
already has as explicit conflicting annotation).
So, normally it wouldn't happen that the fix did nothing, just less than
expected.

> But you can try a hack like
> org.eclipse.jdt.internal.corext.fix.UnimplementedCodeFix#createAddUnimplementedMethodsFix(..)
> (see the anonymous CompilationUnitChange).

Thanks for the pointer. That could help indeed. In my case the decision 
about making a change or not would be made later (during rewriteAST())
but perhaps the same structure will help. I'll have to try.
 
> You could turn the fixes into full-fledged CleanUps with their own page in the
> Source > Clean Up... wizard. But be conservative and only do mass-changes if
> you know that the changes are "right". Otherwise, you will cause MessUps ;-)

Maybe, maybenot. Those fixes are mostly relevant for migrating code from
un-annotated to annotated. During daily work on already-annotated code it's
probably a bad idea to automatically insert more annotations, because new
annotations should reflect new design intent. If arbitrary code changes could
trigger automatically changing existing null contracts those would indeed
be MessUps :)


BTW: is there any chance that JDT/UI might adopt this feature within the
3.7 time frame - assuming the whole concept of null annotations might 
converge within the next few weeks?
Comment 3 Stephan Herrmann CLA 2011-02-26 15:23:00 EST
OK, I tried some tricks to make cross-CU fixes work.

For Ctrl-1 in an editor (i.e., applying one fix at a time) it now works.
I just had to "cheat" on FixCorrectionProposal, which extracts the CU from
the given IInvocationContext (which is the context of the problem location).
By passing a custom context, where getCompilationUnit() answers the target CU
not the problem CU, the infra-structure now uses the correct CU to be modified.

If this solution shall be adopted some time, it would be easiest to add
one more constructor to FixCorrectionProposal that directly takes an
ICompilationUnit instead of the IInvocationContext.


For bulk mode (either from the problem hover or from the Problems view)
I could not get it to work even with small adaptations of CleanUpRefactoring.
From my understanding this would require the following change inside CorrectionMarkerResolutionGenerator.CorrectionMarkerResolution:
Ask "cleanup.getAffectedCU(location)" and then re-arrange the locations 
according to the affected unit instead of the problem unit. This way we could
associate all those problem locations to the same MultiFixTarget that require
changes in the same CU.
Could that make sense?

For the time being I will not support bulk mode for any cross-CU fixes,
although this would be pretty cool in the context of null annotations.

The typical situation here is, if we have a @NonNullByDefault and then we
see a method call
   o.m(null);
This gives an error:
  Null contract violation: passing null to a parameter declared as @NonNull
(the @NonNull being derived from the default).
The quickfix is:
  Declare method parameter as @Nullable
The problem is: m() may be in another compilation unit.
Now, if you have 100 of these errors in the problem view, you don't really
want to fix each single one individually.

The current version can be found as revision 1336 at
http://dev.eclipse.org/viewcvs/viewvc.cgi/trunk/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/?root=TOOLS_OBJECTTEAMS
Comment 4 Stephan Herrmann CLA 2011-02-27 09:26:59 EST
Right now I'm seeing the following artifact when a quickfix modifies another CU:

The target CU is opened in an editor, revealed, changes are applied, the buffer
is marked dirty, BUT I cannot save the buffer (neither using Ctrl-S nor
Shift-Ctrl-S.

The problem goes away once I switch editors.

Are there two different levels of marking a buffer dirty, or what is missing
to enable Ctrl-S on a modified buffer?

Note that this issue occurs after "cheating" as described in the first
para of comment 3.
Comment 5 Markus Keller CLA 2011-03-03 10:11:23 EST
Sorry, I don't have time to look at the problems in detail right now, but here are some preliminary answers:

In an editor, there are two buffers stories involved: ITextFileBuffer holds the content, and ICompilationUnit#becomeWorkingCopy(..) is used to turn a CU into a working copy. Maybe your "cheat" only changed the CU that is used to create an AST (and apply the changes to the file buffer), but it missed the call to becomeWorkingCopy(..), so that the editor doesn't know that the CU is in working copy mode?

Re comment 3:
> Ask "cleanup.getAffectedCU(location)" and then re-arrange the locations 
> according to the affected unit instead of the problem unit. 
From a cursory look, that sounds OK, but you shouldn't add API for this to ICleanUp (rather put it into IMultiFix if possible).
Comment 6 Stephan Herrmann CLA 2011-11-07 18:12:11 EST
Created attachment 206558 [details]
prototype implementation

Here's my prototype adjusted to the latest patch in bug 186342:
attachment 206557 [details].

On top of that patch the quickfix implementation passed the smoke test,
but I'm aware that it still has bugs.

Markus, Deepak: is this something you can build on? Please let me know
if anything is unclear / doesn't work etc.
Comment 7 Markus Keller CLA 2011-12-05 11:55:44 EST
Thanks for the patch, we can definitely build on this. Unfortunately, I ran out of time for a thorough review and integration into the existing quick fix infrastructure for M4.
Comment 8 Markus Keller CLA 2011-12-07 06:18:27 EST
Should also have a quick fix for potential type mismatch (add @NonNull to referenced expression declaration):

	String fString;
	
	void bar(@NonNull String s) {
		@NonNull String s2= copy(s);
		@NonNull String s3= fString;
		@NonNull String s4= " abc ".trim();
		System.out.println(s2 + s3 + s4);
	}
	
	<T> T copy(T arg) {
		return arg;
	}
Comment 9 Stephan Herrmann CLA 2011-12-07 13:53:48 EST
(In reply to comment #8)
> Should also have a quick fix for potential type mismatch (add @NonNull to
> referenced expression declaration):
> 
>     String fString;
> 
>     void bar(@NonNull String s) {
>         @NonNull String s2= copy(s);
>         @NonNull String s3= fString;
>         @NonNull String s4= " abc ".trim();
>         System.out.println(s2 + s3 + s4);
>     }
> 
>     <T> T copy(T arg) {
>         return arg;
>     }

Adding @NonNull to the declaration of copy() is great.
For fString we need bug 331649 before the annotation has an effect.
For String.trim() we need an advanced form of bug 331651 
  (interactively adding annotations to a library).
Apart from these technical constrains: yes, good idea!
Comment 10 Markus Keller CLA 2012-01-23 10:05:40 EST
I'm really sorry I didn't get to this. First M6 item on my plate.
Comment 11 Ayushman Jain CLA 2012-01-25 01:44:25 EST
As an addition to this in the same bug or in a different one, quick fix for the "null pointer access" warning can also be considered.
package p;

public class NullField1 {
 static final Object field1 = null;
 void foo() {
	 Object f = null;
	 f.toString(); // NPE
         field1.toString(); //NPE
 }
}

The quick fix can be "Surround with null check".
Comment 12 Ayushman Jain CLA 2012-03-07 03:38:03 EST
A quick fix for bug 372012 will also be good
Comment 13 Markus Keller CLA 2012-04-29 09:07:16 EDT
http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=f63a426a0aa77b391bf2116dcd4eb3749abad092

Sorry, I didn't have time for a thorough review. But since the implementation didn't look harmful and it is only active if null-annotation-related problem has been found, I just released it, so that we have at least those quick fixes.

Keeping this bug open for the real review and the missing quick fixes.
Comment 14 Deepak Azad CLA 2012-04-29 11:27:58 EDT
(In reply to comment #13)
Target milestone is set to 4.3 but I am hoping that fixing some of the obvious problems is on the plan for 3.8. e.g.

	void foo2(Object o){
		@NonNull Object x = new Object();
		x=o;  // QF here
	}
Comment 15 Deepak Azad CLA 2012-04-30 04:33:18 EDT
I also do not like the wording of the following QFs
>Declare method parameter as @{0}
>Declare method return as @{0}
>Adjust overridden method from {1}, mark parameter as @{0}
>Adjust overridden method from {1}, mark as returning @{0}

I would change these to be consistent with other existing QFs
Change type of parameter to ''@{0} {1}''
Change return type of ''{0}(..)'' to ''@{1} {2}''
Change type of parameter in overridden method to ''@{0} {1}''
Change return type of overridden ''{0}(..)'' to ''@{1} {2}''
Comment 16 Deepak Azad CLA 2012-04-30 05:56:45 EDT
I released a few changes - mostly formatting.
http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=b85527e60dfbcc11c80a8d923b355c68ffbcf6e8

This also fixes the problem from comment 14, though the multi-fix for this still does not work.
Comment 17 Deepak Azad CLA 2012-04-30 08:02:12 EDT
http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=3ac97c9a6c8b51550d0788d291ff25348e271424

- Tweaked the wording of QF messages
- Fix few problems in multi-fix.

Don't feel like making any more changes before writing some tests first, and not much time left for that.
Comment 18 Stephan Herrmann CLA 2012-06-16 21:10:19 EDT
Created attachment 217460 [details]
new quickfix: extract to null-checked local

Here's one more quick fix: when dereferencing a @Nullable field:
- extract the field reference to a new local variable
- add a null check surrounding the dereference (both now using the local)

This is actually a matured version of an example from the JDT tutorial
at this year's ECNA :)  Thanks Ayush for the start!


I'm intending to include this in the patch feature on behalf of bug 331649.

At last this patch also contains some tests (for the new quickfix only).

The quickfix considers the following:
- statement inside a block / with no directly enclosing block
- don't propose the quickfix inside a field initializer
- don't propose names that are already in use in the current method/initializer
- always add an else block with a TODO comment
- if the statement to protect is a return, also add a return to the else block
- handle various kinds of types
  - reference / primitive
  - arrays
  - parameterized types
- various shapes of field references (single / qualified / this-qualified)
- field reference inside structures like ternary, assignment-as-expression
- dereference of array, indexed or ".length"


I'm not sure if we can/should go into suggesting an if statement that covers
several dereferences of the same field? Perhaps for subsequent occurrences
we could propose to extend coverage of an existing if statement..
Comment 19 Stephan Herrmann CLA 2012-06-21 09:58:39 EDT
The quick fix "Change return type of 'foo(..)' to '@NonNull' is currently not offered for IProblem.RequiredNonNullButProvidedSpecdNullable.

This is inconsistent.
Comment 20 Stephan Herrmann CLA 2012-06-21 12:03:03 EDT
Created attachment 217707 [details]
proposed fix for comment 19

(In reply to comment #19)
> The quick fix "Change return type of 'foo(..)' to '@NonNull' is currently not
> offered for IProblem.RequiredNonNullButProvidedSpecdNullable.
> 
> This is inconsistent.

Apparently, the quick fixes just haven't been updated for bug 365859.
For RequiredNonNullButProvidedSpecdNullable the fix is straight forward
as the attached patch shows.

RedundantNullCheckOnSpecdNonNullLocalVariable and
SpecdNonNullLocalVariableComparisonYieldsFalse are, however, not covered 
by the patch, since more investigation is required here.
Comment 21 Markus Keller CLA 2012-06-21 12:43:48 EDT
(In reply to comment #20)
Thanks, released to master with http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=3288d85e7f4f1b65d75ac4a5cbab9095933862b4
Comment 22 Stephan Herrmann CLA 2012-06-21 12:47:38 EDT
(In reply to comment #21)
> (In reply to comment #20)
> Thanks, released to master with
> http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=3288d85e7f4f1b65d75ac4a5cbab9095933862b4

Thanks!

BTW: I'm planning to write a bunch of JUnits during the next days.
If anybody has already started on this task please share it here.
Comment 23 Ayushman Jain CLA 2012-06-25 02:10:27 EDT
(In reply to comment #18)
> Created attachment 217460 [details]
> new quickfix: extract to null-checked local

Since this related to null annotation support for fields which is to be released only in a branch for now, perhaps we can put this patch and any other UI patches for the branch in a different bug to avoid getting these into master at this point, and letting this bug be fixed for the null annotation support for master.
Comment 24 Stephan Herrmann CLA 2012-06-26 08:16:48 EDT
Comment on attachment 217460 [details]
new quickfix: extract to null-checked local

(In reply to comment #23)
> (In reply to comment #18)
> > Created attachment 217460 [details]
> > new quickfix: extract to null-checked local
> 
> Since this related to null annotation support for fields which is to be
> released only in a branch for now, perhaps we can put this patch and any other
> UI patches for the branch in a different bug to avoid getting these into master
> at this point, and letting this bug be fixed for the null annotation support
> for master.

This patch (from comment 18) has been moved to its own bug: bug 383540.
Comment 25 Stephan Herrmann CLA 2012-06-27 17:41:45 EDT
Created attachment 217971 [details]
fix for bug 331649 comment 47

In bug 331649 comment 47 Ayush reported that in one situation a wrong quick fix is offered: change @Nullable argument to @Nullable.

The patch fixes this by taking one more parameter into consideration: is the problem reported against an argument or against the return?

Tests in this patch add to the new test class from bug 383540.
Comment 26 Stephan Herrmann CLA 2012-06-28 08:02:37 EDT
Using the test project from bug 331649 comment 47 I observed:

The same problem is reported for three locations:

  Null type mismatch: required '@NonNull Object' but the provided value is specified as @Nullable

Now, the hover suggests to "Fix 3 problems of same category in file"

If I click that link an InternalError is logged:

  The fix 'Add Annotations' generated a null change.

Debugging the situation I find that the fixes shy out, because in multi-fix
mode they are programmed (by me :) ) never to remove existing annotations.
In single mode they would perform the desired changes but in multi mode
the produce no change. The distinction is made lazily, assuming it would
be too expensive to perform during canFix().

Question: can I avoid logging this error if a multi-fix finds that it should
better not perform any changes to avoid doing a mess-up?
Comment 27 Stephan Herrmann CLA 2012-06-28 08:09:52 EDT
Also: when trying to write tests for a multi-fix I couldn't find any
example in jdt.ui.tests. Do we have some test infrastructure for testing
the "Fix 3 problems of same category in file" kind of action?

Note, that my MultiFixes are not registered as cleanup, because fully
automatic operation is not desired at this point, but interactively
performing several fixes of a similar kind with one click is desired.
Comment 28 Stephan Herrmann CLA 2012-06-28 08:16:23 EDT
Created attachment 218014 [details]
fix for bug 331649 comment 47 - v2

This is a more complete version of the patch from comment 25:
also consider MultiFix scenarios. 

Tests pending, see previous comment.
Comment 29 Deepak Azad CLA 2012-07-18 08:33:10 EDT
(In reply to comment #27)
> Also: when trying to write tests for a multi-fix I couldn't find any
> example in jdt.ui.tests. Do we have some test infrastructure for testing
> the "Fix 3 problems of same category in file" kind of action?
Don't think so. We test the cleanups or the quick fixes, but not multi-fixes. It should be OK for now if we have tests only for quick fixes.

(In reply to comment #28)
> Created attachment 218014 [details] [diff]
> fix for bug 331649 comment 47 - v2

Stephan, can you please include the complete test class in this patch with only the tests that are relevant to this bug. Once the tests are released others would feel more comfortable in tweaking the already released quick fixes.
Comment 30 Deepak Azad CLA 2012-07-18 09:16:19 EDT
http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=f8cc4fcc9e2320a86477888c90a3cf2cc70e16bb

(In reply to comment #28)
> Created attachment 218014 [details] [diff]
> fix for bug 331649 comment 47 - v2
Released the patch minus the tests.

Also merged NullFixMessages to MultiFixMessages.
Comment 31 Stephan Herrmann CLA 2012-07-18 12:43:04 EDT
(In reply to comment #29)
> (In reply to comment #27)
> > Also: when trying to write tests for a multi-fix I couldn't find any
> > example in jdt.ui.tests. Do we have some test infrastructure for testing
> > the "Fix 3 problems of same category in file" kind of action?
> Don't think so. We test the cleanups or the quick fixes, but not multi-fixes.
> It should be OK for now if we have tests only for quick fixes.

This just means, we can't test the situation where the same fix behaves differently in both modes. In my case I want to be less agressive in multi mode than in single mode.
 
> (In reply to comment #28)
> > Created attachment 218014 [details] [diff]
> > fix for bug 331649 comment 47 - v2
> 
> Stephan, can you please include the complete test class in this patch with only
> the tests that are relevant to this bug.

The test class was initially attached in comment 18 as attachment 217460 [details].
While the patch has been moved to bug 383540 the test infrastructure is the same. I currently don't have a backported version. Would you mind extracting just the common parts from that patch?

> Once the tests are released others
> would feel more comfortable in tweaking the already released quick fixes.

Sure. I'm looking forward to your tweaks :)
Comment 32 Deepak Azad CLA 2012-07-19 02:05:46 EDT
(In reply to comment #31)
> While the patch has been moved to bug 383540 the test infrastructure is the
> same. I currently don't have a backported version. Would you mind extracting
> just the common parts from that patch?
Ok, I released the tests. Some tests had to be updated because they relied on annotations for fields feature which is not yet in JDT. I also added a few tests from my side.

http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=b5df3f3ea070137fe77b53c4230959ac339f87cd

Stephan, I noticed that no quick fixes are offered for fixing incompatible annotations on local variables. Is that deliberate or you just did not implement them as yet ?

e.g.
	void foo() {
		@NonNull Exception e = new Exception();
		@Nullable Exception e1 = null;
		e=e1; // QF here
	}
Comment 33 Deepak Azad CLA 2012-07-20 12:48:16 EDT
http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=a70adae0e222ad35b0a5dc6f3c5fb8df188f15a6

I moved/refactored/renamed a bit of code to fit existing code patterns/styles...

http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=35d9730f62138a820e6ba6509a54247827c91f77

Then I also added a quick fix to remove redundant nullness annotations. This multi-fix is especially handy when you start by adding @NonNull annotations everywhere but then start using @NonNullByDefault annotation. (A sort of a baby step towards fixing bug 378724)
Comment 34 Stephan Herrmann CLA 2012-07-21 07:18:35 EDT
Thanks, Deepak, for taking this forward.

(In reply to comment #32)
> Stephan, I noticed that no quick fixes are offered for fixing incompatible
> annotations on local variables. Is that deliberate or you just did not
> implement them as yet ?
> 
> e.g.
>     void foo() {
>         @NonNull Exception e = new Exception();
>         @Nullable Exception e1 = null;
>         e=e1; // QF here
>     }

Just haven't implemented as I started with signature-level issues. QF for LHS is always good. QF for RHS needs of course some more analysis.
Comment 35 Deepak Azad CLA 2012-07-21 09:47:31 EDT
(In reply to comment #33)
> Then I also added a quick fix to remove redundant nullness annotations. 
I had missed adding quick fixes for removing redundant *default* annotations.

http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=7bb0fd23fd2aab330efc99f68a43869e09fc81c4
Comment 36 Stephan Herrmann CLA 2012-11-13 12:19:30 EST
See bug 383371 comment 37:

After promising to insert "@Nullable" (good) a multi fix actually inserts "@NonNull" (bad).

Patch coming in a second.

@Markus: do you have a quick pointer where I should add corresponding tests for multi fixes?
Comment 37 Stephan Herrmann CLA 2012-11-13 12:26:45 EST
Created attachment 223525 [details]
proposed fix for comment 36

Here's the simple patch for the issue in comment 36.

From two similar methods (single fix vs. multi fix) the former has a parameter 'modifyOverridden'. The latter must instead constantly assume false, which it failed to do for two problem IDs - hence the wrong operation despite the correct label.

The second change in the patch regards the name of the multi fix as it will be shown after 'Undo' (but still affected by bug 394206).
Comment 38 Stephan Herrmann CLA 2013-01-24 12:45:29 EST
(In reply to comment #18)
> Created attachment 217460 [details]
> new quickfix: extract to null-checked local
> 
> Here's one more quick fix: when dereferencing a @Nullable field:
> - extract the field reference to a new local variable
> - add a null check surrounding the dereference (both now using the local)
> 
> This is actually a matured version of an example from the JDT tutorial
> at this year's ECNA :)  Thanks Ayush for the start!
> 
> 
> I'm intending to include this in the patch feature on behalf of bug 331649.
> 
> At last this patch also contains some tests (for the new quickfix only).
> 
> The quickfix considers the following:
> - statement inside a block / with no directly enclosing block
> - don't propose the quickfix inside a field initializer
> - don't propose names that are already in use in the current method/initializer
> - always add an else block with a TODO comment
> - if the statement to protect is a return, also add a return to the else block
> - handle various kinds of types
> - reference / primitive
> - arrays
> - parameterized types
> - various shapes of field references (single / qualified / this-qualified)
> - field reference inside structures like ternary, assignment-as-expression
> - dereference of array, indexed or ".length"
> 
> 
> I'm not sure if we can/should go into suggesting an if statement that covers
> several dereferences of the same field? Perhaps for subsequent occurrences
> we could propose to extend coverage of an existing if statement..

Now that bug 331649 is released, I've moved this part (with further improvements) to a new bug 398995.
Comment 39 Stephan Herrmann CLA 2013-04-06 19:51:07 EDT
(In reply to comment #20)
> RedundantNullCheckOnSpecdNonNullLocalVariable and
> SpecdNonNullLocalVariableComparisonYieldsFalse are, however, not covered 
> by the patch, since more investigation is required here.

These are covered by the patch in bug 405086.
Comment 40 Dani Megert CLA 2013-04-24 11:14:05 EDT
I think we should close this bug and have separate bugs for additional/new quick fixes.
Comment 41 Markus Keller CLA 2013-05-15 11:10:06 EDT
(In reply to comment #40)
> I think we should close this bug and have separate bugs for additional/new
> quick fixes.

I agree, closing.