Skip to content

Commit

Permalink
Completion for unimported types doesn't work
Browse files Browse the repository at this point in the history
- use MissingTypesGuesser to select all missing types and offer their completion

The related issue: eclipse-jdt#1502
  • Loading branch information
snjeza committed Dec 5, 2023
1 parent dc8021b commit d038141
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7165,5 +7165,72 @@ public static void foo() {
+ (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}",
result);
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1502
public void testGH1502() throws JavaModelException {
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy("/Completion/src/X.java", """
public class X {
void foo() {
new ArrayList<>().
}
}
""");

CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
requestor.allowAllRequiredProposals();

String str = this.workingCopies[0].getSource();
String completeBehind = "ArrayList<>().";
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, new NullProgressMonitor());

String result = requestor.getResults();
assertResults(
"add[METHOD_REF]{add(), Ljava.util.ArrayList<>;, (ITE;)V, add, (arg0, arg1), 61}\n"
+ "add[METHOD_REF]{add(), Ljava.util.ArrayList<>;, (TE;)Z, add, (arg0), 61}\n"
+ "addAll[METHOD_REF]{addAll(), Ljava.util.ArrayList<>;, (ILjava.util.Collection<+TE;>;)Z, addAll, (arg0, arg1), 61}\n"
+ "addAll[METHOD_REF]{addAll(), Ljava.util.ArrayList<>;, (Ljava.util.Collection<+TE;>;)Z, addAll, (arg0), 61}\n"
+ "clear[METHOD_REF]{clear(), Ljava.util.ArrayList<>;, ()V, clear, null, 61}\n"
+ "clone[METHOD_REF]{clone(), Ljava.util.ArrayList<>;, ()Ljava.lang.Object;, clone, null, 61}\n"
+ "contains[METHOD_REF]{contains(), Ljava.util.ArrayList<>;, (Ljava.lang.Object;)Z, contains, (arg0), 61}\n"
+ "containsAll[METHOD_REF]{containsAll(), Ljava.util.List<TE;>;, (Ljava.util.Collection<*>;)Z, containsAll, (arg0), 61}\n"
+ "ensureCapacity[METHOD_REF]{ensureCapacity(), Ljava.util.ArrayList<>;, (I)V, ensureCapacity, (arg0), 61}\n"
+ "equals[METHOD_REF]{equals(), Ljava.util.List<TE;>;, (Ljava.lang.Object;)Z, equals, (arg0), 61}\n"
+ "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 61}\n"
+ "forEach[METHOD_REF]{forEach(), Ljava.util.ArrayList<>;, (Ljava.util.function.Consumer<-TE;>;)V, forEach, (arg0), 61}\n"
+ "get[METHOD_REF]{get(), Ljava.util.ArrayList<>;, (I)TE;, get, (arg0), 61}\n"
+ "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, getClass, null, 61}\n"
+ "hashCode[METHOD_REF]{hashCode(), Ljava.util.List<TE;>;, ()I, hashCode, null, 61}\n"
+ "indexOf[METHOD_REF]{indexOf(), Ljava.util.ArrayList<>;, (Ljava.lang.Object;)I, indexOf, (arg0), 61}\n"
+ "isEmpty[METHOD_REF]{isEmpty(), Ljava.util.ArrayList<>;, ()Z, isEmpty, null, 61}\n"
+ "iterator[METHOD_REF]{iterator(), Ljava.util.ArrayList<>;, ()Ljava.util.Iterator<TE;>;, iterator, null, 61}\n"
+ "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.util.ArrayList<>;, (Ljava.lang.Object;)I, lastIndexOf, (arg0), 61}\n"
+ "listIterator[METHOD_REF]{listIterator(), Ljava.util.ArrayList<>;, ()Ljava.util.ListIterator<TE;>;, listIterator, null, 61}\n"
+ "listIterator[METHOD_REF]{listIterator(), Ljava.util.ArrayList<>;, (I)Ljava.util.ListIterator<TE;>;, listIterator, (arg0), 61}\n"
+ "modCount[FIELD_REF]{modCount, Ljava.util.AbstractList<TE;>;, I, modCount, null, 61}\n"
+ "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 61}\n"
+ "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 61}\n"
+ "parallelStream[METHOD_REF]{parallelStream(), Ljava.util.Collection<TE;>;, ()Ljava.util.stream.Stream<TE;>;, parallelStream, null, 61}\n"
+ "remove[METHOD_REF]{remove(), Ljava.util.ArrayList<>;, (I)TE;, remove, (arg0), 61}\n"
+ "remove[METHOD_REF]{remove(), Ljava.util.ArrayList<>;, (Ljava.lang.Object;)Z, remove, (arg0), 61}\n"
+ "removeAll[METHOD_REF]{removeAll(), Ljava.util.ArrayList<>;, (Ljava.util.Collection<*>;)Z, removeAll, (arg0), 61}\n"
+ "removeIf[METHOD_REF]{removeIf(), Ljava.util.ArrayList<>;, (Ljava.util.function.Predicate<-TE;>;)Z, removeIf, (arg0), 61}\n"
+ "removeRange[METHOD_REF]{removeRange(), Ljava.util.ArrayList<>;, (II)V, removeRange, (arg0, arg1), 61}\n"
+ "replaceAll[METHOD_REF]{replaceAll(), Ljava.util.ArrayList<>;, (Ljava.util.function.UnaryOperator<TE;>;)V, replaceAll, (arg0), 61}\n"
+ "retainAll[METHOD_REF]{retainAll(), Ljava.util.ArrayList<>;, (Ljava.util.Collection<*>;)Z, retainAll, (arg0), 61}\n"
+ "set[METHOD_REF]{set(), Ljava.util.ArrayList<>;, (ITE;)TE;, set, (arg0, arg1), 61}\n"
+ "size[METHOD_REF]{size(), Ljava.util.ArrayList<>;, ()I, size, null, 61}\n"
+ "sort[METHOD_REF]{sort(), Ljava.util.ArrayList<>;, (Ljava.util.Comparator<-TE;>;)V, sort, (arg0), 61}\n"
+ "spliterator[METHOD_REF]{spliterator(), Ljava.util.ArrayList<>;, ()Ljava.util.Spliterator<TE;>;, spliterator, null, 61}\n"
+ "stream[METHOD_REF]{stream(), Ljava.util.Collection<TE;>;, ()Ljava.util.stream.Stream<TE;>;, stream, null, 61}\n"
+ "subList[METHOD_REF]{subList(), Ljava.util.ArrayList<>;, (II)Ljava.util.List<TE;>;, subList, (arg0, arg1), 61}\n"
+ "toArray[METHOD_REF]{toArray(), Ljava.util.ArrayList<>;, ()[Ljava.lang.Object;, toArray, null, 61}\n"
+ "toArray[METHOD_REF]{toArray(), Ljava.util.ArrayList<>;, <T:Ljava.lang.Object;>([TT;)[TT;, toArray, (arg0), 61}\n"
+ "toString[METHOD_REF]{toString(), Ljava.util.AbstractCollection<TE;>;, ()Ljava.lang.String;, toString, null, 61}\n"
+ "trimToSize[METHOD_REF]{trimToSize(), Ljava.util.ArrayList<>;, ()V, trimToSize, null, 61}\n"
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 61}\n"
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 61}\n"
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 61}",
result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3165,23 +3165,39 @@ private void completionOnMemberAccess(ASTNode astNode, ASTNode enclosingNode, Bi
this.completionToken = access.token;

if (qualifiedBinding.problemId() == ProblemReasons.NotFound) {
// complete method members with missing return type
// class X {
// Missing f() {return null;}
// void foo() {
// f().|
// }
// }
if (this.assistNodeInJavadoc == 0 &&
(this.requestor.isAllowingRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_REF) ||
this.requestor.isAllowingRequiredProposals(CompletionProposal.METHOD_REF, CompletionProposal.TYPE_REF))) {
ProblemMethodBinding problemMethodBinding = (ProblemMethodBinding) qualifiedBinding;
findFieldsAndMethodsFromMissingReturnType(
problemMethodBinding.selector,
problemMethodBinding.parameters,
scope,
access,
insideTypeAnnotation);
if (qualifiedBinding instanceof ProblemMethodBinding) {
// complete method members with missing return type
// class X {
// Missing f() {return null;}
// void foo() {
// f().|
// }
// }
ProblemMethodBinding problemMethodBinding = (ProblemMethodBinding) qualifiedBinding;
findFieldsAndMethodsFromMissingReturnType(problemMethodBinding.selector,
problemMethodBinding.parameters, scope, access, insideTypeAnnotation);
} else if (access.receiver instanceof AllocationExpression expr) {
// complete on missing type
// class X {
// void foo() {
// new ArrayList().|
// }
// }
TypeReference type = expr.type;
char[] token = null;
if (type instanceof SingleTypeReference ref) {
token = ref.token;
}
if (type instanceof QualifiedTypeReference ref) {
token = ref.tokens[0];
}
if (token != null) {
findFieldsAndMethodsFromMissingType(type, scope, access, scope);
}
}
}
} else {
if (!access.isInsideAnnotation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
Expand Down Expand Up @@ -340,6 +348,9 @@ private TypeReference convert(ParameterizedQualifiedTypeReference typeRef) {
this.substituedTypes.put(convertedType, typeNames);
this.originalTypes.put(convertedType, typeName);
this.combinationsCount *= typeNames.length;
if (length > 0 && typeArguments[length - 1] == TypeReference.NO_TYPE_ARGUMENTS) {
convertedType.bits |= ASTNode.IsDiamond;
}
return convertedType;
}
}
Expand Down Expand Up @@ -385,6 +396,9 @@ private TypeReference convert(ParameterizedSingleTypeReference typeRef) {
this.substituedTypes.put(convertedType, typeNames);
this.originalTypes.put(convertedType, typeName);
this.combinationsCount *= typeNames.length;
if (typeArguments == TypeReference.NO_TYPE_ARGUMENTS) {
convertedType.bits |= ASTNode.IsDiamond;
}
return convertedType;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,12 @@ public TypeBinding resolveType(BlockScope scope) {
}
}

if (this.actualReceiverType == null || !this.actualReceiverType.isValidBinding())
if (this.actualReceiverType == null || !this.actualReceiverType.isValidBinding()) {
if (this.receiver.resolvedType != null && this.receiver.resolvedType.problemId() == ProblemReasons.NotFound) {
throw new CompletionNodeFound(this, this.receiver.resolvedType, scope);
}
throw new CompletionNodeFound();
}
else
throw new CompletionNodeFound(this, this.actualReceiverType, scope);
// array types are passed along to find the length field
Expand Down

0 comments on commit d038141

Please sign in to comment.