Skip to content

Commit 411d55e

Browse files
authored
Merge pull request #8860 from mbien/add-default-case-hint
Add hint to resolve "switch does not cover all input values" javac errors
2 parents 7d46e6f + c7e40d7 commit 411d55e

File tree

14 files changed

+348
-132
lines changed

14 files changed

+348
-132
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.netbeans.modules.java.hints.errors;
20+
21+
import com.sun.source.tree.BreakTree;
22+
import com.sun.source.tree.CaseTree;
23+
import com.sun.source.tree.CaseTree.CaseKind;
24+
import com.sun.source.tree.ExpressionTree;
25+
import com.sun.source.tree.ParenthesizedTree;
26+
import com.sun.source.tree.StatementTree;
27+
import com.sun.source.tree.SwitchExpressionTree;
28+
import com.sun.source.tree.SwitchTree;
29+
import com.sun.source.tree.Tree;
30+
import com.sun.source.util.SourcePositions;
31+
import com.sun.source.util.TreePath;
32+
import java.util.ArrayList;
33+
import java.util.List;
34+
import java.util.Set;
35+
import org.netbeans.api.java.source.CompilationInfo;
36+
import org.netbeans.api.java.source.TreeMaker;
37+
import org.netbeans.api.java.source.WorkingCopy;
38+
import org.netbeans.modules.java.hints.spi.ErrorRule;
39+
import org.netbeans.spi.editor.hints.Fix;
40+
import org.netbeans.spi.java.hints.JavaFix;
41+
import org.openide.util.NbBundle;
42+
43+
/**
44+
* Resolves javac error by adding a missing default case to a switch.
45+
*
46+
* @author SANDEEMI
47+
*/
48+
@NbBundle.Messages("FIX_Add_Default_Case=Add Default Case")
49+
public class AddDefaultCase implements ErrorRule<Void> {
50+
51+
private static final Set<String> CODES = Set.of(
52+
"compiler.err.not.exhaustive",
53+
"compiler.err.not.exhaustive.statement"
54+
);
55+
56+
private static final String THROW_ISE = "throw new IllegalStateException(\"Unexpected value: \" + %1$s);";
57+
58+
@Override
59+
public Set<String> getCodes() {
60+
return CODES;
61+
}
62+
63+
@Override
64+
public List<Fix> run(CompilationInfo info, String diagnosticKey, int offset, TreePath treePath, Data<Void> data) {
65+
return List.of(new AddDefaultCaseFix(info, treePath).toEditorFix());
66+
}
67+
68+
@Override
69+
public String getId() {
70+
return AddDefaultCase.class.getName();
71+
}
72+
73+
@Override
74+
public String getDisplayName() {
75+
return Bundle.FIX_Add_Default_Case();
76+
}
77+
78+
@Override
79+
public void cancel() {
80+
81+
}
82+
83+
private static final class AddDefaultCaseFix extends JavaFix {
84+
85+
public AddDefaultCaseFix(CompilationInfo info, TreePath path) {
86+
super(info, path);
87+
}
88+
89+
@Override
90+
protected String getText() {
91+
return Bundle.FIX_Add_Default_Case();
92+
}
93+
94+
@Override
95+
protected void performRewrite(TransformationContext ctx) throws Exception {
96+
WorkingCopy wc = ctx.getWorkingCopy();
97+
TreeMaker make = wc.getTreeMaker();
98+
TreePath path = ctx.getPath();
99+
100+
switch (path.getLeaf().getKind()) {
101+
case SWITCH_EXPRESSION -> {
102+
SwitchExpressionTree expTree = (SwitchExpressionTree) path.getLeaf();
103+
List<? extends CaseTree> cases = expTree.getCases();
104+
if (cases.isEmpty()) {
105+
return;
106+
}
107+
ParenthesizedTree expression = (ParenthesizedTree) expTree.getExpression();
108+
109+
String text = THROW_ISE.formatted(expression.toString());
110+
StatementTree parseStatement = wc.getTreeUtilities().parseStatement(text, new SourcePositions[1]);
111+
CaseTree caseSwitchPatterns;
112+
if (cases.get(0).getCaseKind() == CaseKind.RULE) {
113+
caseSwitchPatterns = make.CasePatterns(List.of(), parseStatement);
114+
} else {
115+
caseSwitchPatterns = make.CasePatterns(List.of(), List.of(parseStatement));
116+
}
117+
118+
List<CaseTree> newCases = new ArrayList<>(cases.size() + 1);
119+
newCases.addAll(cases);
120+
newCases.add(caseSwitchPatterns);
121+
Tree switchExpression = make.SwitchExpression(expression, newCases);
122+
wc.rewrite(expTree, switchExpression);
123+
}
124+
case SWITCH -> {
125+
SwitchTree switchTree = (SwitchTree) path.getLeaf();
126+
List<? extends CaseTree> cases = switchTree.getCases();
127+
if (cases.isEmpty()) {
128+
return;
129+
}
130+
ExpressionTree expression = ((ParenthesizedTree) switchTree.getExpression()).getExpression();
131+
132+
String text = THROW_ISE.formatted(expression.toString());
133+
StatementTree parseStatement = wc.getTreeUtilities().parseStatement(text, new SourcePositions[1]);
134+
CaseTree caseSwitchPatterns;
135+
if (cases.get(0).getCaseKind() == CaseKind.RULE) {
136+
caseSwitchPatterns = make.CasePatterns(List.of(), parseStatement);
137+
} else {
138+
handleLastCase(cases.get(cases.size() - 1), wc, make);
139+
caseSwitchPatterns = make.CasePatterns(List.of(), List.of(parseStatement));
140+
}
141+
142+
SwitchTree insertSwitchCase = make.addSwitchCase(switchTree, caseSwitchPatterns);
143+
wc.rewrite(switchTree, insertSwitchCase);
144+
}
145+
default -> throw new UnsupportedOperationException(path.getLeaf().getKind() + " not implemented");
146+
}
147+
}
148+
149+
private static void handleLastCase(CaseTree lastCase, WorkingCopy wc, TreeMaker make) {
150+
List<? extends StatementTree> statements = lastCase.getStatements();
151+
if (statements.isEmpty() || !(statements.get(statements.size() - 1) instanceof BreakTree)) {
152+
List<StatementTree> expanded = new ArrayList<>(statements.size() + 1);
153+
expanded.addAll(statements);
154+
expanded.add(make.Break(null));
155+
wc.rewrite(lastCase, make.CasePatterns(lastCase.getLabels(), expanded));
156+
}
157+
}
158+
}
159+
}

java/java.hints/src/org/netbeans/modules/java/hints/errors/VarCompDeclaration.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,10 @@ public void cancel() {
8585
private static final class FixImpl extends JavaFix {
8686

8787
CompilationInfo info;
88-
TreePath path;
8988

9089
public FixImpl(CompilationInfo info, TreePath path) {
9190
super(info, path);
9291
this.info = info;
93-
this.path = path;
9492
}
9593

9694
@Override

java/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@
164164
<file name="org-netbeans-modules-java-hints-errors-RemoveFinalModifierFromVariable.instance">
165165
<attr name="instanceCreate" methodvalue="org.netbeans.modules.java.hints.errors.AddOrRemoveFinalModifier.createRemoveFinalFromVariable"/>
166166
</file>
167+
<file name="org-netbeans-modules-java-hints-errors-AddDefaultCase.instance"/>
167168
<file name="org-netbeans-modules-java-hints-errors-RemoveInvalidModifier.instance"/>
168169
<file name="org-netbeans-modules-java-hints-errors-RemoveUselessCast.instance"/>
169170
<file name="org-netbeans-modules-java-hints-errors-SuppressWarningsFixer.instance"/>
@@ -327,7 +328,7 @@
327328

328329
<folder name="bugs">
329330
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.java.hints.resources.Bundle"/>
330-
<file name="org-netbeans-modules-java-hints-SuspiciousNamesCombination.instance" />
331+
<file name="org-netbeans-modules-java-hints-SuspiciousNamesCombination.instance"/>
331332
</folder>
332333

333334
<folder name="thread">

java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/AccessErrorTest.java

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,7 @@
1818
*/
1919
package org.netbeans.modules.java.hints.errors;
2020

21-
import com.sun.source.util.TreePath;
22-
import java.util.List;
23-
import java.util.Set;
24-
import org.netbeans.api.java.source.CompilationInfo;
2521
import org.netbeans.modules.java.hints.infrastructure.ErrorHintsTestBase;
26-
import org.netbeans.spi.editor.hints.Fix;
2722
import org.openide.util.NbBundle;
2823

2924
/**
@@ -33,7 +28,7 @@
3328
public class AccessErrorTest extends ErrorHintsTestBase {
3429

3530
public AccessErrorTest(String name) {
36-
super(name);
31+
super(name, AccessError.class);
3732
}
3833

3934
public void testSimple() throws Exception {
@@ -61,21 +56,6 @@ public void testSimple() throws Exception {
6156
"}\n").replaceAll("[\\s]+", " "));
6257
}
6358

64-
@Override
65-
protected List<Fix> computeFixes(CompilationInfo info, int pos, TreePath path) throws Exception {
66-
return new AccessError().run(info, null, pos, path, null);
67-
}
68-
69-
@Override
70-
protected String toDebugString(CompilationInfo info, Fix f) {
71-
return f.getText();
72-
}
73-
74-
@Override
75-
protected Set<String> getSupportedErrorKeys() {
76-
return new AccessError().getCodes();
77-
}
78-
7959
static {
8060
NbBundle.setBranding("test");
8161
}

java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/AddCastTest.java

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@
1919

2020
package org.netbeans.modules.java.hints.errors;
2121

22-
import com.sun.source.util.TreePath;
23-
import java.util.List;
24-
import java.util.Set;
25-
import org.netbeans.api.java.source.CompilationInfo;
2622
import org.netbeans.modules.java.hints.infrastructure.ErrorHintsTestBase;
27-
import org.netbeans.spi.editor.hints.Fix;
2823
import org.openide.util.NbBundle;
2924

3025
/**
@@ -34,7 +29,7 @@
3429
public class AddCastTest extends ErrorHintsTestBase {
3530

3631
public AddCastTest(String testName) {
37-
super(testName);
32+
super(testName, AddCast.class);
3833
}
3934

4035
public void test117868() throws Exception {
@@ -157,21 +152,6 @@ public void test214835b() throws Exception {
157152
"package test; public class Test { private static void x(long l) { t((byte) l); } void t(byte b) {} void t(int i) {} void t(String s) {} }");
158153
}
159154

160-
@Override
161-
protected List<Fix> computeFixes(CompilationInfo info, int pos, TreePath path) throws Exception {
162-
return new AddCast().run(info, null, pos, path, null);
163-
}
164-
165-
@Override
166-
protected String toDebugString(CompilationInfo info, Fix f) {
167-
return f.getText();
168-
}
169-
170-
@Override
171-
protected Set<String> getSupportedErrorKeys() {
172-
return new AddCast().getCodes();
173-
}
174-
175155
static {
176156
NbBundle.setBranding("test");
177157
}

java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/AddCatchFixTest.java

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,7 @@
1818
*/
1919
package org.netbeans.modules.java.hints.errors;
2020

21-
import com.sun.source.util.TreePath;
22-
import java.util.List;
23-
import java.util.Set;
24-
import org.netbeans.api.java.source.CompilationInfo;
2521
import org.netbeans.modules.java.hints.infrastructure.ErrorHintsTestBase;
26-
import org.netbeans.spi.editor.hints.Fix;
2722
import org.openide.util.NbBundle;
2823

2924
/**
@@ -33,7 +28,7 @@
3328
public class AddCatchFixTest extends ErrorHintsTestBase {
3429

3530
public AddCatchFixTest(String testName) {
36-
super(testName);
31+
super(testName, UncaughtException.class);
3732
}
3833

3934
public void test207480a() throws Exception {
@@ -44,20 +39,6 @@ public void test207480a() throws Exception {
4439
"package test; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; public class Test {public void test() {try { test2(); } catch (IOException ex) { Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex); } finally{ } } private void test2() throws IOException {} }");
4540
}
4641

47-
protected List<Fix> computeFixes(CompilationInfo info, int pos, TreePath path) throws Exception {
48-
return new UncaughtException().run(info, null, pos, path, null);
49-
}
50-
51-
@Override
52-
protected Set<String> getSupportedErrorKeys() {
53-
return new UncaughtException().getCodes();
54-
}
55-
56-
@Override
57-
protected String toDebugString(CompilationInfo info, Fix f) {
58-
return f.getText();
59-
}
60-
6142
static {
6243
NbBundle.setBranding("test");
6344
}

java/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/AddConstructorTest.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,7 @@
1818
*/
1919
package org.netbeans.modules.java.hints.errors;
2020

21-
import org.netbeans.modules.java.hints.errors.AddConstructor;
22-
import com.sun.source.util.TreePath;
23-
import java.util.List;
24-
import java.util.Set;
25-
import org.netbeans.api.java.source.CompilationInfo;
2621
import org.netbeans.modules.java.hints.infrastructure.ErrorHintsTestBase;
27-
import org.netbeans.spi.editor.hints.Fix;
2822
import org.openide.util.NbBundle;
2923

3024
/**

0 commit comments

Comments
 (0)