Skip to content

Commit 20132a6

Browse files
authored
Merge pull request #9260 from lahodaj/GITHUB-9199
Improving code completion in presence of local classes.
2 parents 4e7bd08 + 38c5a28 commit 20132a6

12 files changed

Lines changed: 467 additions & 46 deletions

File tree

java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,9 +1869,13 @@ private void insideMemberSelect(Env env) throws IOException {
18691869
if (el != null && (el.getKind().isClass() || el.getKind().isInterface())) {
18701870
if (parent.getKind() == Tree.Kind.NEW_CLASS && ((NewClassTree) parent).getIdentifier() == fa && prefix != null) {
18711871
String typeName = controller.getElementUtilities().getElementName(el, true) + "." + prefix; //NOI18N
1872-
TypeMirror tm = controller.getTreeUtilities().parseType(typeName, env.getScope().getEnclosingClass());
1872+
TypeMirror tm = controller.getTreeUtilities().parseType(typeName, env.getScope());
18731873
if (tm != null && tm.getKind() == TypeKind.DECLARED) {
1874-
addMembers(env, tm, ((DeclaredType) tm).asElement(), EnumSet.of(CONSTRUCTOR), null, inImport, insideNew, false, false, switchItemAdder);
1874+
TypeElement te = (TypeElement) ((DeclaredType) tm).asElement();
1875+
addMembers(env, tm, te, EnumSet.of(CONSTRUCTOR), null, inImport, insideNew, false, false, switchItemAdder);
1876+
if (shouldExcludeTypeInNewClass(te, env)) {
1877+
env.addToExcludes(te);
1878+
}
18751879
}
18761880
}
18771881
}
@@ -1917,9 +1921,13 @@ private void insideMemberSelect(Env env) throws IOException {
19171921
if (el != null && el.getKind() == PACKAGE) {
19181922
if (parent.getKind() == Tree.Kind.NEW_CLASS && ((NewClassTree) parent).getIdentifier() == fa && prefix != null) {
19191923
String typeName = controller.getElementUtilities().getElementName(el, true) + "." + prefix; //NOI18N
1920-
TypeMirror tm = controller.getTreeUtilities().parseType(typeName, env.getScope().getEnclosingClass());
1924+
TypeMirror tm = controller.getTreeUtilities().parseType(typeName, env.getScope());
19211925
if (tm != null && tm.getKind() == TypeKind.DECLARED) {
1922-
addMembers(env, tm, ((DeclaredType) tm).asElement(), EnumSet.of(CONSTRUCTOR), null, inImport, insideNew, false, false, switchItemAdder);
1926+
TypeElement te = (TypeElement) ((DeclaredType) tm).asElement();
1927+
addMembers(env, tm, te, EnumSet.of(CONSTRUCTOR), null, inImport, insideNew, false, false, switchItemAdder);
1928+
if (shouldExcludeTypeInNewClass(te, env)) {
1929+
env.addToExcludes(te);
1930+
}
19231931
}
19241932
}
19251933
if (exs != null && !exs.isEmpty()) {
@@ -2051,7 +2059,7 @@ private void insideMethodInvocation(Env env) throws IOException {
20512059
if (path.getParentPath().getLeaf().getKind() == Kind.CONSTANT_CASE_LABEL) {
20522060
CompilationController controller = env.getController();
20532061
controller.toPhase(Phase.RESOLVED);
2054-
TypeMirror tm = controller.getTreeUtilities().parseType(fullName(mi.getMethodSelect()), env.getScope().getEnclosingClass());
2062+
TypeMirror tm = controller.getTreeUtilities().parseType(fullName(mi.getMethodSelect()), env.getScope());
20552063
if (tm != null && tm.getKind() == TypeKind.DECLARED) {
20562064
TypeElement te = (TypeElement) ((DeclaredType) tm).asElement();
20572065
if (te.getKind() == RECORD) {
@@ -2062,7 +2070,7 @@ private void insideMethodInvocation(Env env) throws IOException {
20622070
if (ts != null && (ts.token().id() == JavaTokenId.LPAREN || ts.token().id() == JavaTokenId.COMMA)) {
20632071
if (componentType.getKind() == TypeKind.DECLARED) {
20642072
if (prefix != null) {
2065-
TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope().getEnclosingClass());
2073+
TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope());
20662074
if (ptm != null && ptm.getKind() == TypeKind.DECLARED) {
20672075
TypeElement pte = (TypeElement) ((DeclaredType) ptm).asElement();
20682076
if (pte != null && pte.getKind() == RECORD) {
@@ -2135,12 +2143,11 @@ private void insideNewClass(Env env) throws IOException {
21352143
? controller.getTypes().getDeclaredType(tel) : null;
21362144
TypeElement toExclude = null;
21372145
if (nc.getIdentifier().getKind() == Tree.Kind.IDENTIFIER && prefix != null) {
2138-
TypeMirror tm = controller.getTreeUtilities().parseType(prefix, env.getScope().getEnclosingClass());
2146+
TypeMirror tm = controller.getTreeUtilities().parseType(prefix, env.getScope());
21392147
if (tm != null && tm.getKind() == TypeKind.DECLARED) {
21402148
TypeElement te = (TypeElement) ((DeclaredType) tm).asElement();
21412149
addMembers(env, tm, te, EnumSet.of(CONSTRUCTOR), base, false, true, false);
2142-
if ((te.getTypeParameters().isEmpty() || SourceVersion.RELEASE_5.compareTo(controller.getSourceVersion()) > 0)
2143-
&& !hasAccessibleInnerClassConstructor(te, env.getScope(), controller.getTrees())) {
2150+
if (shouldExcludeTypeInNewClass(te, env)) {
21442151
toExclude = te;
21452152
}
21462153
}
@@ -2227,6 +2234,13 @@ private void insideNewClass(Env env) throws IOException {
22272234
}
22282235
}
22292236

2237+
private boolean shouldExcludeTypeInNewClass(TypeElement te, Env env) throws IOException {
2238+
CompilationController controller = env.getController();
2239+
2240+
return (te.getTypeParameters().isEmpty() || SourceVersion.RELEASE_5.compareTo(controller.getSourceVersion()) > 0)
2241+
&& !hasAccessibleInnerClassConstructor(te, env.getScope(), controller.getTrees());
2242+
}
2243+
22302244
private void insideTry(Env env) throws IOException {
22312245
CompilationController controller = env.getController();
22322246
TokenSequence<JavaTokenId> last = findLastNonWhitespaceToken(env, env.getPath().getLeaf(), env.getOffset());
@@ -2550,7 +2564,7 @@ private void insideCase(Env env) throws IOException {
25502564
} else {
25512565
if (env.getController().getSourceVersion().compareTo(RELEASE_21) >= 0) {
25522566
if (prefix != null) {
2553-
TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope().getEnclosingClass());
2567+
TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope());
25542568
if (ptm != null && ptm.getKind() == TypeKind.DECLARED) {
25552569
TypeElement pte = (TypeElement) ((DeclaredType) ptm).asElement();
25562570
if (pte != null && pte.getKind() == RECORD) {
@@ -2754,7 +2768,7 @@ private void insideTypeCheck(Env env) throws IOException {
27542768
TokenSequence<JavaTokenId> ts = findLastNonWhitespaceToken(env, iot, env.getOffset());
27552769
if (ts != null && ts.token().id() == JavaTokenId.INSTANCEOF) {
27562770
if (prefix != null && controller.getSourceVersion().compareTo(RELEASE_21) >= 0) {
2757-
TypeMirror tm = controller.getTreeUtilities().parseType(prefix, env.getScope().getEnclosingClass());
2771+
TypeMirror tm = controller.getTreeUtilities().parseType(prefix, env.getScope());
27582772
if (tm != null && tm.getKind() == TypeKind.DECLARED) {
27592773
TypeElement te = (TypeElement) ((DeclaredType) tm).asElement();
27602774
if (te != null && te.getKind() == RECORD) {
@@ -3485,7 +3499,7 @@ private void insideDeconstructionRecordPattern(final Env env) throws IOException
34853499
TypeMirror componentType = recordComponents.get(size - 1).getAccessor().getReturnType();
34863500
if (componentType.getKind() == TypeKind.DECLARED) {
34873501
if (prefix != null) {
3488-
TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope().getEnclosingClass());
3502+
TypeMirror ptm = controller.getTreeUtilities().parseType(prefix, env.getScope());
34893503
if (ptm != null && ptm.getKind() == TypeKind.DECLARED) {
34903504
TypeElement pte = (TypeElement) ((DeclaredType) ptm).asElement();
34913505
if (pte != null && pte.getKind() == RECORD) {
@@ -4088,6 +4102,9 @@ private void addMembers(final Env env, final TypeMirror type, final Element elem
40884102
ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() {
40894103
@Override
40904104
public boolean accept(Element e, TypeMirror t) {
4105+
if (env.getExcludes() != null && env.getExcludes().contains(e)) {
4106+
return false;
4107+
}
40914108
switch (simplifyElementKind(e.getKind())) {
40924109
case FIELD:
40934110
if (!startsWith(env, e.getSimpleName().toString())) {
@@ -4876,7 +4893,6 @@ public boolean accept(Element e, TypeMirror t) {
48764893
private void addAttributeValues(Env env, Element element, AnnotationMirror annotation, ExecutableElement member) throws IOException {
48774894
CompilationController controller = env.getController();
48784895
TreeUtilities tu = controller.getTreeUtilities();
4879-
ElementUtilities eu = controller.getElementUtilities();
48804896
for (javax.annotation.processing.Completion completion : SourceUtils.getAttributeValueCompletions(controller, element, annotation, member, env.getPrefix())) {
48814897
String value = completion.getValue().trim();
48824898
if (value.length() > 0 && startsWith(env, value)) {
@@ -4889,7 +4905,7 @@ private void addAttributeValues(Env env, Element element, AnnotationMirror annot
48894905
CharSequence fqn = ((TypeElement) ((DeclaredType) type).asElement()).getQualifiedName();
48904906
if (JAVA_LANG_CLASS.contentEquals(fqn)) {
48914907
String name = value.endsWith(".class") ? value.substring(0, value.length() - 6) : value; //NOI18N
4892-
TypeMirror tm = tu.parseType(name, eu.outermostTypeElement(element));
4908+
TypeMirror tm = tu.parseType(name, env.getScope());
48934909
typeElement = tm != null && tm.getKind() == TypeKind.DECLARED ? (TypeElement) ((DeclaredType) tm).asElement() : null;
48944910
if (typeElement != null && startsWith(env, typeElement.getSimpleName().toString())) {
48954911
env.addToExcludes(typeElement);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Local
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Local()
2+
Local(int i)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Local(int i)

java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTask121FeaturesTest.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,61 @@ public void testRecordPatternCompletion_5() throws Exception {
6464
performTest("RecordPattern", 1107, null, "AutoCompletion_RecordPattern_5.pass", SOURCE_LEVEL);
6565
}
6666

67+
public void testRecordPatternLocalClass1() throws Exception {
68+
performTest("Method", 935,
69+
"""
70+
record Local(int i) {}
71+
Object o = null;
72+
switch (o) {
73+
case Loc""",
74+
"LocalClass.pass",
75+
SOURCE_LEVEL);
76+
}
77+
78+
public void testRecordPatternLocalClass2() throws Exception {
79+
performTest("Method", 935,
80+
"""
81+
record Local(int i) {}
82+
Object o = null;
83+
switch (o) {
84+
case Local""",
85+
"LocalRecordPattern.pass",
86+
SOURCE_LEVEL);
87+
}
88+
89+
public void testRecordPatternLocalClass3() throws Exception {
90+
performTest("Method", 935,
91+
"""
92+
record Local(int i) {}
93+
Object o = null;
94+
boolean b = o instanceof Local""",
95+
"LocalRecordPattern.pass",
96+
SOURCE_LEVEL);
97+
}
98+
99+
public void testRecordPatternLocalClass4() throws Exception {
100+
performTest("Method", 935,
101+
"""
102+
record Local(int i) {}
103+
record Box(Local l) {}
104+
Object o = null;
105+
boolean b = o instanceof Box(Local""",
106+
"LocalRecordPattern.pass",
107+
SOURCE_LEVEL);
108+
}
109+
110+
public void testRecordPatternLocalClass5() throws Exception {
111+
performTest("Method", 935,
112+
"""
113+
record Local(int i) {}
114+
record Box(Local l) {}
115+
Object o = null;
116+
switch (o) {
117+
case Box(Local""",
118+
"LocalRecordPattern.pass",
119+
SOURCE_LEVEL);
120+
}
121+
67122
public void testCaseLabels_1() throws Exception {
68123
performTest("SwitchPatternMatching", 971, null, "AutoCompletion_CaseLabels_PatternMatchingSwitch_1.pass", SOURCE_LEVEL);
69124
}

java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaCompletionTaskAdvancedTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,4 +958,35 @@ public void testGenericMethod1() throws Exception {
958958
public void testGenericMethod2() throws Exception {
959959
performTest("GenericMethodInvocation", 1231, "run2(\"\", arg", "GenericMethodInvocation2.pass");
960960
}
961+
962+
public void testLocalClassConstructor1() throws Exception {
963+
performTest("Method", 935,
964+
"""
965+
class Local {
966+
Local() {}
967+
Local(int i) {}
968+
}
969+
new Local""",
970+
"LocalClassConstructor.pass");
971+
}
972+
973+
public void testLocalClassConstructor2() throws Exception {
974+
performTest("Method", 935,
975+
"""
976+
class LocalOuter {
977+
static class Local {
978+
Local() {}
979+
Local(int i) {}
980+
}
981+
}
982+
new LocalOuter.Local""",
983+
"LocalClassConstructor.pass");
984+
}
985+
986+
public void testConstructorFromPackage() throws Exception {
987+
performTest("Method", 935,
988+
"""
989+
new java.lang.String""",
990+
"stringConstructors.pass");
991+
}
961992
}

java/java.source.base/apichanges.xml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,25 @@
1919
under the License.
2020
2121
-->
22-
<!DOCTYPE apichanges PUBLIC "-//NetBeans//DTD API changes list 1.0//EN" "../../nbbuild/javadoctools/apichanges.dtd">
22+
<!DOCTYPE apichanges PUBLIC "-//NetBeans//DTD API changes list 1.1//EN" "../../nbbuild/javadoctools/apichanges-1.1.dtd">
2323
<apichanges>
2424
<apidefs>
2525
<apidef name="javasource_base">Java Source API</apidef>
2626
</apidefs>
2727
<changes>
28+
<change id="parseTypeScope">
29+
<api name="javasource_base" />
30+
<summary>Adding TreeUtilities.parseType(String, Scope) method overload</summary>
31+
<version major="1" minor="2.84"/>
32+
<date day="17" month="3" year="2026"/>
33+
<author login="jlahoda"/>
34+
<compatibility addition="yes" binary="compatible" source="compatible"/>
35+
<description>
36+
Adding TreeUtilities.parseType(String, Scope) method overload
37+
to support more exact type parsing.
38+
</description>
39+
<class name="TreeUtilities" package="org.netbeans.api.java.source"/>
40+
</change>
2841
<change id="ModuleImports">
2942
<api name="javasource_base" />
3043
<summary>API Support for module imports</summary>

java/java.source.base/nbproject/project.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ javadoc.name=Java Source Base
2323
javadoc.title=Java Source Base
2424
javadoc.arch=${basedir}/arch.xml
2525
javadoc.apichanges=${basedir}/apichanges.xml
26-
spec.version.base=2.83.0
26+
spec.version.base=2.84.0
2727
test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/nb-javac-api.jar
2828
test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\
2929
${o.n.core.dir}/lib/boot.jar:\

java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,26 @@ public TypeMirror parseType(String expr, TypeElement scope) {
700700
}
701701
}
702702

703+
/**Parses given type in given context.
704+
*
705+
* @param expr type specification
706+
* @param scope in which simple names should be resolved
707+
* @return parsed {@link TypeMirror} or null if the given specification cannot be parsed
708+
* @since 2.84
709+
*/
710+
public TypeMirror parseType(String expr, Scope scope) {
711+
Env<AttrContext> env = getEnv(scope);
712+
if (scope instanceof NBScope && ((NBScope)scope).areAccessibilityChecksDisabled()) {
713+
NBResolve.instance(info.impl.getJavacTask().getContext()).disableAccessibilityChecks();
714+
}
715+
try {
716+
Tree type = parseType(expr);
717+
return attributeTree(info.impl.getJavacTask(), env.toplevel, (JCTree) type, scope, true, new ArrayList<>());
718+
} finally {
719+
NBResolve.instance(info.impl.getJavacTask().getContext()).restoreAccessbilityChecks();
720+
}
721+
}
722+
703723
/**Parses given type in given context.
704724
*
705725
* @param expr type specification
@@ -922,7 +942,7 @@ public TypeMirror attributeTree(Tree tree, Scope scope) {
922942
NBResolve.instance(info.impl.getJavacTask().getContext()).disableAccessibilityChecks();
923943
}
924944
try {
925-
return attributeTree(info.impl.getJavacTask(), env.toplevel, (JCTree) tree, scope, new ArrayList<>());
945+
return attributeTree(info.impl.getJavacTask(), env.toplevel, (JCTree) tree, scope, false, new ArrayList<>());
926946
} finally {
927947
NBResolve.instance(info.impl.getJavacTask().getContext()).restoreAccessbilityChecks();
928948
}
@@ -949,7 +969,7 @@ public TypeMirror reattributeTree(Tree tree, Scope scope) {
949969
NBResolve.instance(info.impl.getJavacTask().getContext()).disableAccessibilityChecks();
950970
}
951971
try {
952-
return attributeTree(info.impl.getJavacTask(), env.toplevel, (JCTree)tree, scope, new ArrayList<>());
972+
return attributeTree(info.impl.getJavacTask(), env.toplevel, (JCTree)tree, scope, false, new ArrayList<>());
953973
} finally {
954974
NBResolve.instance(info.impl.getJavacTask().getContext()).restoreAccessbilityChecks();
955975
}
@@ -968,7 +988,7 @@ public Scope reattributeTreeTo(Tree tree, Scope scope, Tree to) {
968988
}
969989

970990
//from org/netbeans/modules/java/hints/spiimpl/Utilities.java:
971-
private static TypeMirror attributeTree(JavacTaskImpl jti, CompilationUnitTree cut, Tree tree, Scope scope, final List<Diagnostic<? extends JavaFileObject>> errors) {
991+
private static TypeMirror attributeTree(JavacTaskImpl jti, CompilationUnitTree cut, Tree tree, Scope scope, boolean attributeAsType, final List<Diagnostic<? extends JavaFileObject>> errors) {
972992
Log log = Log.instance(jti.getContext());
973993
JavaFileObject prev = log.useSource(new DummyJFO());
974994
Log.DiagnosticHandler discardHandler = log.new DiscardDiagnosticHandler() {
@@ -986,6 +1006,9 @@ public void reportReady(JCDiagnostic diag) {
9861006
try {
9871007
Attr attr = Attr.instance(jti.getContext());
9881008
Env<AttrContext> env = getEnv(scope);
1009+
if (attributeAsType) {
1010+
return attr.attribType((JCTree) tree, env);
1011+
}
9891012
if (tree instanceof JCExpression)
9901013
return attr.attribExpr((JCTree) tree,env, Type.noType);
9911014
return attr.attribStat((JCTree) tree,env);

0 commit comments

Comments
 (0)