Skip to content

Commit

Permalink
Fix for JavacTypeBinding.getTypeArguments
Browse files Browse the repository at this point in the history
The main goal of this PR is to get the quick fix for the following case
working:

```java
public class MyClass extends java.util.List {

}
```

I needed to redo a lot of the type variable and method param/arg code in
order to accomplish this.

Signed-off-by: David Thompson <[email protected]>
  • Loading branch information
datho7561 authored and mickaelistria committed Sep 18, 2024
1 parent 22d4243 commit 69f4d0a
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import com.sun.tools.javac.code.Type.MethodType;
import com.sun.tools.javac.code.Type.ModuleType;
import com.sun.tools.javac.code.Type.PackageType;
import com.sun.tools.javac.code.Type.TypeVar;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.tree.JCTree;
Expand Down Expand Up @@ -149,6 +150,12 @@ public JavacPackageBinding getPackageBinding(PackageSymbol packageSymbol) {
//
private Map<String, JavacTypeBinding> typeBinding = new HashMap<>();
public JavacTypeBinding getTypeBinding(com.sun.tools.javac.code.Type type) {
if (type instanceof com.sun.tools.javac.code.Type.TypeVar typeVar) {
return getTypeVariableBinding(typeVar);
}
if (type == null || type == com.sun.tools.javac.code.Type.noType) {
return null;
}
if (type instanceof ErrorType errorType
&& (errorType.getOriginalType() != com.sun.tools.javac.code.Type.noType)
&& !(errorType.getOriginalType() instanceof com.sun.tools.javac.code.Type.MethodType)
Expand All @@ -166,8 +173,8 @@ public JavacTypeBinding getTypeBinding(com.sun.tools.javac.code.Type type) {
}
//
private Map<String, JavacTypeVariableBinding> typeVariableBindings = new HashMap<>();
public JavacTypeVariableBinding getTypeVariableBinding(TypeVariableSymbol typeVariableSymbol) {
JavacTypeVariableBinding newInstance = new JavacTypeVariableBinding(typeVariableSymbol) { };
public JavacTypeVariableBinding getTypeVariableBinding(TypeVar typeVar) {
JavacTypeVariableBinding newInstance = new JavacTypeVariableBinding(typeVar, (TypeVariableSymbol)typeVar.tsym, JavacBindingResolver.this) { };
typeVariableBindings.putIfAbsent(newInstance.getKey(), newInstance);
return typeVariableBindings.get(newInstance.getKey());
}
Expand Down Expand Up @@ -200,6 +207,13 @@ public IBinding getBinding(final Symbol owner, final com.sun.tools.javac.code.Ty
return getPackageBinding(other);
} else if (owner instanceof ModuleSymbol typeSymbol) {
return getModuleBinding(typeSymbol);
} else if (owner instanceof Symbol.TypeVariableSymbol typeVariableSymbol) {
if (type instanceof TypeVar typeVar) {
return getTypeVariableBinding(typeVar);
} else if (typeVariableSymbol.type instanceof TypeVar typeVar) {
return getTypeVariableBinding(typeVar);
}
// without the type there is not much we can do; fallthrough to null
} else if (owner instanceof TypeSymbol typeSymbol) {
return getTypeBinding(typeSymbol.type);
} else if (owner instanceof final MethodSymbol other) {
Expand Down Expand Up @@ -797,7 +811,7 @@ public ITypeBinding resolveExpressionType(Expression expr) {
IMethodBinding resolveConstructor(ClassInstanceCreation expression) {
return (IMethodBinding)resolveCached(expression, (n) -> resolveConstructorImpl((ClassInstanceCreation)n));
}

private IMethodBinding resolveConstructorImpl(ClassInstanceCreation expression) {
resolve();
return this.converter.domToJavac.get(expression) instanceof JCNewClass jcExpr
Expand All @@ -811,7 +825,7 @@ private IMethodBinding resolveConstructorImpl(ClassInstanceCreation expression)
IMethodBinding resolveConstructor(ConstructorInvocation invocation) {
return (IMethodBinding)resolveCached(invocation, (n) -> resolveConstructorImpl((ConstructorInvocation)n));
}

private IMethodBinding resolveConstructorImpl(ConstructorInvocation invocation) {
resolve();
JCTree javacElement = this.converter.domToJavac.get(invocation);
Expand Down Expand Up @@ -895,7 +909,7 @@ private java.util.List<TypeSymbol> getTypeArguments(final MethodInvocation metho
IModuleBinding resolveModule(ModuleDeclaration module) {
return (IModuleBinding)resolveCached(module, (n) -> resolveModuleImpl((ModuleDeclaration)n));
}

private IBinding resolveModuleImpl(ModuleDeclaration module) {
resolve();
JCTree javacElement = this.converter.domToJavac.get(module);
Expand Down Expand Up @@ -947,7 +961,7 @@ public Object getValueFromAttribute(Attribute attribute) {
IBinding resolveImport(ImportDeclaration importDeclaration) {
return resolveCached(importDeclaration, (n) -> resolveImportImpl((ImportDeclaration)n));
}

private IBinding resolveImportImpl(ImportDeclaration importDeclaration) {
var javac = this.converter.domToJavac.get(importDeclaration.getName());
if (javac instanceof JCFieldAccess fieldAccess) {
Expand Down Expand Up @@ -1008,7 +1022,7 @@ public ITypeBinding resolveWellKnownType(String typeName) {
IAnnotationBinding resolveAnnotation(Annotation annotation) {
return (IAnnotationBinding)resolveCached(annotation, (n) -> resolveAnnotationImpl((Annotation)n));
}

IAnnotationBinding resolveAnnotationImpl(Annotation annotation) {
resolve();
IBinding recipient = null;
Expand All @@ -1028,7 +1042,7 @@ IAnnotationBinding resolveAnnotationImpl(Annotation annotation) {
IBinding resolveReference(MethodRef ref) {
return resolveCached(ref, (n) -> resolveReferenceImpl((MethodRef)n));
}

private IBinding resolveReferenceImpl(MethodRef ref) {
resolve();
DocTreePath path = this.converter.findDocTreePath(ref);
Expand All @@ -1042,7 +1056,7 @@ private IBinding resolveReferenceImpl(MethodRef ref) {
IBinding resolveReference(MemberRef ref) {
return resolveCached(ref, (n) -> resolveReferenceImpl((MemberRef)n));
}

private IBinding resolveReferenceImpl(MemberRef ref) {
resolve();
DocTreePath path = this.converter.findDocTreePath(ref);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,8 @@ yield switch (rootCauseCode) {
case "compiler.err.expected4" -> IProblem.Syntax;
case "compiler.err.no.intf.expected.here" -> IProblem.SuperclassMustBeAClass;
case "compiler.err.intf.expected.here" -> IProblem.SuperInterfaceMustBeAnInterface;
case "compiler.err.method.does.not.override.superclass" -> IProblem.MethodMustOverrideOrImplement;
case "compiler.err.name.clash.same.erasure.no.override" -> IProblem.DuplicateMethodErasure;
default -> {
ILog.get().error("Could not convert diagnostic (" + diagnostic.getCode() + ")\n" + diagnostic);
yield 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
package org.eclipse.jdt.internal.javac.dom;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.eclipse.jdt.core.IJavaElement;
Expand All @@ -41,14 +42,17 @@
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.JCNoType;
import com.sun.tools.javac.code.Type.MethodType;
import com.sun.tools.javac.code.Type.TypeVar;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Names;

public abstract class JavacMethodBinding implements IMethodBinding {

private static final ITypeBinding[] NO_TYPE_ARGUMENTS = new ITypeBinding[0];
private static final ITypeBinding[] NO_TYPE_PARAMS = new ITypeBinding[0];

public final MethodSymbol methodSymbol;
final MethodType methodType;
Expand Down Expand Up @@ -211,11 +215,11 @@ private String resolveTypeName(com.sun.tools.javac.code.Type type, boolean binar
@Override
public String getKey() {
StringBuilder builder = new StringBuilder();
getKey(builder, this.methodSymbol, this.resolver);
getKey(builder, this.methodSymbol, this.methodType, this.resolver);
return builder.toString();
}

static void getKey(StringBuilder builder, MethodSymbol methodSymbol, JavacBindingResolver resolver) {
static void getKey(StringBuilder builder, MethodSymbol methodSymbol, MethodType methodType, JavacBindingResolver resolver) {
Symbol ownerSymbol = methodSymbol.owner;
while (ownerSymbol != null && !(ownerSymbol instanceof TypeSymbol)) {
ownerSymbol = ownerSymbol.owner;
Expand All @@ -230,17 +234,28 @@ static void getKey(StringBuilder builder, MethodSymbol methodSymbol, JavacBindin
builder.append(methodSymbol.getSimpleName());
}
if (methodSymbol.type != null) { // initializer
if (!methodSymbol.getTypeParameters().isEmpty()) {
if (methodType != null && !methodType.getTypeArguments().isEmpty()) {
builder.append('<');
for (var typeParam : methodType.getTypeArguments()) {
JavacTypeBinding.getKey(builder, typeParam, false);
}
builder.append('>');
} else if (!methodSymbol.getTypeParameters().isEmpty()) {
builder.append('<');
for (var typeParam : methodSymbol.getTypeParameters()) {
JavacTypeVariableBinding typeVarBinding = resolver.bindings.getTypeVariableBinding(typeParam);
builder.append(typeVarBinding.getKey());
builder.append(JavacTypeVariableBinding.getTypeVariableKey(typeParam));
}
builder.append('>');
}
builder.append('(');
for (var param : methodSymbol.getParameters()) {
JavacTypeBinding.getKey(builder, param.type, false);
if (methodType != null) {
for (var param : methodType.getParameterTypes()) {
JavacTypeBinding.getKey(builder, param, false);
}
} else {
for (var param : methodSymbol.getParameters()) {
JavacTypeBinding.getKey(builder, param.type, false);
}
}
builder.append(')');
if (!(methodSymbol.getReturnType() instanceof JCNoType)) {
Expand Down Expand Up @@ -358,6 +373,9 @@ public ITypeBinding[] getExceptionTypes() {

@Override
public ITypeBinding[] getTypeParameters() {
if (this.getTypeArguments().length != 0) {
return NO_TYPE_PARAMS;
}
return this.methodSymbol.getTypeParameters().stream()
.map(symbol -> this.resolver.bindings.getTypeBinding(symbol.type))
.toArray(ITypeBinding[]::new);
Expand All @@ -375,33 +393,78 @@ public boolean isGenericMethod() {

@Override
public boolean isParameterizedMethod() {
return !this.methodType.getTypeArguments().isEmpty();
return this.getTypeArguments().length != 0;
}

@Override
public ITypeBinding[] getTypeArguments() {
if (this.methodType.getTypeArguments().isEmpty()) {
// methodType.getTypeArguments() is always null
// we must compute the arguments ourselves by computing a mapping from the method with type variables
// to the specific instance that potentially has the type variables substituted for real types
Map<Type, Type> typeMap = new HashMap<>();
// scrape the parameters
for (int i = 0; i < methodSymbol.type.getParameterTypes().size(); i++) {
ListBuffer<Type> originalTypes = new ListBuffer<>();
ListBuffer<Type> substitutedTypes = new ListBuffer<>();
this.resolver.getTypes().adapt(
methodSymbol.type.getParameterTypes().get(i),
methodType.getParameterTypes().get(i), originalTypes, substitutedTypes);
List<Type> originalTypesList = originalTypes.toList();
List<Type> substitutedTypesList = substitutedTypes.toList();
for (int j = 0; j < originalTypesList.size(); j++) {
typeMap.putIfAbsent(originalTypesList.get(j), substitutedTypesList.get(j));
}
}
{
// also scrape the return type
ListBuffer<Type> originalTypes = new ListBuffer<>();
ListBuffer<Type> substitutedTypes = new ListBuffer<>();
this.resolver.getTypes().adapt(methodSymbol.type.getReturnType(), methodType.getReturnType(), originalTypes, substitutedTypes);
List<Type> originalTypesList = originalTypes.toList();
List<Type> substitutedTypesList = substitutedTypes.toList();
for (int j = 0; j < originalTypesList.size(); j++) {
typeMap.putIfAbsent(originalTypesList.get(j), substitutedTypesList.get(j));
}
}

boolean allEqual = true;
for (Map.Entry<Type, Type> entry : typeMap.entrySet()) {
if (!entry.getKey().equals(entry.getValue())) {
allEqual = false;
}
if (entry.getValue() == null) {
return NO_TYPE_ARGUMENTS;
}
}
if (allEqual) {
// methodType also contains all the type variables,
// which means it's also generic and no type arguments have been applied.
return NO_TYPE_ARGUMENTS;
}
return this.methodType.getTypeArguments().stream()
.map(this.resolver.bindings::getTypeBinding)

return this.methodSymbol.getTypeParameters().stream() //
.map(tvSym -> typeMap.get(tvSym.type)) //
.map(this.resolver.bindings::getTypeBinding) //
.toArray(ITypeBinding[]::new);
}

@Override
public IMethodBinding getMethodDeclaration() {
return this;
// This method intentionally converts the type to its generic type,
// i.e. drops the type arguments
// i.e. <code>this.<String>getValue(12);</code> will be converted back to <code><T> T getValue(int i) {</code>
return this.resolver.bindings.getMethodBinding(methodSymbol.type.asMethodType(), methodSymbol);
}

@Override
public boolean isRawMethod() {
return this.methodSymbol.type.isRaw();
return this.methodType.isRaw();
}

@Override
public boolean isSubsignature(IMethodBinding otherMethod) {
if (otherMethod instanceof JavacMethodBinding otherJavacMethod) {
return resolver.getTypes().isSubSignature(this.methodSymbol.asType(), otherJavacMethod.methodSymbol.asType());
return resolver.getTypes().isSubSignature(this.methodType, otherJavacMethod.methodType);
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,18 @@ public boolean isSynthetic() {

@Override
public IJavaElement getJavaElement() {
if (isTypeVariable()
&& this.typeSymbol != null
&& this.typeSymbol.owner instanceof ClassSymbol ownerSymbol
&& ownerSymbol.type != null
&& this.resolver.bindings.getTypeBinding(ownerSymbol.type).getJavaElement() instanceof IType ownerType
&& ownerType.getTypeParameter(this.getName()) != null) {
return ownerType.getTypeParameter(this.getName());
if (isTypeVariable() && this.typeSymbol != null) {
if (this.typeSymbol.owner instanceof ClassSymbol ownerSymbol
&& ownerSymbol.type != null
&& this.resolver.bindings.getTypeBinding(ownerSymbol.type).getJavaElement() instanceof IType ownerType
&& ownerType.getTypeParameter(this.getName()) != null) {
return ownerType.getTypeParameter(this.getName());
} else if (this.typeSymbol.owner instanceof MethodSymbol ownerSymbol
&& ownerSymbol.type != null
&& this.resolver.bindings.getMethodBinding(ownerSymbol.type.asMethodType(), ownerSymbol).getJavaElement() instanceof IMethod ownerMethod
&& ownerMethod.getTypeParameter(this.getName()) != null) {
return ownerMethod.getTypeParameter(this.getName());
}
}
if (this.resolver.javaProject == null) {
return null;
Expand Down Expand Up @@ -353,7 +358,10 @@ public IMethodBinding[] getDeclaredMethods() {
return StreamSupport.stream(l.spliterator(), false)
.filter(MethodSymbol.class::isInstance)
.map(MethodSymbol.class::cast)
.map(sym -> this.resolver.bindings.getMethodBinding(sym.type.asMethodType(), sym))
.map(sym -> {
Type.MethodType methodType = this.types.memberType(this.type, sym).asMethodType();
return this.resolver.bindings.getMethodBinding(methodType, sym);
})
.toArray(IMethodBinding[]::new);
}

Expand Down Expand Up @@ -394,7 +402,10 @@ public IMethodBinding getDeclaringMethod() {
Symbol parentSymbol = this.typeSymbol.owner;
do {
if (parentSymbol instanceof final MethodSymbol method) {
return this.resolver.bindings.getMethodBinding(method.type.asMethodType(), method);
if (!(method.type instanceof Type.MethodType methodType)) {
return null;
}
return this.resolver.bindings.getMethodBinding(methodType, method);
}
parentSymbol = parentSymbol.owner;
} while (parentSymbol != null);
Expand Down Expand Up @@ -428,7 +439,7 @@ public ITypeBinding getElementType() {

@Override
public ITypeBinding getErasure() {
return this.resolver.bindings.getTypeBinding(this.types.erasure(this.type));
return this.resolver.bindings.getTypeBinding(this.types.erasureRecursive(this.type));
}

@Override
Expand Down Expand Up @@ -530,7 +541,7 @@ public String getQualifiedName() {
}

StringBuilder res = new StringBuilder();
res.append(this.typeSymbol.getQualifiedName().toString());
res.append(this.typeSymbol.toString());
ITypeBinding[] typeArguments = this.getTypeArguments();
if (typeArguments.length > 0) {
res.append("<");
Expand Down
Loading

0 comments on commit 69f4d0a

Please sign in to comment.