Skip to content

Commit 4250827

Browse files
committed
Improve some support for completing missing type
Failback to type of declaration from Search if not resolved
1 parent 10b5cc0 commit 4250827

File tree

4 files changed

+195
-12
lines changed

4 files changed

+195
-12
lines changed

org.eclipse.jdt.core.javac/META-INF/MANIFEST.MF

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Bundle-Version: 1.0.0.qualifier
77
Fragment-Host: org.eclipse.jdt.core
88
Automatic-Module-Name: org.eclipse.jdt.core.javac
99
Require-Capability: osgi.ee; filter:="(&(osgi.ee=JavaSE)(version=23))"
10-
Import-Package: org.eclipse.jdt.core.dom
10+
Import-Package: org.eclipse.core.resources,
11+
org.eclipse.jdt.core.dom
1112
Export-Package: org.eclipse.jdt.internal.javac;x-friends:="org.eclipse.jdt.core.tests.javac",
1213
org.eclipse.jdt.internal.javac.dom;x-friends:="org.eclipse.jdt.core.tests.javac"

org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacBindingResolver.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@
4646
import com.sun.tools.javac.api.JavacTaskImpl;
4747
import com.sun.tools.javac.api.JavacTrees;
4848
import com.sun.tools.javac.code.Attribute;
49-
import com.sun.tools.javac.code.Attribute.Compound;
5049
import com.sun.tools.javac.code.Symbol;
50+
import com.sun.tools.javac.code.Symtab;
51+
import com.sun.tools.javac.code.TypeTag;
52+
import com.sun.tools.javac.code.Types;
53+
import com.sun.tools.javac.code.Attribute.Compound;
5154
import com.sun.tools.javac.code.Symbol.ClassSymbol;
5255
import com.sun.tools.javac.code.Symbol.MethodSymbol;
5356
import com.sun.tools.javac.code.Symbol.ModuleSymbol;
@@ -56,7 +59,6 @@
5659
import com.sun.tools.javac.code.Symbol.TypeSymbol;
5760
import com.sun.tools.javac.code.Symbol.TypeVariableSymbol;
5861
import com.sun.tools.javac.code.Symbol.VarSymbol;
59-
import com.sun.tools.javac.code.Symtab;
6062
import com.sun.tools.javac.code.Type.ArrayType;
6163
import com.sun.tools.javac.code.Type.ClassType;
6264
import com.sun.tools.javac.code.Type.ErrorType;
@@ -68,9 +70,8 @@
6870
import com.sun.tools.javac.code.Type.ModuleType;
6971
import com.sun.tools.javac.code.Type.PackageType;
7072
import com.sun.tools.javac.code.Type.TypeVar;
71-
import com.sun.tools.javac.code.TypeTag;
72-
import com.sun.tools.javac.code.Types;
7373
import com.sun.tools.javac.tree.JCTree;
74+
import com.sun.tools.javac.tree.TreeInfo;
7475
import com.sun.tools.javac.tree.JCTree.JCAnnotatedType;
7576
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
7677
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
@@ -95,7 +96,6 @@
9596
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
9697
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
9798
import com.sun.tools.javac.tree.JCTree.JCWildcard;
98-
import com.sun.tools.javac.tree.TreeInfo;
9999
import com.sun.tools.javac.util.Context;
100100
import com.sun.tools.javac.util.Names;
101101

@@ -1172,7 +1172,8 @@ IBinding resolveNameToJavac(Name name, JCTree tree) {
11721172

11731173
if (tree instanceof JCIdent ident && ident.sym != null) {
11741174
if (ident.type instanceof ErrorType errorType
1175-
&& errorType.getOriginalType() instanceof ErrorType) {
1175+
&& errorType.getOriginalType() instanceof ErrorType
1176+
&& !isRecoveringBindings()) {
11761177
return null;
11771178
}
11781179
if (isTypeDeclaration) {

org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/codeassist/DOMCompletionEngine.java

Lines changed: 166 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.eclipse.jdt.core.IField;
4646
import org.eclipse.jdt.core.IJavaElement;
4747
import org.eclipse.jdt.core.IJavaProject;
48+
import org.eclipse.jdt.core.IMember;
4849
import org.eclipse.jdt.core.IMemberValuePair;
4950
import org.eclipse.jdt.core.IMethod;
5051
import org.eclipse.jdt.core.IModuleDescription;
@@ -95,7 +96,6 @@
9596
import org.eclipse.jdt.core.dom.MethodInvocation;
9697
import org.eclipse.jdt.core.dom.MethodRef;
9798
import org.eclipse.jdt.core.dom.Modifier;
98-
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
9999
import org.eclipse.jdt.core.dom.ModuleDeclaration;
100100
import org.eclipse.jdt.core.dom.Name;
101101
import org.eclipse.jdt.core.dom.NormalAnnotation;
@@ -129,10 +129,12 @@
129129
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
130130
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
131131
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
132+
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
132133
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
133134
import org.eclipse.jdt.core.search.IJavaSearchConstants;
134135
import org.eclipse.jdt.core.search.SearchEngine;
135136
import org.eclipse.jdt.core.search.SearchPattern;
137+
import org.eclipse.jdt.core.search.TypeNameMatch;
136138
import org.eclipse.jdt.core.search.TypeNameMatchRequestor;
137139
import org.eclipse.jdt.internal.codeassist.impl.AssistOptions;
138140
import org.eclipse.jdt.internal.codeassist.impl.Keywords;
@@ -562,8 +564,13 @@ public void complete(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sour
562564
Expression fieldAccessExpr = fieldAccess.getExpression();
563565
ITypeBinding fieldAccessType = fieldAccessExpr.resolveTypeBinding();
564566
if (fieldAccessType != null) {
565-
processMembers(fieldAccess, fieldAccessExpr.resolveTypeBinding(), specificCompletionBindings, false);
566-
publishFromScope(specificCompletionBindings);
567+
if (!fieldAccessType.isRecovered()) {
568+
processMembers(fieldAccess, fieldAccessExpr.resolveTypeBinding(), specificCompletionBindings, false);
569+
publishFromScope(specificCompletionBindings);
570+
} else if (fieldAccessExpr instanceof MethodInvocation method &&
571+
this.unit.findDeclaringNode(method.resolveMethodBinding()) instanceof MethodDeclaration decl) {
572+
completeMissingType(decl.getReturnType2());
573+
}
567574
} else if (DOMCompletionUtil.findParent(fieldAccessExpr, new int[]{ ASTNode.METHOD_INVOCATION }) == null) {
568575
String packageName = ""; //$NON-NLS-1$
569576
if (fieldAccess.getExpression() instanceof FieldAccess parentFieldAccess
@@ -989,6 +996,26 @@ public void complete(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sour
989996
}
990997
} else if (qualifiedNameBinding instanceof IVariableBinding variableBinding) {
991998
ITypeBinding typeBinding = variableBinding.getType();
999+
if (typeBinding == null && unit.findDeclaringNode(variableBinding) instanceof VariableDeclaration decl) {
1000+
Type type = null;
1001+
if (decl instanceof SingleVariableDeclaration single) {
1002+
type = single.getType();
1003+
} else if (decl instanceof VariableDeclarationFragment fragment) {
1004+
if (fragment.getParent() instanceof FieldDeclaration field) {
1005+
type = field.getType();
1006+
} else if (fragment.getParent() instanceof VariableDeclarationExpression expr) {
1007+
type = expr.getType();
1008+
} else if (fragment.getParent() instanceof VariableDeclarationStatement stmt) {
1009+
type = stmt.getType();
1010+
}
1011+
}
1012+
if (type != null) {
1013+
typeBinding = type.resolveBinding();
1014+
}
1015+
if (typeBinding == null || typeBinding.isRecovered()) {
1016+
completeMissingType(type);
1017+
}
1018+
}
9921019
processMembers(qualifiedName, typeBinding, specificCompletionBindings, false);
9931020
publishFromScope(specificCompletionBindings);
9941021
suggestDefaultCompletions = false;
@@ -1519,6 +1546,8 @@ public void complete(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sour
15191546
}
15201547

15211548
checkCancelled();
1549+
} catch (JavaModelException e) {
1550+
ILog.get().error(e.getMessage(), e);
15221551
} finally {
15231552
this.requestor.endReporting();
15241553
if (this.monitor != null) {
@@ -1547,6 +1576,27 @@ private boolean isTypeInVariableDeclaration(ASTNode context) {
15471576
return false;
15481577
}
15491578

1579+
private void completeMissingType(Type type) throws JavaModelException {
1580+
if (type instanceof ParameterizedType parameterized) {
1581+
type = parameterized.getType();
1582+
}
1583+
final Type finalType = type;
1584+
var scope = SearchEngine.createJavaSearchScope(new IJavaElement[] { this.javaProject });
1585+
new SearchEngine(this.workingCopyOwner).searchAllTypeNames(null, SearchPattern.R_PREFIX_MATCH, type.toString().toCharArray(), SearchPattern.R_EXACT_MATCH, IJavaSearchConstants.TYPE, scope, new TypeNameMatchRequestor() {
1586+
@Override
1587+
public void acceptTypeNameMatch(TypeNameMatch match) {
1588+
processMembers(match.getType()).stream()
1589+
.map(member -> {
1590+
CompletionProposal typeProposal = toProposal(match.getType());
1591+
typeProposal.setReplaceRange(finalType.getStartPosition(), finalType.getStartPosition() + finalType.getLength());
1592+
typeProposal.setRelevance(member.getRelevance());
1593+
member.setRequiredProposals(new CompletionProposal[] { typeProposal });
1594+
return member;
1595+
}).forEach(DOMCompletionEngine.this.requestor::accept);
1596+
}
1597+
}, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor);
1598+
}
1599+
15501600
private void suggestSuperConstructors() {
15511601
if (this.requestor.isIgnored(CompletionProposal.METHOD_REF) || this.requestor.isIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION)) {
15521602
return;
@@ -2443,6 +2493,21 @@ private void processMembers(ITypeBinding typeBinding, Bindings scope,
24432493
}
24442494
}
24452495

2496+
private List<CompletionProposal> processMembers(IType type) {
2497+
IJavaElement[] children;
2498+
try {
2499+
children = type.getChildren();
2500+
} catch (JavaModelException ex) {
2501+
ILog.get().error(ex.getMessage(), ex);
2502+
children = new IJavaElement[0];
2503+
}
2504+
return Arrays.stream(children)
2505+
.filter(element -> element.getElementType() == IJavaElement.FIELD || element.getElementType() == IJavaElement.METHOD)
2506+
.filter(this::isVisible)
2507+
.map(this::toProposal)
2508+
.toList();
2509+
}
2510+
24462511
private String getSignature(IMethodBinding method) {
24472512
return method.getName() + '(' +
24482513
Arrays.stream(method.getParameterTypes()).map(ITypeBinding::getName).collect(Collectors.joining(","))
@@ -2904,7 +2969,7 @@ private CompletionProposal toProposal(IType type) {
29042969
if (packageDecl != null) {
29052970
packageName = packageDecl.getName().toString();
29062971
}
2907-
if (!packageName.equals(type.getPackageFragment().getElementName())) {
2972+
if (!packageName.equals(type.getPackageFragment().getElementName()) && !new String(res.getCompletion()).equals(type.getFullyQualifiedName())) {
29082973
// propose importing the type
29092974
res.setRequiredProposals(new CompletionProposal[] { toImportProposal(simpleName, signature, type.getPackageFragment().getElementName().toCharArray()) });
29102975
}
@@ -2931,7 +2996,58 @@ private CompletionProposal toSuperConstructorProposal(IMethodBinding superConstr
29312996

29322997
return res;
29332998
}
2934-
2999+
3000+
private CompletionProposal toProposal(IJavaElement element) {
3001+
if (element instanceof IType type) {
3002+
return toProposal(type);
3003+
}
3004+
DOMInternalCompletionProposal res = null;
3005+
IType parentType = (IType)element.getAncestor(IJavaElement.TYPE);
3006+
if (element instanceof IField field) {
3007+
res = createProposal(CompletionProposal.FIELD_REF);
3008+
res.setName(field.getElementName().toCharArray());
3009+
res.setCompletion(field.getElementName().toCharArray());
3010+
setRange(res);
3011+
res.setRelevance(RelevanceConstants.R_DEFAULT +
3012+
RelevanceConstants.R_RESOLVED +
3013+
RelevanceConstants.R_INTERESTING +
3014+
RelevanceConstants.R_NON_RESTRICTED);
3015+
}
3016+
if (element instanceof IMethod method) {
3017+
res = createProposal(CompletionProposal.METHOD_REF);
3018+
try {
3019+
res.setSignature(method.getSignature().toCharArray());
3020+
res.setParameterNames(Arrays.stream(method.getParameterNames()).map(String::toCharArray).toArray(char[][]::new));
3021+
res.setParameterTypeNames(Arrays.stream(method.getParameterTypes()).map(String::toCharArray).toArray(char[][]::new));
3022+
} catch (JavaModelException ex) {
3023+
ILog.get().error(ex.getMessage(), ex);
3024+
}
3025+
res.setName(method.getElementName().toCharArray());
3026+
res.setCompletion((method.getElementName() + "()").toCharArray());
3027+
setRange(res);
3028+
res.setRelevance(RelevanceConstants.R_DEFAULT +
3029+
RelevanceConstants.R_RESOLVED +
3030+
RelevanceConstants.R_INTERESTING +
3031+
RelevanceConstants.R_CASE +
3032+
RelevanceConstants.R_NON_STATIC +
3033+
RelevanceConstants.R_NON_RESTRICTED +
3034+
RelevanceConstants.R_NO_PROBLEMS);
3035+
}
3036+
if (res != null) {
3037+
if (element instanceof IMember member) {
3038+
try {
3039+
res.setFlags(member.getFlags());
3040+
} catch (JavaModelException ex) {
3041+
ILog.get().error(ex.getMessage(), ex);
3042+
}
3043+
}
3044+
res.setDeclarationSignature(SignatureUtils.createSignature(parentType).toCharArray());
3045+
res.setDeclarationTypeName(parentType.getFullyQualifiedName().toCharArray());
3046+
res.setDeclarationPackageName(element.getAncestor(IJavaElement.PACKAGE_FRAGMENT).getElementName().toCharArray());
3047+
}
3048+
return res;
3049+
}
3050+
29353051
private CompletionProposal toNewMethodProposal(ITypeBinding parentType, String newMethodName) {
29363052
DOMInternalCompletionProposal res = createProposal(CompletionProposal.POTENTIAL_METHOD_DECLARATION);
29373053
res.setDeclarationSignature(DOMCompletionEngineBuilder.getSignature(parentType));
@@ -3682,6 +3798,51 @@ private boolean isVisible(IBinding binding) {
36823798
return declaringClass.getPackage().isEqualTo(DOMCompletionUtil.findParentTypeDeclaration(this.toComplete).resolveBinding().getPackage());
36833799
}
36843800

3801+
private boolean isVisible(IJavaElement element) {
3802+
if (element == null) {
3803+
return false;
3804+
}
3805+
int flags;
3806+
try {
3807+
flags = element instanceof IType type ? type.getFlags() :
3808+
element instanceof IMethod method ? method.getFlags() :
3809+
element instanceof IField field ? field.getFlags() :
3810+
0;
3811+
} catch (JavaModelException ex) {
3812+
ILog.get().error(ex.getMessage(), ex);
3813+
flags = 0;
3814+
}
3815+
if (element instanceof IType type) {
3816+
if (Modifier.isPublic(flags)) {
3817+
return true;
3818+
}
3819+
if (Modifier.isPrivate(flags)) {
3820+
return type.equals(this.toComplete);
3821+
}
3822+
if (Modifier.isProtected(flags)) {
3823+
// TODO
3824+
}
3825+
return Objects.equals(type.getPackageFragment().getElementName(), this.unit.getPackage().getName().toString());
3826+
} else {
3827+
IType type = element instanceof IMethod method ? method.getDeclaringType() :
3828+
element instanceof IField field ? field.getDeclaringType() :
3829+
null;
3830+
if (!isVisible(type)) {
3831+
return false;
3832+
}
3833+
if (Modifier.isPublic(flags)) {
3834+
return true;
3835+
}
3836+
if (Modifier.isPrivate(flags)) {
3837+
return type.equals(this.toComplete);
3838+
}
3839+
if (Modifier.isProtected(flags)) {
3840+
// TODO
3841+
}
3842+
return Objects.equals(type.getPackageFragment().getElementName(), this.unit.getPackage().getName().toString());
3843+
}
3844+
}
3845+
36853846
private ITypeBinding getDeclaringClass(IBinding binding) {
36863847
return binding instanceof ITypeBinding typeBinding ? typeBinding :
36873848
binding instanceof IMethodBinding methodBinding ? methodBinding.getDeclaringClass() :
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Red Hat, Inc. and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*******************************************************************************/
11+
package org.eclipse.jdt.internal.codeassist;
12+
13+
import org.eclipse.jdt.core.IType;
14+
15+
public class SignatureUtils {
16+
17+
public static final String createSignature(IType type) {
18+
return type.getKey().replace('/', '.');
19+
}
20+
}

0 commit comments

Comments
 (0)