Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 348179

Summary: [quick assist] Provide a "Convert 'if-else' to 'switch'" quick assist
Product: [Eclipse Project] JDT Reporter: Deepak Azad <deepakazad>
Component: UIAssignee: Deepak Azad <deepakazad>
Status: VERIFIED FIXED QA Contact:
Severity: enhancement    
Priority: P3 CC: billc.cn, daniel_megert, markus.kell.r, pbenedict, raksha.vasisht
Version: 3.7   
Target Milestone: 4.3 M1   
Hardware: All   
OS: All   
Whiteboard:
Attachments:
Description Flags
Patch_v0.5 none

Description Deepak Azad CLA 2011-06-03 04:55:54 EDT
Currently we have a "Convert 'switch' to 'if-else'" quick assist. Would be useful to have the reverse quick assist as well. Especially if someone wants to use the new strings in switch feature and convert their existing if-else statements to switch. Though we will have to decide how to handle the NPE that can be thrown by the switch statement.
Comment 1 Markus Keller CLA 2011-06-06 10:48:13 EDT
When all the if-expressions are regular "==" checks of the form
"if (key.equals(<constantString>)" for String keys of "if (key == 1)" or
"if (1 == key)", then we don't have any issues with NPEs, not even if "key" has a boxed type.

For "if (<constantString>.equals(key))", the behavior-preserving transformation is to execute the default statements. If they are not null, an extra
"if (key ==null) { <defaultStatements> } else { switch ... }" is necessary (thereby duplicating the default statements).
Comment 2 Raksha Vasisht CLA 2011-07-13 08:41:41 EDT
Created attachment 199573 [details]
Patch_v0.5

Patch under construction.


1)  Conversion if the if-else block contains one or more expressions with either "equals" or "=="  , one of the operand being a constant value or a String Literal
2)  Conversion to switch with Enum constants with Qualified Names
 

Ex that does not work :

            if (TEST.equals(s) || "TEST1".equals(s) || "TEST3".equals(s) || "TEST4".equals(s)) {
			System.out.println("test1"); //$NON-NLS-1$
		} else {
			System.out.println("default"); //$NON-NLS-1$
		}

Need to add support to handle additional operands list in the Infix Expressions.

Released the strings part to BETA_JAVA7.
Comment 3 Deepak Azad CLA 2011-07-27 07:49:52 EDT
Ping!

Will this make it to 3.7.1? We need the N&N by next week.
Comment 4 Markus Keller CLA 2011-07-27 08:05:42 EDT
We'll move this to 3.8.
Comment 5 Dani Megert CLA 2012-02-20 10:05:43 EST
Time permitting for 3.8.
Comment 6 Paul Benedict CLA 2012-06-13 14:21:54 EDT
Due to all the new switch warnings added in 3.8, please make sure the outputted switch won't generate those warnings. For example, always include a default: label?
Comment 8 Deepak Azad CLA 2012-06-14 04:49:49 EDT
(In reply to comment #1)
> For "if (<constantString>.equals(key))", the behavior-preserving transformation
> is to execute the default statements. If they are not null, an extra
> "if (key ==null) { <defaultStatements> } else { switch ... }" is necessary
> (thereby duplicating the default statements).
Oops, I missed this case. Will fix it.

(In reply to comment #6)
> Due to all the new switch warnings added in 3.8, please make sure the outputted
> switch won't generate those warnings. For example, always include a default:
> label?
The default settings/behavior has not changed, so I have not done anything special so far. However, we could probably read the compiler preferences and generate code such that there are no warnings and that all warnings that should be there are there. E.g. unless 'signal even if default case exists' is enabled we should not generate default cases since that would suppress potential 'Incomplete switch cases on enum' warnings.
Comment 9 Paul Benedict CLA 2012-06-14 10:46:50 EDT
Deepak, I noticed all the test cases follow if..else if..else pattern, but none are if..else if without else. In such a situation, will a default: case be generated? And should there be a test for that?
Comment 10 Deepak Azad CLA 2012-06-14 11:20:49 EDT
(In reply to comment #9)
> Deepak, I noticed all the test cases follow if..else if..else pattern, but none
> are if..else if without else. In such a situation, will a default: case be
> generated? 
Nope.

> And should there be a test for that?
I will add a test when I fix the remaining issues.

(In reply to comment #8)
> E.g. unless 'signal even if default case exists' is enabled
> we should not generate default cases since that would suppress potential
> 'Incomplete switch cases on enum' warnings.
Just to be 100% clear, what I meant was if the original if-else construct does not contain a 'default' case, the resultant switch statement will also not have a 'default' case.
Comment 11 Deepak Azad CLA 2012-06-19 07:07:58 EDT
There is no way to completely preserve behavior in snippets where 'one style' w.r.t to <const>.equals(<var>) is not followed consistently.
----------------------------------------
void foo3(String s) {
	if ("aa".equals(s)) {
		
	} else if (s.equals("bb")) {
		
	} else if ("cc".equals(s)) {
		
	}
}
---------------------------------------

Hence, the strategy that I am inclined to follow is that if "if (<constantString>.equals(key))" is encountered even once the result will be of the form "if (key ==null) { <defaultStatements> } else { switch ... }" since (I think) that's what most developers would want.
Comment 12 Markus Keller CLA 2012-06-19 07:49:32 EDT
(In reply to comment #11)
That's OK for me. We could also just not offer the quick assist if the if-else construct is not completely regular.
Comment 13 Paul Benedict CLA 2012-06-19 08:26:23 EDT
It might be beneficial to provide two quick assists if the switch is working on an String/Enum. One that preserves throwing NPE and another that just ignores it. 

switch (s) {
case "a":
case "b":
}

* Convert 'switch' to 'if-else'
if ("a".equals(s)) {
  ... 
} else if ("b".equals(s)) {
  ... 
}

* Convert 'switch' to 'if-else' with null pointer check
if (s == null) {
  throw new NullPointerException();
} else if ("a".equals(s)) {
  ... 
} else if ("b".equals(s)) {
  ... 
}
Comment 14 Markus Keller CLA 2012-06-19 08:35:16 EDT
(In reply to comment #13)
This bug is not about "Convert 'switch' to 'if-else'". Please file a new bug if you think that "Convert 'switch' to 'if-else' with null pointer check" would really be useful.
Comment 15 Deepak Azad CLA 2012-06-19 08:40:20 EDT
Fixed remaining issues and added necessary tests
http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=8cd12906e44758cb73ff76b6b70f3eca52f691d3

(In reply to comment #12)
> (In reply to comment #11)
> That's OK for me. We could also just not offer the quick assist if the if-else
> construct is not completely regular.
I offer the quick assist, since the irregularity is probably a silly error on part of the developer and not offering the quick assist would come in the way.

(In reply to comment #13)
> It might be beneficial to provide two quick assists if the switch is working on
> an String/Enum. One that preserves throwing NPE and another that just ignores
> it. 
Here we are converting if-else-if to switch :-)

In any case, I think we should offer one quick assist which just does the 'right' thing.

Marking this bug as Fixed.
Comment 16 Markus Keller CLA 2012-06-20 12:13:01 EDT
I filed bug 383110 for problems with switch on non-primitive types.
Comment 17 Paul Benedict CLA 2012-06-20 12:26:04 EDT
Thanks Markus. My comment 13, I think, alludes to your desire to make the quick fixes inverses of each other. Glad you picked that out and clarified the boundary conditions.
Comment 18 Deepak Azad CLA 2012-06-20 13:57:24 EDT
(In reply to comment #16)
> I filed bug 383110 for problems with switch on non-primitive types.

Right, so why is this bug reopened? Are there more issues?
Comment 19 Markus Keller CLA 2012-06-20 14:24:39 EDT
> Right, so why is this bug reopened? Are there more issues?

Sorry, it's getting late :-/. I first wanted to reopen this bug, then decided to open a new one, and then added my comment in the tainted browser tab.
Comment 20 Deepak Azad CLA 2012-06-20 14:37:15 EDT
(In reply to comment #19)
> Sorry, it's getting late :-/. I first wanted to reopen this bug, then decided
> to open a new one, and then added my comment in the tainted browser tab.
np :-) Thanks for the reviewing the feature!
Comment 21 Dani Megert CLA 2012-08-07 10:08:43 EDT
Verified in I20120806-2000.