From 5fda76561c1573cb3465b4cdecbd5d72d4817e00 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Tue, 10 Mar 2026 20:02:06 +0100 Subject: [PATCH] Improving code completion in presence of local classes. --- .../java/completion/JavaCompletionTask.java | 42 +++- .../1.8/LocalClass.pass | 1 + .../1.8/LocalClassConstructor.pass | 2 + .../1.8/LocalRecordPattern.pass | 1 + .../JavaCompletionTask121FeaturesTest.java | 55 +++++ .../JavaCompletionTaskAdvancedTest.java | 31 +++ java/java.source.base/apichanges.xml | 15 +- .../nbproject/project.properties | 2 +- .../api/java/source/TreeUtilities.java | 29 ++- .../api/java/source/TreeUtilitiesTest.java | 41 ++++ .../netbeans/lib/nbjavac/services/NBAttr.java | 81 ++++--- nbbuild/javadoctools/apichanges-1.1.dtd | 211 ++++++++++++++++++ 12 files changed, 467 insertions(+), 44 deletions(-) create mode 100644 java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalClass.pass create mode 100644 java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalClassConstructor.pass create mode 100644 java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalRecordPattern.pass create mode 100644 nbbuild/javadoctools/apichanges-1.1.dtd diff --git a/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java b/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java index ab43b4c26038..05bf5bef047b 100644 --- a/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java +++ b/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java @@ -1869,9 +1869,13 @@ private void insideMemberSelect(Env env) throws IOException { if (el != null && (el.getKind().isClass() || el.getKind().isInterface())) { if (parent.getKind() == Tree.Kind.NEW_CLASS && ((NewClassTree) parent).getIdentifier() == fa && prefix != null) { String typeName = controller.getElementUtilities().getElementName(el, true) + "." + prefix; //NOI18N - TypeMirror tm = controller.getTreeUtilities().parseType(typeName, env.getScope().getEnclosingClass()); + TypeMirror tm = controller.getTreeUtilities().parseType(typeName, env.getScope()); if (tm != null && tm.getKind() == TypeKind.DECLARED) { - addMembers(env, tm, ((DeclaredType) tm).asElement(), EnumSet.of(CONSTRUCTOR), null, inImport, insideNew, false, false, switchItemAdder); + TypeElement te = (TypeElement) ((DeclaredType) tm).asElement(); + addMembers(env, tm, te, EnumSet.of(CONSTRUCTOR), null, inImport, insideNew, false, false, switchItemAdder); + if (shouldExcludeTypeInNewClass(te, env)) { + env.addToExcludes(te); + } } } } @@ -1917,9 +1921,13 @@ private void insideMemberSelect(Env env) throws IOException { if (el != null && el.getKind() == PACKAGE) { if (parent.getKind() == Tree.Kind.NEW_CLASS && ((NewClassTree) parent).getIdentifier() == fa && prefix != null) { String typeName = controller.getElementUtilities().getElementName(el, true) + "." + prefix; //NOI18N - TypeMirror tm = controller.getTreeUtilities().parseType(typeName, env.getScope().getEnclosingClass()); + TypeMirror tm = controller.getTreeUtilities().parseType(typeName, env.getScope()); if (tm != null && tm.getKind() == TypeKind.DECLARED) { - addMembers(env, tm, ((DeclaredType) tm).asElement(), EnumSet.of(CONSTRUCTOR), null, inImport, insideNew, false, false, switchItemAdder); + TypeElement te = (TypeElement) ((DeclaredType) tm).asElement(); + addMembers(env, tm, te, EnumSet.of(CONSTRUCTOR), null, inImport, insideNew, false, false, switchItemAdder); + if (shouldExcludeTypeInNewClass(te, env)) { + env.addToExcludes(te); + } } } if (exs != null && !exs.isEmpty()) { @@ -2051,7 +2059,7 @@ private void insideMethodInvocation(Env env) throws IOException { if (path.getParentPath().getLeaf().getKind() == Kind.CONSTANT_CASE_LABEL) { CompilationController controller = env.getController(); controller.toPhase(Phase.RESOLVED); - TypeMirror tm = controller.getTreeUtilities().parseType(fullName(mi.getMethodSelect()), env.getScope().getEnclosingClass()); + TypeMirror tm = controller.getTreeUtilities().parseType(fullName(mi.getMethodSelect()), env.getScope()); if (tm != null && tm.getKind() == TypeKind.DECLARED) { TypeElement te = (TypeElement) ((DeclaredType) tm).asElement(); if (te.getKind() == RECORD) { @@ -2062,7 +2070,7 @@ private void insideMethodInvocation(Env env) throws IOException { if (ts != null && (ts.token().id() == JavaTokenId.LPAREN || ts.token().id() == JavaTokenId.COMMA)) { if (componentType.getKind() == TypeKind.DECLARED) { if (prefix != null) { - TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope().getEnclosingClass()); + TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope()); if (ptm != null && ptm.getKind() == TypeKind.DECLARED) { TypeElement pte = (TypeElement) ((DeclaredType) ptm).asElement(); if (pte != null && pte.getKind() == RECORD) { @@ -2135,12 +2143,11 @@ private void insideNewClass(Env env) throws IOException { ? controller.getTypes().getDeclaredType(tel) : null; TypeElement toExclude = null; if (nc.getIdentifier().getKind() == Tree.Kind.IDENTIFIER && prefix != null) { - TypeMirror tm = controller.getTreeUtilities().parseType(prefix, env.getScope().getEnclosingClass()); + TypeMirror tm = controller.getTreeUtilities().parseType(prefix, env.getScope()); if (tm != null && tm.getKind() == TypeKind.DECLARED) { TypeElement te = (TypeElement) ((DeclaredType) tm).asElement(); addMembers(env, tm, te, EnumSet.of(CONSTRUCTOR), base, false, true, false); - if ((te.getTypeParameters().isEmpty() || SourceVersion.RELEASE_5.compareTo(controller.getSourceVersion()) > 0) - && !hasAccessibleInnerClassConstructor(te, env.getScope(), controller.getTrees())) { + if (shouldExcludeTypeInNewClass(te, env)) { toExclude = te; } } @@ -2227,6 +2234,13 @@ private void insideNewClass(Env env) throws IOException { } } + private boolean shouldExcludeTypeInNewClass(TypeElement te, Env env) throws IOException { + CompilationController controller = env.getController(); + + return (te.getTypeParameters().isEmpty() || SourceVersion.RELEASE_5.compareTo(controller.getSourceVersion()) > 0) + && !hasAccessibleInnerClassConstructor(te, env.getScope(), controller.getTrees()); + } + private void insideTry(Env env) throws IOException { CompilationController controller = env.getController(); TokenSequence last = findLastNonWhitespaceToken(env, env.getPath().getLeaf(), env.getOffset()); @@ -2550,7 +2564,7 @@ private void insideCase(Env env) throws IOException { } else { if (env.getController().getSourceVersion().compareTo(RELEASE_21) >= 0) { if (prefix != null) { - TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope().getEnclosingClass()); + TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope()); if (ptm != null && ptm.getKind() == TypeKind.DECLARED) { TypeElement pte = (TypeElement) ((DeclaredType) ptm).asElement(); if (pte != null && pte.getKind() == RECORD) { @@ -2754,7 +2768,7 @@ private void insideTypeCheck(Env env) throws IOException { TokenSequence ts = findLastNonWhitespaceToken(env, iot, env.getOffset()); if (ts != null && ts.token().id() == JavaTokenId.INSTANCEOF) { if (prefix != null && controller.getSourceVersion().compareTo(RELEASE_21) >= 0) { - TypeMirror tm = controller.getTreeUtilities().parseType(prefix, env.getScope().getEnclosingClass()); + TypeMirror tm = controller.getTreeUtilities().parseType(prefix, env.getScope()); if (tm != null && tm.getKind() == TypeKind.DECLARED) { TypeElement te = (TypeElement) ((DeclaredType) tm).asElement(); if (te != null && te.getKind() == RECORD) { @@ -3485,7 +3499,7 @@ private void insideDeconstructionRecordPattern(final Env env) throws IOException TypeMirror componentType = recordComponents.get(size - 1).getAccessor().getReturnType(); if (componentType.getKind() == TypeKind.DECLARED) { if (prefix != null) { - TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope().getEnclosingClass()); + TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope()); if (ptm != null && ptm.getKind() == TypeKind.DECLARED) { TypeElement pte = (TypeElement) ((DeclaredType) ptm).asElement(); if (pte != null && pte.getKind() == RECORD) { @@ -4088,6 +4102,9 @@ private void addMembers(final Env env, final TypeMirror type, final Element elem ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() { @Override public boolean accept(Element e, TypeMirror t) { + if (!(env.getExcludes() == null || !env.getExcludes().contains(e))) { + return false; //TODO: correct? + } switch (simplifyElementKind(e.getKind())) { case FIELD: if (!startsWith(env, e.getSimpleName().toString())) { @@ -4889,6 +4906,7 @@ private void addAttributeValues(Env env, Element element, AnnotationMirror annot CharSequence fqn = ((TypeElement) ((DeclaredType) type).asElement()).getQualifiedName(); if (JAVA_LANG_CLASS.contentEquals(fqn)) { String name = value.endsWith(".class") ? value.substring(0, value.length() - 6) : value; //NOI18N + //NOTE: for annotations, not sure if keeping this is not more sensible. Can annotations realistically use local classes as attribute values? TypeMirror tm = tu.parseType(name, eu.outermostTypeElement(element)); typeElement = tm != null && tm.getKind() == TypeKind.DECLARED ? (TypeElement) ((DeclaredType) tm).asElement() : null; if (typeElement != null && startsWith(env, typeElement.getSimpleName().toString())) { diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalClass.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalClass.pass new file mode 100644 index 000000000000..7bb37ba9f300 --- /dev/null +++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalClass.pass @@ -0,0 +1 @@ +Local diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalClassConstructor.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalClassConstructor.pass new file mode 100644 index 000000000000..c6fa3f783203 --- /dev/null +++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalClassConstructor.pass @@ -0,0 +1,2 @@ +Local() +Local(int i) diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalRecordPattern.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalRecordPattern.pass new file mode 100644 index 000000000000..1e457e4850ac --- /dev/null +++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaCompletionTaskTest/1.8/LocalRecordPattern.pass @@ -0,0 +1 @@ +Local(int i) diff --git a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask121FeaturesTest.java b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask121FeaturesTest.java index 9d9d3d65c6e3..c0dbd977465f 100644 --- a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask121FeaturesTest.java +++ b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask121FeaturesTest.java @@ -64,6 +64,61 @@ public void testRecordPatternCompletion_5() throws Exception { performTest("RecordPattern", 1107, null, "AutoCompletion_RecordPattern_5.pass", SOURCE_LEVEL); } + public void testRecordPatternLocalClass1() throws Exception { + performTest("Method", 935, + """ + record Local(int i) {} + Object o = null; + switch (o) { + case Loc""", + "LocalClass.pass", + SOURCE_LEVEL); + } + + public void testRecordPatternLocalClass2() throws Exception { + performTest("Method", 935, + """ + record Local(int i) {} + Object o = null; + switch (o) { + case Local""", + "LocalRecordPattern.pass", + SOURCE_LEVEL); + } + + public void testRecordPatternLocalClass3() throws Exception { + performTest("Method", 935, + """ + record Local(int i) {} + Object o = null; + boolean b = o instanceof Local""", + "LocalRecordPattern.pass", + SOURCE_LEVEL); + } + + public void testRecordPatternLocalClass4() throws Exception { + performTest("Method", 935, + """ + record Local(int i) {} + record Box(Local l) {} + Object o = null; + boolean b = o instanceof Box(Local""", + "LocalRecordPattern.pass", + SOURCE_LEVEL); + } + + public void testRecordPatternLocalClass5() throws Exception { + performTest("Method", 935, + """ + record Local(int i) {} + record Box(Local l) {} + Object o = null; + switch (o) { + case Box(Local""", + "LocalRecordPattern.pass", + SOURCE_LEVEL); + } + public void testCaseLabels_1() throws Exception { performTest("SwitchPatternMatching", 971, null, "AutoCompletion_CaseLabels_PatternMatchingSwitch_1.pass", SOURCE_LEVEL); } diff --git a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTaskAdvancedTest.java b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTaskAdvancedTest.java index 192cb88986f5..b6f7190a5b4b 100644 --- a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTaskAdvancedTest.java +++ b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTaskAdvancedTest.java @@ -958,4 +958,35 @@ public void testGenericMethod1() throws Exception { public void testGenericMethod2() throws Exception { performTest("GenericMethodInvocation", 1231, "run2(\"\", arg", "GenericMethodInvocation2.pass"); } + + public void testLocalClassConstructor1() throws Exception { + performTest("Method", 935, + """ + class Local { + Local() {} + Local(int i) {} + } + new Local""", + "LocalClassConstructor.pass"); + } + + public void testLocalClassConstructor2() throws Exception { + performTest("Method", 935, + """ + class LocalOuter { + static class Local { + Local() {} + Local(int i) {} + } + } + new LocalOuter.Local""", + "LocalClassConstructor.pass"); + } + + public void testConstructorFromPackage() throws Exception { + performTest("Method", 935, + """ + new java.lang.String""", + "stringConstructors.pass"); + } } diff --git a/java/java.source.base/apichanges.xml b/java/java.source.base/apichanges.xml index 358112a16e48..29d550edfa18 100644 --- a/java/java.source.base/apichanges.xml +++ b/java/java.source.base/apichanges.xml @@ -19,12 +19,25 @@ under the License. --> - + Java Source API + + + Adding TreeUtilities.parseType(String, Scope) method overload + + + + + + Adding TreeUtilities.parseType(String, Scope) method overload + to support more exact type parsing. + + + API Support for module imports diff --git a/java/java.source.base/nbproject/project.properties b/java/java.source.base/nbproject/project.properties index 923ec707672b..d977f1c852c0 100644 --- a/java/java.source.base/nbproject/project.properties +++ b/java/java.source.base/nbproject/project.properties @@ -23,7 +23,7 @@ javadoc.name=Java Source Base javadoc.title=Java Source Base javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml -spec.version.base=2.83.0 +spec.version.base=2.84.0 test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/nb-javac-api.jar test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\ ${o.n.core.dir}/lib/boot.jar:\ diff --git a/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java b/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java index ed3fe7a274ed..1f2d439a0078 100644 --- a/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java +++ b/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java @@ -700,6 +700,26 @@ public TypeMirror parseType(String expr, TypeElement scope) { } } + /**Parses given type in given context. + * + * @param expr type specification + * @param scope in which simple names should be resolved + * @return parsed {@link TypeMirror} or null if the given specification cannot be parsed + * @since 2.84 + */ + public TypeMirror parseType(String expr, Scope scope) { + Env env = getEnv(scope); + if (scope instanceof NBScope && ((NBScope)scope).areAccessibilityChecksDisabled()) { + NBResolve.instance(info.impl.getJavacTask().getContext()).disableAccessibilityChecks(); + } + try { + Tree type = parseType(expr); + return attributeTree(info.impl.getJavacTask(), env.toplevel, (JCTree) type, scope, true, new ArrayList<>()); + } finally { + NBResolve.instance(info.impl.getJavacTask().getContext()).restoreAccessbilityChecks(); + } + } + /**Parses given type in given context. * * @param expr type specification @@ -922,7 +942,7 @@ public TypeMirror attributeTree(Tree tree, Scope scope) { NBResolve.instance(info.impl.getJavacTask().getContext()).disableAccessibilityChecks(); } try { - return attributeTree(info.impl.getJavacTask(), env.toplevel, (JCTree) tree, scope, new ArrayList<>()); + return attributeTree(info.impl.getJavacTask(), env.toplevel, (JCTree) tree, scope, false, new ArrayList<>()); } finally { NBResolve.instance(info.impl.getJavacTask().getContext()).restoreAccessbilityChecks(); } @@ -949,7 +969,7 @@ public TypeMirror reattributeTree(Tree tree, Scope scope) { NBResolve.instance(info.impl.getJavacTask().getContext()).disableAccessibilityChecks(); } try { - return attributeTree(info.impl.getJavacTask(), env.toplevel, (JCTree)tree, scope, new ArrayList<>()); + return attributeTree(info.impl.getJavacTask(), env.toplevel, (JCTree)tree, scope, false, new ArrayList<>()); } finally { NBResolve.instance(info.impl.getJavacTask().getContext()).restoreAccessbilityChecks(); } @@ -968,7 +988,7 @@ public Scope reattributeTreeTo(Tree tree, Scope scope, Tree to) { } //from org/netbeans/modules/java/hints/spiimpl/Utilities.java: - private static TypeMirror attributeTree(JavacTaskImpl jti, CompilationUnitTree cut, Tree tree, Scope scope, final List> errors) { + private static TypeMirror attributeTree(JavacTaskImpl jti, CompilationUnitTree cut, Tree tree, Scope scope, boolean attributeAsType, final List> errors) { Log log = Log.instance(jti.getContext()); JavaFileObject prev = log.useSource(new DummyJFO()); Log.DiagnosticHandler discardHandler = log.new DiscardDiagnosticHandler() { @@ -986,6 +1006,9 @@ public void reportReady(JCDiagnostic diag) { try { Attr attr = Attr.instance(jti.getContext()); Env env = getEnv(scope); + if (attributeAsType) { + return attr.attribType((JCTree) tree, env); + } if (tree instanceof JCExpression) return attr.attribExpr((JCTree) tree,env, Type.noType); return attr.attribStat((JCTree) tree,env); diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java index 97041a85f6fc..1063b4cb11d7 100644 --- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java +++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/TreeUtilitiesTest.java @@ -46,6 +46,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Function; import java.util.regex.Pattern; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; @@ -899,4 +900,44 @@ private void test(Runnable r) { StatementTree tree = info.getTreeUtilities().parseStatement("{ return super.test(p); }", new SourcePositions[1]); info.getTreeUtilities().attributeTree(tree, scope); } + + public void testParseType() throws Exception { + this.sourceLevel = "21"; + + String code = """ + package test; + public class Test { + static class Param {} + private void test(Runnable r) { + record Local(T t) {} + int ma|rk; + } + } + """; + + prepareTest("Test", code.replace("|", "")); + + int pos = code.indexOf("|"); + TreePath tp = info.getTreeUtilities().pathFor(pos); + Scope scope = info.getTrees().getScope(tp); + Function type2String = + type -> type.getKind() + ":" + type.toString(); + TypeElement topLevel = info.getTopLevelElements().get(0); + assertEquals("DECLARED:test.Test.Param", + type2String.apply(info.getTreeUtilities().parseType("Param", topLevel))); + assertEquals("DECLARED:test.Test.Param", + type2String.apply(info.getTreeUtilities().parseType("Param", scope))); + assertEquals("DECLARED:test.Test.Param", + type2String.apply(info.getTreeUtilities().parseType("Param", topLevel))); + assertEquals("DECLARED:test.Test.Param", + type2String.apply(info.getTreeUtilities().parseType("Param", scope))); + assertEquals("ERROR:Local", + type2String.apply(info.getTreeUtilities().parseType("Local", topLevel))); + assertEquals("DECLARED:Local", + type2String.apply(info.getTreeUtilities().parseType("Local", scope))); + assertEquals("ERROR:Local", + type2String.apply(info.getTreeUtilities().parseType("Local", topLevel))); + assertEquals("DECLARED:Local", + type2String.apply(info.getTreeUtilities().parseType("Local", scope))); + } } diff --git a/java/lib.nbjavac/src/org/netbeans/lib/nbjavac/services/NBAttr.java b/java/lib.nbjavac/src/org/netbeans/lib/nbjavac/services/NBAttr.java index beb38b2aaa33..dfba5c8319e8 100644 --- a/java/lib.nbjavac/src/org/netbeans/lib/nbjavac/services/NBAttr.java +++ b/java/lib.nbjavac/src/org/netbeans/lib/nbjavac/services/NBAttr.java @@ -25,17 +25,19 @@ import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCCatch; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCSwitch; +import com.sun.tools.javac.tree.JCTree.JCSwitchExpression; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; /** * @@ -113,38 +115,15 @@ public void visitCatch(JCCatch that) { private boolean fullyAttribute; private Env fullyAttributeResult; + @Override protected void breakTreeFound(Env env) { if (fullyAttribute) { fullyAttributeResult = env; } else { - try { - MethodHandles.lookup() - .findSpecial(Attr.class, "breakTreeFound", MethodType.methodType(void.class, Env.class), NBAttr.class) - .invokeExact(this, env); - } catch (Throwable ex) { - sneakyThrows(ex); - } + super.breakTreeFound(env); } } - protected void breakTreeFound(Env env, Type result) { - if (fullyAttribute) { - fullyAttributeResult = env; - } else { - try { - MethodHandles.lookup() - .findSpecial(Attr.class, "breakTreeFound", MethodType.methodType(void.class, Env.class, Type.class), NBAttr.class) - .invokeExact(this, env, result); - } catch (Throwable ex) { - sneakyThrows(ex); - } - } - } - - private void sneakyThrows(Throwable t) throws T { - throw (T) t; - } - public Env attributeAndCapture(JCTree tree, Env env, JCTree to) { try { fullyAttribute = true; @@ -158,4 +137,52 @@ public Env attributeAndCapture(JCTree tree, Env env, J fullyAttribute = false; } } + + @Override + public Env attribExprToTree(JCTree expr, Env env, JCTree tree) { + return super.attribExprToTree(expr, env, workaroundTreeTarget(expr, tree)); + } + + @Override + public Env attribStatToTree(JCTree stmt, Env env, JCTree tree) { + return super.attribStatToTree(stmt, env, workaroundTreeTarget(stmt, tree)); + } + + private JCTree workaroundTreeTarget(JCTree root, JCTree toTree) { + if (!(toTree instanceof JCCase)) { + return toTree; + } + //workaround for a bug in javac: if toTree is JCCase, it is never found + //slightly incorrectly, but more acceptable: stop after the switch + class Result extends RuntimeException { + private final JCTree newToTree; + + public Result(JCTree newTarget) { + this.newToTree = newTarget; + } + } + try { + new TreeScanner() { + @Override + public void visitSwitch(JCSwitch tree) { + if (tree.cases.contains(toTree)) { + throw new Result(tree); + } + super.visitSwitch(tree); + } + + @Override + public void visitSwitchExpression(JCSwitchExpression tree) { + if (tree.cases.contains(toTree)) { + throw new Result(tree); + } + super.visitSwitchExpression(tree); + } + }.scan(root); + + return toTree; + } catch (Result r) { + return r.newToTree; + } + } } diff --git a/nbbuild/javadoctools/apichanges-1.1.dtd b/nbbuild/javadoctools/apichanges-1.1.dtd new file mode 100644 index 000000000000..f49c07db2a39 --- /dev/null +++ b/nbbuild/javadoctools/apichanges-1.1.dtd @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + +%xhtml; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +