'val' now works for member types whose outers have generics. (issue #343)

This commit is contained in:
Reinier Zwitserloot
2012-02-14 16:31:48 +01:00
parent 653104aa4e
commit f259af306f
5 changed files with 95 additions and 48 deletions
@@ -30,6 +30,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -562,10 +563,38 @@ public class EclipseHandlerUtil {
}
}
// Keep moving up via 'binding.enclosingType()' and gather generics from each binding. We stop after a local type, or a static type, or a top-level type.
// Finally, add however many nullTypeArgument[] arrays as that are missing, inverse the list, toArray it, and use that as PTR's typeArgument argument.
List<TypeReference[]> params = new ArrayList<TypeReference[]>();
/* Calculate generics */ {
TypeBinding b = binding;
while (true) {
boolean isFinalStop = b.isLocalType() || !b.isMemberType() || b.enclosingType() == null;
TypeReference[] tyParams = null;
if (b instanceof ParameterizedTypeBinding) {
ParameterizedTypeBinding paramized = (ParameterizedTypeBinding) b;
if (paramized.arguments != null) {
tyParams = new TypeReference[paramized.arguments.length];
for (int i = 0; i < tyParams.length; i++) {
tyParams[i] = makeType(paramized.arguments[i], pos, true);
}
}
}
params.add(tyParams);
if (isFinalStop) break;
b = b.enclosingType();
}
}
char[][] parts;
if (binding.isLocalType() || binding.isTypeVariable()) {
if (binding.isTypeVariable()) {
parts = new char[][] { binding.shortReadableName() };
} else if (binding.isLocalType()) {
parts = new char[][] { binding.sourceName() };
} else {
String[] pkg = new String(binding.qualifiedPackageName()).split("\\.");
String[] name = new String(binding.qualifiedSourceName()).split("\\.");
@@ -576,27 +605,25 @@ public class EclipseHandlerUtil {
for (; ptr < pkg.length + name.length; ptr++) parts[ptr] = name[ptr - pkg.length].toCharArray();
}
TypeReference[] params = new TypeReference[0];
while (params.size() < parts.length) params.add(null);
Collections.reverse(params);
if (binding instanceof ParameterizedTypeBinding) {
ParameterizedTypeBinding paramized = (ParameterizedTypeBinding) binding;
if (paramized.arguments != null) {
params = new TypeReference[paramized.arguments.length];
for (int i = 0; i < params.length; i++) {
params[i] = makeType(paramized.arguments[i], pos, true);
}
boolean isParamized = false;
for (TypeReference[] tyParams : params) {
if (tyParams != null) {
isParamized = true;
break;
}
}
if (params.length > 0) {
if (isParamized) {
if (parts.length > 1) {
TypeReference[][] typeArguments = new TypeReference[parts.length][];
typeArguments[typeArguments.length - 1] = params;
TypeReference[][] typeArguments = params.toArray(new TypeReference[0][]);
TypeReference result = new ParameterizedQualifiedTypeReference(parts, typeArguments, dims, poss(pos, parts.length));
setGeneratedBy(result, pos);
return result;
}
TypeReference result = new ParameterizedSingleTypeReference(parts[0], params, dims, pos(pos));
TypeReference result = new ParameterizedSingleTypeReference(parts[0], params.get(0), dims, pos(pos));
setGeneratedBy(result, pos);
return result;
}
+43 -22
View File
@@ -362,18 +362,19 @@ public class JavacResolution {
return iterableParams.isEmpty() ? syms.objectType : types.upperBound(iterableParams.head);
}
public static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast, boolean allowVoid) throws TypeNotConvertibleException {
return typeToJCTree(type, maker, ast, false, allowVoid);
public static JCExpression typeToJCTree(Type type, JavacAST ast, boolean allowVoid) throws TypeNotConvertibleException {
return typeToJCTree(type, ast, false, allowVoid);
}
public static JCExpression createJavaLangObject(TreeMaker maker, JavacAST ast) {
public static JCExpression createJavaLangObject(JavacAST ast) {
TreeMaker maker = ast.getTreeMaker();
JCExpression out = maker.Ident(ast.toName("java"));
out = maker.Select(out, ast.toName("lang"));
out = maker.Select(out, ast.toName("Object"));
return out;
}
private static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast, boolean allowCompound, boolean allowVoid) throws TypeNotConvertibleException {
private static JCExpression typeToJCTree(Type type, JavacAST ast, boolean allowCompound, boolean allowVoid) throws TypeNotConvertibleException {
int dims = 0;
Type type0 = type;
while (type0 instanceof ArrayType) {
@@ -381,20 +382,22 @@ public class JavacResolution {
type0 = ((ArrayType)type0).elemtype;
}
JCExpression result = typeToJCTree0(type0, maker, ast, allowCompound, allowVoid);
JCExpression result = typeToJCTree0(type0, ast, allowCompound, allowVoid);
while (dims > 0) {
result = maker.TypeArray(result);
result = ast.getTreeMaker().TypeArray(result);
dims--;
}
return result;
}
private static JCExpression typeToJCTree0(Type type, TreeMaker maker, JavacAST ast, boolean allowCompound, boolean allowVoid) throws TypeNotConvertibleException {
private static JCExpression typeToJCTree0(Type type, JavacAST ast, boolean allowCompound, boolean allowVoid) throws TypeNotConvertibleException {
// NB: There's such a thing as maker.Type(type), but this doesn't work very well; it screws up anonymous classes, captures, and adds an extra prefix dot for some reason too.
// -- so we write our own take on that here.
if (type.tag == Javac.getCtcInt(TypeTags.class, "BOT")) return createJavaLangObject(maker, ast);
if (type.tag == Javac.getCtcInt(TypeTags.class, "VOID")) return allowVoid ? primitiveToJCTree(type.getKind(), maker) : createJavaLangObject(maker, ast);
TreeMaker maker = ast.getTreeMaker();
if (type.tag == Javac.getCtcInt(TypeTags.class, "BOT")) return createJavaLangObject(ast);
if (type.tag == Javac.getCtcInt(TypeTags.class, "VOID")) return allowVoid ? primitiveToJCTree(type.getKind(), maker) : createJavaLangObject(ast);
if (type.isPrimitive()) return primitiveToJCTree(type.getKind(), maker);
if (type.isErroneous()) throw new TypeNotConvertibleException("Type cannot be resolved");
@@ -411,9 +414,9 @@ public class JavacResolution {
List<Type> ifaces = ((ClassType)type).interfaces_field;
Type supertype = ((ClassType)type).supertype_field;
if (ifaces != null && ifaces.length() == 1) {
return typeToJCTree(ifaces.get(0), maker, ast, allowCompound, allowVoid);
return typeToJCTree(ifaces.get(0), ast, allowCompound, allowVoid);
}
if (supertype != null) return typeToJCTree(supertype, maker, ast, allowCompound, allowVoid);
if (supertype != null) return typeToJCTree(supertype, ast, allowCompound, allowVoid);
}
throw new TypeNotConvertibleException("Anonymous inner class");
}
@@ -432,34 +435,52 @@ public class JavacResolution {
if (upper == null || upper.toString().equals("java.lang.Object")) {
return maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
}
return maker.Wildcard(maker.TypeBoundKind(BoundKind.EXTENDS), typeToJCTree(upper, maker, ast, false, false));
return maker.Wildcard(maker.TypeBoundKind(BoundKind.EXTENDS), typeToJCTree(upper, ast, false, false));
} else {
return maker.Wildcard(maker.TypeBoundKind(BoundKind.SUPER), typeToJCTree(lower, maker, ast, false, false));
return maker.Wildcard(maker.TypeBoundKind(BoundKind.SUPER), typeToJCTree(lower, ast, false, false));
}
}
if (upper != null) {
return typeToJCTree(upper, maker, ast, allowCompound, allowVoid);
return typeToJCTree(upper, ast, allowCompound, allowVoid);
}
return createJavaLangObject(maker, ast);
return createJavaLangObject(ast);
}
String qName;
if (symbol.isLocal()) {
qName = symbol.getSimpleName().toString();
} else if (symbol.type != null && symbol.type.getEnclosingType() != null && symbol.type.getEnclosingType().tag == TypeTags.CLASS) {
replacement = typeToJCTree0(type.getEnclosingType(), ast, false, false);
qName = symbol.getSimpleName().toString();
} else {
qName = symbol.getQualifiedName().toString();
}
String qName = symbol.getQualifiedName().toString();
if (qName.isEmpty()) throw new TypeNotConvertibleException("unknown type");
if (qName.startsWith("<")) throw new TypeNotConvertibleException(qName);
String[] baseNames = symbol.getQualifiedName().toString().split("\\.");
replacement = maker.Ident(ast.toName(baseNames[0]));
for (int i = 1; i < baseNames.length; i++) {
String[] baseNames = qName.split("\\.");
int i = 0;
if (replacement == null) {
replacement = maker.Ident(ast.toName(baseNames[0]));
i = 1;
}
for (; i < baseNames.length; i++) {
replacement = maker.Select(replacement, ast.toName(baseNames[i]));
}
return genericsToJCTreeNodes(generics, ast, replacement);
}
private static JCExpression genericsToJCTreeNodes(List<Type> generics, JavacAST ast, JCExpression rawTypeNode) throws TypeNotConvertibleException {
if (generics != null && !generics.isEmpty()) {
ListBuffer<JCExpression> args = ListBuffer.lb();
for (Type t : generics) args.append(typeToJCTree(t, maker, ast, true, false));
replacement = maker.TypeApply(replacement, args.toList());
for (Type t : generics) args.append(typeToJCTree(t, ast, true, false));
return ast.getTreeMaker().TypeApply(rawTypeNode, args.toList());
}
return replacement;
return rawTypeNode;
}
private static JCExpression primitiveToJCTree(TypeKind kind, TreeMaker maker) throws TypeNotConvertibleException {
@@ -277,7 +277,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
}
JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annotations);
JCExpression returnType = JavacResolution.typeToJCTree((Type) sig.type.getReturnType(), maker, annotation.getAst(), true);
JCExpression returnType = JavacResolution.typeToJCTree((Type) sig.type.getReturnType(), annotation.getAst(), true);
boolean useReturn = sig.type.getReturnType().getKind() != TypeKind.VOID;
ListBuffer<JCVariableDecl> params = sig.type.getParameterTypes().isEmpty() ? null : new ListBuffer<JCVariableDecl>();
ListBuffer<JCExpression> args = sig.type.getParameterTypes().isEmpty() ? null : new ListBuffer<JCExpression>();
@@ -293,7 +293,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
}
for (TypeMirror ex : sig.type.getThrownTypes()) {
thrown.append(JavacResolution.typeToJCTree((Type) ex, maker, annotation.getAst(), true));
thrown.append(JavacResolution.typeToJCTree((Type) ex, annotation.getAst(), true));
}
int idx = 0;
@@ -301,7 +301,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
JCModifiers paramMods = maker.Modifiers(Flags.FINAL);
String[] paramNames = sig.getParameterNames();
Name name = annotation.toName(paramNames[idx++]);
params.append(maker.VarDef(paramMods, name, JavacResolution.typeToJCTree((Type) param, maker, annotation.getAst(), true), null));
params.append(maker.VarDef(paramMods, name, JavacResolution.typeToJCTree((Type) param, annotation.getAst(), true), null));
args.append(maker.Ident(name));
}
@@ -82,7 +82,7 @@ public class HandleVal extends JavacASTAdapter {
local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation);
}
local.vartype = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst());
local.vartype = JavacResolution.createJavaLangObject(localNode.getAst());
Type type;
try {
@@ -107,24 +107,24 @@ public class HandleVal extends JavacASTAdapter {
if (rhsOfEnhancedForLoop != null) {
Type componentType = JavacResolution.ifTypeIsIterableToComponent(type, localNode.getAst());
if (componentType == null) replacement = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst());
else replacement = JavacResolution.typeToJCTree(componentType, localNode.getTreeMaker(), localNode.getAst(), false);
if (componentType == null) replacement = JavacResolution.createJavaLangObject(localNode.getAst());
else replacement = JavacResolution.typeToJCTree(componentType, localNode.getAst(), false);
} else {
replacement = JavacResolution.typeToJCTree(type, localNode.getTreeMaker(), localNode.getAst(), false);
replacement = JavacResolution.typeToJCTree(type, localNode.getAst(), false);
}
if (replacement != null) {
local.vartype = replacement;
} else {
local.vartype = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst());
local.vartype = JavacResolution.createJavaLangObject(localNode.getAst());
}
localNode.getAst().setChanged();
} catch (JavacResolution.TypeNotConvertibleException e) {
localNode.addError("Cannot use 'val' here because initializer expression does not have a representable type: " + e.getMessage());
local.vartype = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst());
local.vartype = JavacResolution.createJavaLangObject(localNode.getAst());
}
} catch (RuntimeException e) {
local.vartype = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst());
local.vartype = JavacResolution.createJavaLangObject(localNode.getAst());
throw e;
} finally {
recursiveSetGeneratedBy(local.vartype, source);
+1 -2
View File
@@ -103,8 +103,7 @@ public abstract class AbstractRunTests {
}
try {
compareContent(name, expectedMessages, actualMessages);
}
catch (Throwable e) {
} catch (Throwable e) {
if (printErrors) {
System.out.println("***** " + name + " *****");
System.out.println(e.getMessage());