Skip to content

Commit 8fcdc04

Browse files
committed
Do not try to auto-import simple enum names in switches, that are 'in scope' due to enum switch target typing.
1 parent 2dc2800 commit 8fcdc04

File tree

2 files changed

+187
-28
lines changed

2 files changed

+187
-28
lines changed

java/java.editor/src/org/netbeans/modules/java/editor/imports/ClipboardHandler.java

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,17 @@
7373
import javax.swing.text.Position;
7474
import javax.swing.text.StyledDocument;
7575

76-
import com.sun.source.tree.CaseTree;
7776
import com.sun.source.tree.ClassTree;
7877
import com.sun.source.tree.ImportTree;
7978
import com.sun.source.tree.MethodTree;
79+
import com.sun.source.tree.SwitchExpressionTree;
80+
import com.sun.source.tree.SwitchTree;
81+
import com.sun.source.tree.Tree.Kind;
82+
import com.sun.source.util.Trees;
8083
import java.awt.Component;
8184
import java.util.Set;
85+
import javax.lang.model.type.TypeKind;
86+
import javax.lang.model.type.TypeMirror;
8287
import org.netbeans.api.editor.EditorActionRegistration;
8388
import org.netbeans.api.editor.EditorActionRegistrations;
8489
import org.netbeans.api.java.lexer.JavaTokenId;
@@ -407,25 +412,50 @@ public void exportToClipboard(JComponent comp, Clipboard clip, int action) throw
407412
@Override public void run(final CompilationController parameter) throws Exception {
408413
parameter.toPhase(JavaSource.Phase.RESOLVED);
409414

415+
Trees trees = parameter.getTrees();
416+
410417
new ErrorAwareTreePathScanner<Void, Void>() {
411418
private final Set<Element> declaredInCopiedText = new HashSet<>();
412419
@Override public Void visitIdentifier(IdentifierTree node, Void p) {
413-
int s = (int) parameter.getTrees().getSourcePositions().getStartPosition(parameter.getCompilationUnit(), node);
414-
int e = (int) parameter.getTrees().getSourcePositions().getEndPosition(parameter.getCompilationUnit(), node);
415-
javax.lang.model.element.Element el = parameter.getTrees().getElement(getCurrentPath());
420+
int s = (int) trees.getSourcePositions().getStartPosition(parameter.getCompilationUnit(), node);
421+
int e = (int) trees.getSourcePositions().getEndPosition(parameter.getCompilationUnit(), node);
422+
javax.lang.model.element.Element el = trees.getElement(getCurrentPath());
416423

417424
if (s >= start && e >= start && e <= end && el != null && !declaredInCopiedText.contains(el)) {
418425
if (el.getKind().isClass() || el.getKind().isInterface()) {
419-
TreePath parentPath = getCurrentPath().getParentPath();
420-
if (parentPath == null || parentPath.getLeaf().getKind() != Tree.Kind.NEW_CLASS
421-
|| ((NewClassTree)parentPath.getLeaf()).getEnclosingExpression() == null
422-
|| ((NewClassTree)parentPath.getLeaf()).getIdentifier() != node) {
423-
simple2ImportFQN.put(el.getSimpleName().toString(), ((TypeElement) el).getQualifiedName().toString());
424-
spans.add(new int[] {s - start, e - start});
426+
if (el.asType().getKind() != TypeKind.ERROR) {
427+
TreePath parentPath = getCurrentPath().getParentPath();
428+
if (parentPath == null || parentPath.getLeaf().getKind() != Tree.Kind.NEW_CLASS
429+
|| ((NewClassTree)parentPath.getLeaf()).getEnclosingExpression() == null
430+
|| ((NewClassTree)parentPath.getLeaf()).getIdentifier() != node) {
431+
simple2ImportFQN.put(el.getSimpleName().toString(), ((TypeElement) el).getQualifiedName().toString());
432+
spans.add(new int[] {s - start, e - start});
433+
}
425434
}
426435
} else if ((el.getKind() == ElementKind.ENUM_CONSTANT)) {
427436
TreePath parentPath = getCurrentPath().getParentPath();
428-
if (parentPath.getLeaf().getKind() != Tree.Kind.CASE || ((CaseTree)parentPath.getLeaf()).getExpression() != node) {
437+
boolean shouldImport = true;
438+
if (parentPath.getLeaf().getKind() == Tree.Kind.CONSTANT_CASE_LABEL) {
439+
TreePath swtch = parentPath;
440+
while (swtch != null &&
441+
swtch.getLeaf().getKind() != Kind.SWITCH &&
442+
swtch.getLeaf().getKind() != Kind.SWITCH_EXPRESSION) {
443+
swtch = swtch.getParentPath();
444+
}
445+
TypeMirror selectorType;
446+
if (swtch != null) {
447+
selectorType = switch (swtch.getLeaf().getKind()) {
448+
case SWITCH -> trees.getTypeMirror(new TreePath(swtch, ((SwitchTree) swtch.getLeaf()).getExpression()));
449+
case SWITCH_EXPRESSION -> trees.getTypeMirror(new TreePath(swtch, ((SwitchExpressionTree) swtch.getLeaf()).getExpression()));
450+
default -> null;
451+
};
452+
} else {
453+
selectorType = null;
454+
}
455+
Element selectorElement = selectorType != null ? parameter.getTypes().asElement(selectorType) : null;
456+
shouldImport = selectorElement == null || selectorElement.getKind() != ElementKind.ENUM;
457+
}
458+
if (shouldImport) {
429459
simple2ImportFQN.put(el.getSimpleName().toString(), ((TypeElement) el.getEnclosingElement()).getQualifiedName().toString() + '.' + el.getSimpleName().toString());
430460
spans.add(new int[] {s - start, e - start});
431461
}
@@ -447,8 +477,8 @@ public void exportToClipboard(JComponent comp, Clipboard clip, int action) throw
447477
return super.visitMethod(node, p);
448478
}
449479
private void handleDeclaration() {
450-
int s = (int) parameter.getTrees().getSourcePositions().getStartPosition(parameter.getCompilationUnit(), getCurrentPath().getLeaf());
451-
int e = (int) parameter.getTrees().getSourcePositions().getEndPosition(parameter.getCompilationUnit(), getCurrentPath().getLeaf());
480+
int s = (int) trees.getSourcePositions().getStartPosition(parameter.getCompilationUnit(), getCurrentPath().getLeaf());
481+
int e = (int) trees.getSourcePositions().getEndPosition(parameter.getCompilationUnit(), getCurrentPath().getLeaf());
452482
javax.lang.model.element.Element el = parameter.getTrees().getElement(getCurrentPath());
453483

454484
if (el != null && ((start <= s && s <= end) || (start <= e && e <= end))) {
@@ -719,7 +749,7 @@ private boolean insideToken(final JTextComponent jtc, final JavaTokenId first, f
719749

720750
private static final Object NO_IMPORTS = new Object();
721751
private static final Object RUN_SYNCHRONOUSLY = new Object();
722-
private static final DataFlavor IMPORT_FLAVOR = new DataFlavor(ImportsWrapper.class, NbBundle.getMessage(ClipboardHandler.class, "MSG_ClipboardImportFlavor"));
752+
static final DataFlavor IMPORT_FLAVOR = new DataFlavor(ImportsWrapper.class, NbBundle.getMessage(ClipboardHandler.class, "MSG_ClipboardImportFlavor"));
723753
private static final DataFlavor COPY_FROM_STRING_FLAVOR = new DataFlavor(Boolean.class, NbBundle.getMessage(ClipboardHandler.class, "MSG_ClipboardCopyFromStringFlavor"));
724754

725755
private static final class WrappedTransferable implements Transferable {
@@ -777,6 +807,10 @@ public ImportsWrapper(FileObject sourceFO, Map<String, String> simple2ImportFQN,
777807
this.simple2ImportFQN = simple2ImportFQN;
778808
this.identifiers = identifiers;
779809
}
810+
811+
public Map<String, String> getSimple2ImportFQN() {
812+
return simple2ImportFQN;
813+
}
780814
}
781815

782816
public static final class JavaCutAction extends CutAction {

java/java.editor/test/unit/src/org/netbeans/modules/java/editor/imports/ClipboardHandlerTest.java

Lines changed: 139 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
*/
1919
package org.netbeans.modules.java.editor.imports;
2020

21+
import java.awt.Toolkit;
2122
import java.beans.PropertyChangeEvent;
2223
import java.beans.PropertyChangeListener;
2324
import java.io.IOException;
25+
import java.util.Map;
2426
import java.util.concurrent.Exchanger;
2527
import java.util.concurrent.TimeUnit;
2628
import java.util.regex.Pattern;
@@ -34,6 +36,7 @@
3436
import org.netbeans.api.lexer.Language;
3537
import org.netbeans.core.startup.Main;
3638
import org.netbeans.junit.NbTestCase;
39+
import org.netbeans.modules.java.editor.imports.ClipboardHandler.ImportsWrapper;
3740
import org.netbeans.modules.java.source.BootClassPathUtil;
3841
import org.netbeans.modules.java.source.parsing.JavacParser;
3942
import org.openide.cookies.EditorCookie;
@@ -119,6 +122,111 @@ public void testStaticImportsOn11_JIRA3019() throws Exception {
119122
"11");
120123
}
121124

125+
public void testSwitchSimpleEnumNames1() throws Exception {
126+
doCopyAndPaste("""
127+
package test;
128+
import static test.E.A;
129+
public class Test {
130+
private void test(E e) {
131+
|switch (e) {
132+
case A:
133+
case B:
134+
case C:
135+
System.err.println(A);
136+
break;
137+
}|
138+
}
139+
}
140+
enum E {
141+
A, B, C;
142+
}
143+
""",
144+
"21");
145+
ImportsWrapper wrapper = (ImportsWrapper) Toolkit.getDefaultToolkit().getSystemClipboard().getData(ClipboardHandler.IMPORT_FLAVOR);
146+
Map<String, String> simple2ImportFQN = wrapper.getSimple2ImportFQN();
147+
assertEquals(Map.of("A", "test.E.A", "System", "java.lang.System"),
148+
simple2ImportFQN);
149+
}
150+
151+
public void testSwitchSimpleEnumNames2() throws Exception {
152+
doCopyAndPaste("""
153+
package test;
154+
import static test.E.A;
155+
public class Test {
156+
private int test(E e) {
157+
return |switch (e) {
158+
case A, B, C -> {
159+
System.err.println(A);
160+
yield 0;
161+
}
162+
}|
163+
}
164+
}
165+
enum E {
166+
A, B, C;
167+
}
168+
""",
169+
"21");
170+
ImportsWrapper wrapper = (ImportsWrapper) Toolkit.getDefaultToolkit().getSystemClipboard().getData(ClipboardHandler.IMPORT_FLAVOR);
171+
Map<String, String> simple2ImportFQN = wrapper.getSimple2ImportFQN();
172+
assertEquals(Map.of("A", "test.E.A", "System", "java.lang.System"),
173+
simple2ImportFQN);
174+
}
175+
176+
public void testSwitchSimpleEnumNames3() throws Exception {
177+
doCopyAndPaste("""
178+
package test;
179+
import static test.E.*;
180+
public class Test {
181+
private int test(Object o) {
182+
return |switch (o) {
183+
case A:
184+
case B:
185+
case C:
186+
System.err.println(A);
187+
yield 0;
188+
default: yield 0;
189+
}|
190+
}
191+
}
192+
enum E {
193+
A, B, C;
194+
}
195+
""",
196+
"21");
197+
ImportsWrapper wrapper = (ImportsWrapper) Toolkit.getDefaultToolkit().getSystemClipboard().getData(ClipboardHandler.IMPORT_FLAVOR);
198+
Map<String, String> simple2ImportFQN = wrapper.getSimple2ImportFQN();
199+
assertEquals(Map.of("A", "test.E.A", "B", "test.E.B", "C", "test.E.C", "System", "java.lang.System"),
200+
simple2ImportFQN);
201+
}
202+
203+
public void testSwitchSimpleEnumNames4() throws Exception {
204+
doCopyAndPaste("""
205+
package test;
206+
import static test.E.A;
207+
public class Test {
208+
private int test(Undef o) {
209+
return |switch (o) {
210+
case A:
211+
case B:
212+
case C:
213+
System.err.println(A);
214+
yield 0;
215+
default: yield 0;
216+
}|
217+
}
218+
}
219+
enum E {
220+
A, B, C;
221+
}
222+
""",
223+
"21");
224+
ImportsWrapper wrapper = (ImportsWrapper) Toolkit.getDefaultToolkit().getSystemClipboard().getData(ClipboardHandler.IMPORT_FLAVOR);
225+
Map<String, String> simple2ImportFQN = wrapper.getSimple2ImportFQN();
226+
assertEquals(Map.of("A", "test.E.A", "System", "java.lang.System"),
227+
simple2ImportFQN);
228+
}
229+
122230
private void copyAndPaste(String from, final String to, String golden) throws Exception {
123231
copyAndPaste(from, to, golden, null);
124232
}
@@ -128,6 +236,36 @@ private void copyAndPaste(String from, final String to, String golden, String so
128236

129237
assertTrue(pastePos >= 0);
130238

239+
FileObject src = doCopyAndPaste(from, sourceLevel);
240+
final JEditorPane[] target = new JEditorPane[1];
241+
final Exception[] fromAWT = new Exception[1];
242+
243+
target[0] = paneFor(src, "test/Target.java", to.replaceAll(Pattern.quote("^"), ""), sourceLevel);
244+
SwingUtilities.invokeAndWait(new Runnable() {
245+
@Override public void run() {
246+
try {
247+
target[0].setCaretPosition(pastePos);
248+
249+
target[0].paste();
250+
} catch (Exception ex) {
251+
fromAWT[0] = ex;
252+
}
253+
}
254+
});
255+
256+
if (fromAWT[0] != null) throw fromAWT[0];
257+
258+
final String[] actual = new String[1];
259+
260+
SwingUtilities.invokeAndWait(new Runnable() {
261+
@Override public void run() {
262+
actual[0] = target[0].getText();
263+
}
264+
});
265+
assertEquals(golden, actual[0]);
266+
}
267+
268+
private FileObject doCopyAndPaste(String from, String sourceLevel) throws Exception {
131269
String[] split = from.split(Pattern.quote("|"));
132270

133271
assertEquals(3, split.length);
@@ -145,22 +283,16 @@ private void copyAndPaste(String from, final String to, String golden, String so
145283
SourceUtilsTestUtil.prepareTest(src, build, cache);
146284
SourceUtilsTestUtil.compileRecursively(src);
147285

148-
final JEditorPane[] target = new JEditorPane[1];
149286
final Exception[] fromAWT = new Exception[1];
150287

151288
final JEditorPane source = paneFor(src, "test/Test.java", cleanFrom, sourceLevel);
152-
target[0] = paneFor(src, "test/Target.java", to.replaceAll(Pattern.quote("^"), ""), sourceLevel);
153289
SwingUtilities.invokeAndWait(new Runnable() {
154290
@Override public void run() {
155291
try {
156292
source.setSelectionStart(start);
157293
source.setSelectionEnd(end);
158294

159295
source.copy();
160-
161-
target[0].setCaretPosition(pastePos);
162-
163-
target[0].paste();
164296
} catch (Exception ex) {
165297
fromAWT[0] = ex;
166298
}
@@ -169,14 +301,7 @@ private void copyAndPaste(String from, final String to, String golden, String so
169301

170302
if (fromAWT[0] != null) throw fromAWT[0];
171303

172-
final String[] actual = new String[1];
173-
174-
SwingUtilities.invokeAndWait(new Runnable() {
175-
@Override public void run() {
176-
actual[0] = target[0].getText();
177-
}
178-
});
179-
assertEquals(golden, actual[0]);
304+
return src;
180305
}
181306

182307
private JEditorPane paneFor(FileObject src, String fileName, String code, String sourceLevel) throws Exception, DataObjectNotFoundException, IOException {

0 commit comments

Comments
 (0)