From d34d8af732cb7443979f6186de18004572fc37b8 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Wed, 24 Apr 2024 16:11:14 -0400 Subject: [PATCH] Improve Javac(Method|Type)Binding.getKey() (#273) Fixes #284 --------- Signed-off-by: David Thompson Signed-off-by: Rob Stryker Co-authored-by: Rob Stryker --- .../jdt/core/dom/JavacBindingResolver.java | 41 ++-- .../javac/dom/JavacAnnotationBinding.java | 2 +- .../dom/JavacMemberValuePairBinding.java | 2 +- .../javac/dom/JavacMethodBinding.java | 72 ++++--- .../internal/javac/dom/JavacTypeBinding.java | 176 +++++++++++------- .../javac/dom/JavacTypeVariableBinding.java | 43 +++++ .../javac/dom/JavacVariableBinding.java | 10 +- 7 files changed, 230 insertions(+), 116 deletions(-) create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacTypeVariableBinding.java diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacBindingResolver.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacBindingResolver.java index ea6f2cc1a89..843123d93e5 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacBindingResolver.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacBindingResolver.java @@ -137,15 +137,14 @@ private Optional symbol(JCTree value) { ITypeBinding resolveType(Type type) { resolve(); JCTree jcTree = this.converter.domToJavac.get(type); - final java.util.List typeArguments = getTypeArguments(type); if (jcTree instanceof JCIdent ident && ident.sym instanceof TypeSymbol typeSymbol) { - return new JavacTypeBinding(typeSymbol, this, typeArguments); + return new JavacTypeBinding(ident.type, this); } if (jcTree instanceof JCFieldAccess access && access.sym instanceof TypeSymbol typeSymbol) { - return new JavacTypeBinding(typeSymbol, this, typeArguments); + return new JavacTypeBinding(access.type, this); } if (jcTree instanceof JCPrimitiveTypeTree primitive) { - return new JavacTypeBinding(primitive.type, this, typeArguments); + return new JavacTypeBinding(primitive.type, this); } // return this.flowResult.stream().map(env -> env.enclClass) // .filter(Objects::nonNull) @@ -167,7 +166,7 @@ ITypeBinding resolveType(TypeDeclaration type) { resolve(); JCTree javacNode = this.converter.domToJavac.get(type); if (javacNode instanceof JCClassDecl jcClassDecl) { - return new JavacTypeBinding(jcClassDecl.sym, this, null); + return new JavacTypeBinding(jcClassDecl.type, this); } return null; } @@ -177,18 +176,18 @@ ITypeBinding resolveType(EnumDeclaration enumDecl) { resolve(); JCTree javacNode = this.converter.domToJavac.get(enumDecl); if (javacNode instanceof JCClassDecl jcClassDecl) { - return new JavacTypeBinding(jcClassDecl.sym, this, null); + return new JavacTypeBinding(jcClassDecl.type, this); } return null; } - public IBinding getBinding(final Symbol owner, final java.util.List typeArguments) { + public IBinding getBinding(final Symbol owner, final com.sun.tools.javac.code.Type type) { if (owner instanceof final PackageSymbol other) { return new JavacPackageBinding(other, this); - } else if (owner instanceof final TypeSymbol other) { - return new JavacTypeBinding(other, this, typeArguments); + } else if (owner instanceof TypeSymbol) { + return new JavacTypeBinding(type, this); } else if (owner instanceof final MethodSymbol other) { - return new JavacMethodBinding(other, this, typeArguments); + return new JavacMethodBinding(type.asMethodType(), other, this); } else if (owner instanceof final VarSymbol other) { return new JavacVariableBinding(other, this); } @@ -209,15 +208,14 @@ IVariableBinding resolveField(FieldAccess fieldAccess) { IMethodBinding resolveMethod(MethodInvocation method) { resolve(); JCTree javacElement = this.converter.domToJavac.get(method); - final java.util.List typeArguments = getTypeArguments(method); if (javacElement instanceof JCMethodInvocation javacMethodInvocation) { javacElement = javacMethodInvocation.getMethodSelect(); } if (javacElement instanceof JCIdent ident && ident.sym instanceof MethodSymbol methodSymbol) { - return new JavacMethodBinding(methodSymbol, this, typeArguments); + return new JavacMethodBinding(ident.type.asMethodType(), methodSymbol, this); } if (javacElement instanceof JCFieldAccess fieldAccess && fieldAccess.sym instanceof MethodSymbol methodSymbol) { - return new JavacMethodBinding(methodSymbol, this, typeArguments); + return new JavacMethodBinding(fieldAccess.type.asMethodType(), methodSymbol, this); } return null; } @@ -227,7 +225,7 @@ IMethodBinding resolveMethod(MethodDeclaration method) { resolve(); JCTree javacElement = this.converter.domToJavac.get(method); if (javacElement instanceof JCMethodDecl methodDecl) { - return new JavacMethodBinding(methodDecl.sym, this, null); + return new JavacMethodBinding(methodDecl.type.asMethodType(), methodDecl.sym, this); } return null; } @@ -239,18 +237,17 @@ IBinding resolveName(Name name) { if (tree == null) { tree = this.converter.domToJavac.get(name.getParent()); } - final java.util.List typeArguments = getTypeArguments(name); if (tree instanceof JCIdent ident && ident.sym != null) { - return getBinding(ident.sym, typeArguments); + return getBinding(ident.sym, ident.type); } if (tree instanceof JCFieldAccess fieldAccess && fieldAccess.sym != null) { - return getBinding(fieldAccess.sym, typeArguments); + return getBinding(fieldAccess.sym, fieldAccess.type); } if (tree instanceof JCClassDecl classDecl && classDecl.sym != null) { - return getBinding(classDecl.sym, typeArguments); + return getBinding(classDecl.sym, classDecl.type); } if (tree instanceof JCVariableDecl variableDecl && variableDecl.sym != null) { - return getBinding(variableDecl.sym, typeArguments); + return getBinding(variableDecl.sym, variableDecl.type); } return null; } @@ -272,7 +269,7 @@ public IPackageBinding resolvePackage(PackageDeclaration decl) { public ITypeBinding resolveExpressionType(Expression expr) { resolve(); return this.converter.domToJavac.get(expr) instanceof JCExpression jcExpr ? - new JavacTypeBinding(jcExpr.type, this, null) : + new JavacTypeBinding(jcExpr.type, this) : null; } @@ -356,7 +353,7 @@ public Object getValueFromAttribute(Attribute attribute) { if (attribute instanceof Attribute.Constant constant) { return constant.value; } else if (attribute instanceof Attribute.Class clazz) { - return new JavacTypeBinding(clazz.classType.tsym, this, null); + return new JavacTypeBinding(clazz.classType, this); } else if (attribute instanceof Attribute.Enum enumm) { return new JavacVariableBinding(enumm.value, this); } else if (attribute instanceof Attribute.Array array) { @@ -365,7 +362,7 @@ public Object getValueFromAttribute(Attribute attribute) { if (attribute instanceof Attribute.Constant constant) { return constant.value; } else if (attribute instanceof Attribute.Class clazz) { - return new JavacTypeBinding(clazz.classType.tsym, this, null); + return new JavacTypeBinding(clazz.classType, this); } else if (attribute instanceof Attribute.Enum enumerable) { return new JavacVariableBinding(enumerable.value, this); } diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacAnnotationBinding.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacAnnotationBinding.java index 10f6375bf02..307603f6edb 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacAnnotationBinding.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacAnnotationBinding.java @@ -93,7 +93,7 @@ public IMemberValuePairBinding[] getAllMemberValuePairs() { @Override public ITypeBinding getAnnotationType() { - return new JavacTypeBinding(this.annotation.type, this.resolver, null); + return new JavacTypeBinding(this.annotation.type, this.resolver); } @Override diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacMemberValuePairBinding.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacMemberValuePairBinding.java index ddf029fa576..e4ecc54c170 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacMemberValuePairBinding.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacMemberValuePairBinding.java @@ -29,7 +29,7 @@ public class JavacMemberValuePairBinding implements IMemberValuePairBinding { private final JavacBindingResolver resolver; public JavacMemberValuePairBinding(MethodSymbol key, Attribute value, JavacBindingResolver resolver) { - this.method = new JavacMethodBinding(key, resolver, null); + this.method = new JavacMethodBinding(key.type.asMethodType(), key, resolver); this.value = value; this.resolver = resolver; } diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacMethodBinding.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacMethodBinding.java index 131ef4e3dcb..19090278e72 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacMethodBinding.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacMethodBinding.java @@ -36,17 +36,21 @@ 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.JCNoType; +import com.sun.tools.javac.code.Type.MethodType; public class JavacMethodBinding implements IMethodBinding { + private static final ITypeBinding[] NO_TYPE_ARGUMENTS = new ITypeBinding[0]; + public final MethodSymbol methodSymbol; + private final MethodType methodType; final JavacBindingResolver resolver; - private final List typeArguments; - public JavacMethodBinding(MethodSymbol sym, JavacBindingResolver resolver, List typeArguments) { - this.methodSymbol = sym; + public JavacMethodBinding(MethodType methodType, MethodSymbol methodSymbol, JavacBindingResolver resolver) { + this.methodType = methodType; + this.methodSymbol = methodSymbol; this.resolver = resolver; - this.typeArguments = typeArguments; } @Override @@ -109,7 +113,7 @@ public boolean isSynthetic() { @Override public IJavaElement getJavaElement() { - IJavaElement parent = this.resolver.getBinding(this.methodSymbol.owner, null).getJavaElement(); + IJavaElement parent = this.resolver.getBinding(this.methodSymbol.owner, this.methodType).getJavaElement(); if (parent instanceof IType type) { return type.getMethod(this.methodSymbol.getSimpleName().toString(), this.methodSymbol.params().stream() @@ -123,30 +127,47 @@ public IJavaElement getJavaElement() { @Override public String getKey() { StringBuilder builder = new StringBuilder(); - getKey(builder, this.methodSymbol); + getKey(builder, this.methodSymbol, this.resolver); return builder.toString(); } - static void getKey(StringBuilder builder, MethodSymbol methodSymbol) { + static void getKey(StringBuilder builder, MethodSymbol methodSymbol, JavacBindingResolver resolver) { Symbol ownerSymbol = methodSymbol.owner; while (ownerSymbol != null && !(ownerSymbol instanceof TypeSymbol)) { ownerSymbol = ownerSymbol.owner; } if (ownerSymbol instanceof TypeSymbol ownerTypeSymbol) { - builder.append(ownerTypeSymbol.name); + JavacTypeBinding.getKey(builder, resolver.getTypes().erasure(ownerTypeSymbol.type), false); } else { throw new IllegalArgumentException("Method has no owning class"); } builder.append('.'); - // TODO: what is a selector? why is it added? - for (var typeParam : methodSymbol.getTypeParameters()) { - builder.append(typeParam.getQualifiedName()); + if (!methodSymbol.isConstructor()) { + builder.append(methodSymbol.getSimpleName()); + } + if (!methodSymbol.getTypeParameters().isEmpty()) { + builder.append('<'); + for (var typeParam : methodSymbol.getTypeParameters()) { + JavacTypeVariableBinding typeVarBinding = new JavacTypeVariableBinding(typeParam); + builder.append(typeVarBinding.getKey()); + } + builder.append('>'); } + builder.append('('); for (var param : methodSymbol.getParameters()) { - builder.append(param.getQualifiedName()); + JavacTypeBinding.getKey(builder, param.type, false); } - for (var thrownException : methodSymbol.getThrownTypes()) { - builder.append(thrownException.tsym.getQualifiedName()); + builder.append(')'); + if (!(methodSymbol.getReturnType() instanceof JCNoType)) { + JavacTypeBinding.getKey(builder, methodSymbol.getReturnType(), false); + } + if ( + methodSymbol.getThrownTypes().stream().anyMatch(a -> !a.getParameterTypes().isEmpty()) + ) { + builder.append('^'); + for (var thrownException : methodSymbol.getThrownTypes()) { + builder.append(thrownException.tsym.getQualifiedName()); + } } } @@ -188,7 +209,7 @@ public ITypeBinding getDeclaringClass() { Symbol parentSymbol = this.methodSymbol.owner; do { if (parentSymbol instanceof ClassSymbol clazz) { - return new JavacTypeBinding(clazz, this.resolver, null); + return new JavacTypeBinding(clazz.type, this.resolver); } parentSymbol = parentSymbol.owner; } while (parentSymbol != null); @@ -201,7 +222,7 @@ public IBinding getDeclaringMember() { return null; } if (this.methodSymbol.owner instanceof MethodSymbol methodSymbol) { - return new JavacMethodBinding(methodSymbol, resolver, null); + return new JavacMethodBinding(methodSymbol.type.asMethodType(), methodSymbol, resolver); } else if (this.methodSymbol.owner instanceof VarSymbol variableSymbol) { return new JavacVariableBinding(variableSymbol, resolver); } @@ -225,18 +246,18 @@ public IAnnotationBinding[] getParameterAnnotations(int paramIndex) { public ITypeBinding[] getParameterTypes() { return this.methodSymbol.params().stream() .map(param -> param.type) - .map(type -> new JavacTypeBinding(type, this.resolver, /* TODO */ null)) + .map(type -> new JavacTypeBinding(type, this.resolver)) .toArray(ITypeBinding[]::new); } @Override public ITypeBinding getDeclaredReceiverType() { - return new JavacTypeBinding(this.methodSymbol.getReceiverType(), this.resolver, /* TODO */ null); + return new JavacTypeBinding(this.methodSymbol.getReceiverType(), this.resolver); } @Override public ITypeBinding getReturnType() { - return new JavacTypeBinding(this.methodSymbol.getReturnType(), this.resolver, /* TODO */ null); + return new JavacTypeBinding(this.methodSymbol.getReturnType(), this.resolver); } @SuppressWarnings("unchecked") @@ -254,7 +275,7 @@ public ITypeBinding[] getExceptionTypes() { @Override public ITypeBinding[] getTypeParameters() { return this.methodSymbol.getTypeParameters().stream() - .map(symbol -> new JavacTypeBinding(symbol, this.resolver, null)) + .map(symbol -> new JavacTypeBinding(symbol.type, this.resolver)) .toArray(ITypeBinding[]::new); } @@ -265,18 +286,21 @@ public boolean isAnnotationMember() { @Override public boolean isGenericMethod() { - return this.typeArguments == null && !this.methodSymbol.getTypeParameters().isEmpty(); + return this.methodType.getTypeArguments().isEmpty() && !this.methodSymbol.getTypeParameters().isEmpty(); } @Override public boolean isParameterizedMethod() { - return this.typeArguments != null; + return !this.methodType.getTypeArguments().isEmpty(); } @Override public ITypeBinding[] getTypeArguments() { - return this.typeArguments.stream() - .map(symbol -> new JavacTypeBinding(symbol, this.resolver, null)) + if (this.methodType.getTypeArguments().isEmpty()) { + return NO_TYPE_ARGUMENTS; + } + return this.methodType.getTypeArguments().stream() + .map(type -> new JavacTypeBinding(type, this.resolver)) .toArray(ITypeBinding[]::new); } diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacTypeBinding.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacTypeBinding.java index 15c2d7d76ed..e69e084cfae 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacTypeBinding.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacTypeBinding.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.jdt.internal.javac.dom; -import java.util.List; import java.util.Objects; import java.util.stream.StreamSupport; @@ -28,20 +27,23 @@ import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.JavacBindingResolver; import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.codegen.ConstantPool; import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Kinds; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; +import com.sun.tools.javac.code.Symbol.TypeVariableSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.ArrayType; +import com.sun.tools.javac.code.Type.ClassType; import com.sun.tools.javac.code.Type.TypeVar; import com.sun.tools.javac.code.Type.WildcardType; -import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError; import com.sun.tools.javac.code.Types; -import com.sun.tools.javac.code.Kinds; +import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError; public class JavacTypeBinding implements ITypeBinding { @@ -50,23 +52,17 @@ public class JavacTypeBinding implements ITypeBinding { final JavacBindingResolver resolver; public final TypeSymbol typeSymbol; private final Types types; - private final List typeArguments; - - /** - * - * @param classSymbol - * @param resolver - * @param typeArguments the type arguments (NOT the type parameters) or null if this is not a parameterized type - */ - public JavacTypeBinding(final TypeSymbol classSymbol, final JavacBindingResolver resolver, final List typeArguments) { - this.typeSymbol = classSymbol; - this.resolver = resolver; - this.types = Types.instance(this.resolver.context); - this.typeArguments = typeArguments; + private final Type type; + + public JavacTypeBinding(final Type type, final JavacBindingResolver resolver) { + this(type, type.tsym, resolver); } - public JavacTypeBinding(final Type type, final JavacBindingResolver resolver, final List typeArguments) { - this(type.tsym, resolver, typeArguments); + private JavacTypeBinding(final Type type, final TypeSymbol typeSymbol, JavacBindingResolver resolver) { + this.type = type; + this.typeSymbol = typeSymbol; + this.resolver = resolver; + this.types = Types.instance(this.resolver.context); } @Override @@ -113,20 +109,42 @@ public IType getJavaElement() { @Override public String getKey() { + return getKey(this.type); + } + public String getKey(Type t) { StringBuilder builder = new StringBuilder(); - getKey(builder, this.typeSymbol.type, false); + getKey(builder, t, false); return builder.toString(); } static void getKey(StringBuilder builder, Type typeToBuild, boolean isLeaf) { + if (typeToBuild instanceof Type.JCNoType) { + return; + } if (typeToBuild instanceof ArrayType arrayType) { builder.append('['); getKey(builder, arrayType.elemtype, isLeaf); return; } + if (typeToBuild instanceof Type.WildcardType wildcardType) { + if (wildcardType.isUnbound()) { + builder.append('*'); + } else if (wildcardType.isExtendsBound()) { + builder.append('+'); + getKey(builder, wildcardType.getExtendsBound(), isLeaf); + } else if (wildcardType.isSuperBound()) { + builder.append('-'); + getKey(builder, wildcardType.getSuperBound(), isLeaf); + } + return; + } if (typeToBuild.isReference()) { if (!isLeaf) { - builder.append('L'); + if (typeToBuild.tsym instanceof Symbol.TypeVariableSymbol) { + builder.append('T'); + } else { + builder.append('L'); + } } builder.append(typeToBuild.asElement().getQualifiedName().toString().replace('.', '/')); if (typeToBuild.isParameterized()) { @@ -170,11 +188,11 @@ public boolean isEqualTo(final IBinding binding) { @Override public ITypeBinding createArrayType(final int dimension) { - Type type = this.typeSymbol.type; + Type type = this.type; for (int i = 0; i < dimension; i++) { type = this.types.makeArrayType(type); } - return new JavacTypeBinding(type, this.resolver, this.typeArguments); + return new JavacTypeBinding(type, this.resolver); } @Override @@ -201,7 +219,7 @@ public ITypeBinding getGenericTypeOfWildcardType() { } if (this.typeSymbol.type instanceof WildcardType wildcardType) { // TODO: probably wrong, we might need to pass in the parent node from the AST - return (ITypeBinding)this.resolver.getBinding(wildcardType.type.tsym, null); + return (ITypeBinding)this.resolver.getBinding(wildcardType.type.tsym, wildcardType.type); } throw new IllegalStateException("Binding is a wildcard, but type cast failed"); } @@ -209,15 +227,15 @@ public ITypeBinding getGenericTypeOfWildcardType() { @Override public int getRank() { if (isWildcardType() || isIntersectionType()) { - return types.rank(this.typeSymbol.type); + return types.rank(this.type); } return -1; } @Override public ITypeBinding getComponentType() { - if (this.typeSymbol.type instanceof ArrayType arrayType) { - return new JavacTypeBinding(arrayType.elemtype.tsym, this.resolver, null); + if (this.type instanceof ArrayType arrayType) { + return new JavacTypeBinding(arrayType.elemtype, this.resolver); } return null; } @@ -242,7 +260,7 @@ public IMethodBinding[] getDeclaredMethods() { return StreamSupport.stream(this.typeSymbol.members().getSymbols().spliterator(), false) .filter(MethodSymbol.class::isInstance) .map(MethodSymbol.class::cast) - .map(sym -> new JavacMethodBinding(sym, this.resolver, null)) + .map(sym -> new JavacMethodBinding(sym.type.asMethodType(), sym, this.resolver)) .toArray(IMethodBinding[]::new); } @@ -258,7 +276,7 @@ public ITypeBinding[] getDeclaredTypes() { return StreamSupport.stream(this.typeSymbol.members().getSymbols().spliterator(), false) .filter(TypeSymbol.class::isInstance) .map(TypeSymbol.class::cast) - .map(sym -> new JavacTypeBinding(sym, this.resolver, null)) + .map(sym -> new JavacTypeBinding(sym.type, this.resolver)) .toArray(ITypeBinding[]::new); } @@ -267,7 +285,7 @@ public ITypeBinding getDeclaringClass() { Symbol parentSymbol = this.typeSymbol.owner; do { if (parentSymbol instanceof final ClassSymbol clazz) { - return new JavacTypeBinding(clazz, this.resolver, null); + return new JavacTypeBinding(clazz.type, this.resolver); } parentSymbol = parentSymbol.owner; } while (parentSymbol != null); @@ -279,7 +297,7 @@ public IMethodBinding getDeclaringMethod() { Symbol parentSymbol = this.typeSymbol.owner; do { if (parentSymbol instanceof final MethodSymbol method) { - return new JavacMethodBinding(method, this.resolver, null); + return new JavacMethodBinding(method.type.asMethodType(), method, this.resolver); } parentSymbol = parentSymbol.owner; } while (parentSymbol != null); @@ -291,22 +309,22 @@ public IBinding getDeclaringMember() { if (!this.isLocal()) { return null; } - return this.resolver.getBinding(this.typeSymbol.owner, null); + return this.resolver.getBinding(this.typeSymbol.owner, this.typeSymbol.owner.type); } @Override public int getDimensions() { - return this.types.dimensions(this.typeSymbol.type); + return this.types.dimensions(this.type); } @Override public ITypeBinding getElementType() { - return new JavacTypeBinding(this.types.elemtype(this.typeSymbol.type), this.resolver, null); + return new JavacTypeBinding(this.types.elemtype(this.type), this.resolver); } @Override public ITypeBinding getErasure() { - return new JavacTypeBinding(this.types.erasure(this.typeSymbol.type), this.resolver, null); + return new JavacTypeBinding(this.types.erasure(this.type), this.resolver); } @Override @@ -314,7 +332,7 @@ public IMethodBinding getFunctionalInterfaceMethod() { try { Symbol symbol = types.findDescriptorSymbol(this.typeSymbol); if (symbol instanceof MethodSymbol methodSymbol) { - return new JavacMethodBinding(methodSymbol, resolver, null); + return new JavacMethodBinding(methodSymbol.type.asMethodType(), methodSymbol, resolver); } } catch (FunctionDescriptorLookupError ignore) { } @@ -323,9 +341,20 @@ public IMethodBinding getFunctionalInterfaceMethod() { @Override public ITypeBinding[] getInterfaces() { - return this.typeSymbol instanceof final ClassSymbol classSymbol && classSymbol.getInterfaces() != null ? - classSymbol.getInterfaces().map(t -> new JavacTypeBinding(t, this.resolver, null)).toArray(ITypeBinding[]::new) : - null; + if (this.typeSymbol instanceof TypeVariableSymbol && this.type instanceof TypeVar tv) { + Type t = tv.getUpperBound(); + if (t.tsym instanceof ClassSymbol) { + JavacTypeBinding jtb = new JavacTypeBinding(t, this.resolver); + if( jtb.isInterface()) { + return new ITypeBinding[] {jtb}; + } + } + } + + if( this.typeSymbol instanceof final ClassSymbol classSymbol && classSymbol.getInterfaces() != null ) { + return classSymbol.getInterfaces().map(t -> new JavacTypeBinding(t, this.resolver)).toArray(ITypeBinding[]::new); + } + return new ITypeBinding[0]; } @Override @@ -352,9 +381,29 @@ public String getQualifiedName() { @Override public ITypeBinding getSuperclass() { + if (this.typeSymbol instanceof TypeVariableSymbol && this.type instanceof TypeVar tv) { + Type t = tv.getUpperBound(); + JavacTypeBinding possible = new JavacTypeBinding(t, this.resolver); + if( !possible.isInterface()) { + return possible; + } + if( t instanceof ClassType ct ) { + // we need to return java.lang.object + ClassType working = ct; + while( working != null ) { + Type wt = working.supertype_field; + String sig = getKey(wt); + if( new String(ConstantPool.JavaLangObjectSignature).equals(sig)) { + return new JavacTypeBinding(wt, this.resolver); + } + working = wt instanceof ClassType ? (ClassType)wt : null; + } + } + } if (this.typeSymbol instanceof final ClassSymbol classSymbol && classSymbol.getSuperclass() != null && classSymbol.getSuperclass().tsym != null) { - return new JavacTypeBinding(classSymbol.getSuperclass().tsym, this.resolver, null); + return new JavacTypeBinding(classSymbol.getSuperclass(), this.resolver); } + return null; } @@ -367,21 +416,22 @@ public IAnnotationBinding[] getTypeAnnotations() { @Override public ITypeBinding[] getTypeArguments() { - if (this.typeArguments == null) { + if (this.type.getTypeArguments().isEmpty()) { return NO_TYPE_ARGUMENTS; } - return this.typeArguments.stream() - .map(typeArgument -> this.resolver.getBinding(typeArgument, null)) - .toArray(ITypeBinding[]::new); + return this.type.getTypeArguments() + .stream() + .map(typeArg -> new JavacTypeBinding(typeArg, this.resolver)) + .toArray(ITypeBinding[]::new); } @Override public ITypeBinding[] getTypeBounds() { - Type upperBound = this.typeSymbol.type.getUpperBound(); + Type upperBound = this.type.getUpperBound(); if (upperBound == null) { return new ITypeBinding[0]; } - return new ITypeBinding[] { new JavacTypeBinding(upperBound.tsym, this.resolver, null) }; + return new ITypeBinding[] { new JavacTypeBinding(upperBound, this.resolver) }; } @Override @@ -392,21 +442,21 @@ public ITypeBinding getTypeDeclaration() { @Override public ITypeBinding[] getTypeParameters() { return this.typeSymbol.getTypeParameters().stream() - .map(symbol -> new JavacTypeBinding(symbol, this.resolver, null)) + .map(symbol -> new JavacTypeBinding(symbol.type, this.resolver)) .toArray(ITypeBinding[]::new); } @Override public ITypeBinding getWildcard() { //TODO low confidence on this implem. - if (typeSymbol.type instanceof WildcardType wildcardType) { + if (this.type instanceof WildcardType wildcardType) { Type extendsBound = wildcardType.getExtendsBound(); if (extendsBound != null) { - return new JavacTypeBinding(extendsBound, resolver, null); + return new JavacTypeBinding(extendsBound, resolver); } Type superBound = wildcardType.getSuperBound(); if (superBound != null) { - return new JavacTypeBinding(superBound, resolver, null); + return new JavacTypeBinding(superBound, resolver); } } return null; @@ -424,26 +474,26 @@ public boolean isAnonymous() { @Override public boolean isArray() { - return this.typeSymbol.type instanceof ArrayType; + return this.type instanceof ArrayType; } @Override public boolean isAssignmentCompatible(final ITypeBinding variableType) { if (variableType instanceof JavacTypeBinding other) { - return this.types.isAssignable(other.typeSymbol.type, this.typeSymbol.type); + return this.types.isAssignable(other.type, this.type); } throw new UnsupportedOperationException("Cannot mix with non Javac binding"); //$NON-NLS-1$ } @Override public boolean isCapture() { - return this.typeSymbol.type instanceof Type.CapturedType; + return this.type instanceof Type.CapturedType; } @Override public boolean isCastCompatible(final ITypeBinding type) { if (type instanceof JavacTypeBinding other) { - return this.types.isCastable(this.typeSymbol.type, other.typeSymbol.type); + return this.types.isCastable(this.type, other.type); } throw new UnsupportedOperationException("Cannot mix with non Javac binding"); //$NON-NLS-1$ } @@ -471,7 +521,7 @@ public boolean isFromSource() { @Override public boolean isGenericType() { - return this.typeArguments == null && !this.typeSymbol.getTypeParameters().isEmpty(); + return this.type.getTypeArguments().isEmpty() && !this.typeSymbol.getTypeParameters().isEmpty(); } @Override @@ -481,7 +531,7 @@ public boolean isInterface() { @Override public boolean isIntersectionType() { - return this.typeSymbol.type.isIntersection(); + return this.type.isIntersection(); } @Override @@ -502,22 +552,22 @@ public boolean isNested() { @Override public boolean isNullType() { - return this.typeSymbol.type instanceof NullType; + return this.type instanceof NullType; } @Override public boolean isParameterizedType() { - return this.typeArguments != null; + return !this.type.getTypeArguments().isEmpty(); } @Override public boolean isPrimitive() { - return this.typeSymbol.type.isPrimitive(); + return this.type.isPrimitive(); } @Override public boolean isRawType() { - return this.typeSymbol.type.isRaw(); + return this.type.isRaw(); } @Override @@ -526,7 +576,7 @@ public boolean isSubTypeCompatible(final ITypeBinding type) { return true; } if (type instanceof JavacTypeBinding other) { - return this.types.isSubtype(this.typeSymbol.type, other.typeSymbol.type); + return this.types.isSubtype(this.type, other.type); } return false; } @@ -538,17 +588,17 @@ public boolean isTopLevel() { @Override public boolean isTypeVariable() { - return this.typeSymbol.type instanceof TypeVar; + return this.type instanceof TypeVar; } @Override public boolean isUpperbound() { - return this.typeSymbol.type.isExtendsBound(); + return this.type.isExtendsBound(); } @Override public boolean isWildcardType() { - return this.typeSymbol.type instanceof WildcardType; + return this.type instanceof WildcardType; } } diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacTypeVariableBinding.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacTypeVariableBinding.java new file mode 100644 index 00000000000..5e41af15996 --- /dev/null +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacTypeVariableBinding.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat Inc., and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.javac.dom; + +import com.sun.tools.javac.code.Symbol.TypeVariableSymbol; + +/** + * Note that this isn't API and isn't part of the IBinding tree type. + * The sole purpose of this class is to help calculate getKey. + */ +class JavacTypeVariableBinding { + private TypeVariableSymbol typeVar; + + JavacTypeVariableBinding(TypeVariableSymbol typeVar) { + this.typeVar = typeVar; + } + + public String getKey() { + StringBuilder builder = new StringBuilder(); + builder.append(typeVar.getSimpleName()); + builder.append(':'); + boolean prependColon = typeVar.getBounds().size() > 1 + || (typeVar.getBounds().size() > 0 && typeVar.getBounds().get(0).isInterface()); + for (var bound : typeVar.getBounds()) { + if (prependColon) { + builder.append(":"); + } + JavacTypeBinding.getKey(builder, bound, false); + } + return builder.toString(); + } +} diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacVariableBinding.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacVariableBinding.java index ae6704fc392..d65bd3e907c 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacVariableBinding.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/dom/JavacVariableBinding.java @@ -76,7 +76,7 @@ public IField getJavaElement() { return null; } if (this.variableSymbol.owner instanceof TypeSymbol parentType) {//field - return new JavacTypeBinding(parentType, this.resolver, null).getJavaElement().getField(this.variableSymbol.name.toString()); + return new JavacTypeBinding(parentType.type, this.resolver).getJavaElement().getField(this.variableSymbol.name.toString()); } return null; } @@ -96,7 +96,7 @@ public String getKey() { } return builder.toString(); } else if (this.variableSymbol.owner instanceof MethodSymbol methodSymbol) { - JavacMethodBinding.getKey(builder, methodSymbol); + JavacMethodBinding.getKey(builder, methodSymbol, this.resolver); builder.append('#'); builder.append(this.variableSymbol.name); // FIXME: is it possible for the javac AST to contain multiple definitions of the same variable? @@ -138,7 +138,7 @@ public ITypeBinding getDeclaringClass() { Symbol parentSymbol = this.variableSymbol.owner; do { if (parentSymbol instanceof ClassSymbol clazz) { - return new JavacTypeBinding(clazz, this.resolver, null); + return new JavacTypeBinding(clazz.type, this.resolver); } parentSymbol = parentSymbol.owner; } while (parentSymbol != null); @@ -147,7 +147,7 @@ public ITypeBinding getDeclaringClass() { @Override public ITypeBinding getType() { - return new JavacTypeBinding(this.variableSymbol.type, this.resolver, null); + return new JavacTypeBinding(this.variableSymbol.type, this.resolver); } @Override @@ -165,7 +165,7 @@ public IMethodBinding getDeclaringMethod() { Symbol parentSymbol = this.variableSymbol.owner; do { if (parentSymbol instanceof MethodSymbol method) { - return new JavacMethodBinding(method, this.resolver, null); + return new JavacMethodBinding(method.type.asMethodType(), method, this.resolver); } parentSymbol = parentSymbol.owner; } while (parentSymbol != null);