changeset 481:699ecefbdd4e

6919889: assorted position errors in compiler syntax trees Reviewed-by: darcy
author jjg
date Fri, 29 Jan 2010 16:06:51 -0800
parents ff7a01f9eff3
children 8e638442522a
files src/share/classes/com/sun/tools/javac/code/Flags.java src/share/classes/com/sun/tools/javac/parser/JavacParser.java src/share/classes/com/sun/tools/javac/tree/TreeInfo.java src/share/classes/com/sun/tools/javac/tree/TreeMaker.java test/tools/javac/T6654037.java test/tools/javac/generics/diamond/neg/Neg01.out test/tools/javac/generics/diamond/neg/Neg02.out test/tools/javac/generics/diamond/neg/Neg03.out test/tools/javac/generics/diamond/neg/Neg04.out test/tools/javac/treepostests/TreePosTest.java
diffstat 10 files changed, 957 insertions(+), 93 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/code/Flags.java	Wed Jan 27 14:46:37 2010 -0800
+++ b/src/share/classes/com/sun/tools/javac/code/Flags.java	Fri Jan 29 16:06:51 2010 -0800
@@ -113,6 +113,7 @@
     public static final int ENUM         = 1<<14;
 
     public static final int StandardFlags = 0x0fff;
+    public static final int ModifierFlags = StandardFlags & ~INTERFACE;
 
     // Because the following access flags are overloaded with other
     // bit positions, we translate them when reading and writing class
--- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Wed Jan 27 14:46:37 2010 -0800
+++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Fri Jan 29 16:06:51 2010 -0800
@@ -761,23 +761,28 @@
         JCExpression[] odStack = newOdStack();
         List<Token[]> savedOp = opStackSupply.elems;
         Token[] opStack = newOpStack();
+        List<int[]> savedPos = posStackSupply.elems;
+        int[] posStack = newPosStack();
         // optimization, was odStack = new Tree[...]; opStack = new Tree[...];
         int top = 0;
         odStack[0] = t;
         int startPos = S.pos();
         Token topOp = ERROR;
+        int topOpPos = Position.NOPOS;
         while (prec(S.token()) >= minprec) {
+            posStack[top] = topOpPos;
             opStack[top] = topOp;
             top++;
             topOp = S.token();
-            int pos = S.pos();
+            topOpPos = S.pos();
             S.nextToken();
-            odStack[top] = topOp == INSTANCEOF ? parseType() : term3();
+            odStack[top] = (topOp == INSTANCEOF) ? parseType() : term3();
             while (top > 0 && prec(topOp) >= prec(S.token())) {
-                odStack[top-1] = makeOp(pos, topOp, odStack[top-1],
+                odStack[top-1] = makeOp(topOpPos, topOp, odStack[top-1],
                                         odStack[top]);
                 top--;
                 topOp = opStack[top];
+                topOpPos = posStack[top];
             }
         }
         assert top == 0;
@@ -792,6 +797,7 @@
 
         odStackSupply.elems = savedOd; // optimization
         opStackSupply.elems = savedOp; // optimization
+        posStackSupply.elems = savedPos; // optimization
         return t;
     }
 //where
@@ -845,6 +851,7 @@
          */
         ListBuffer<JCExpression[]> odStackSupply = new ListBuffer<JCExpression[]>();
         ListBuffer<Token[]> opStackSupply = new ListBuffer<Token[]>();
+        ListBuffer<int[]> posStackSupply = new ListBuffer<int[]>();
 
         private JCExpression[] newOdStack() {
             if (odStackSupply.elems == odStackSupply.last)
@@ -862,6 +869,14 @@
             return opStack;
         }
 
+        private int[] newPosStack() {
+            if (posStackSupply.elems == posStackSupply.last)
+                posStackSupply.append(new int[infixPrecedenceLevels + 1]);
+            int[] posStack = posStackSupply.elems.head;
+            posStackSupply.elems = posStackSupply.elems.tail;
+            return posStack;
+        }
+
     /** Expression3    = PrefixOp Expression3
      *                 | "(" Expr | TypeNoParams ")" Expression3
      *                 | Primary {Selector} {PostfixOp}
@@ -939,7 +954,7 @@
                             args.append(typeArgument());
                         }
                         accept(GT);
-                        t = F.at(pos1).TypeApply(t, args.toList());
+                        t = toP(F.at(pos1).TypeApply(t, args.toList()));
                         checkGenerics();
                         while (S.token() == DOT) {
                             S.nextToken();
@@ -950,7 +965,8 @@
                         t = bracketsOpt(toP(t));
                     } else if ((mode & EXPR) != 0) {
                         mode = EXPR;
-                        t = F.at(pos1).Binary(op, t, term2Rest(t1, TreeInfo.shiftPrec));
+                        JCExpression e = term2Rest(t1, TreeInfo.shiftPrec);
+                        t = F.at(pos1).Binary(op, t, e);
                         t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec)));
                     } else {
                         accept(GT);
@@ -998,7 +1014,8 @@
         case SUPER:
             if ((mode & EXPR) != 0) {
                 mode = EXPR;
-                t = to(superSuffix(typeArgs, F.at(pos).Ident(names._super)));
+                t = to(F.at(pos).Ident(names._super));
+                t = superSuffix(typeArgs, t);
                 typeArgs = null;
             } else return illegal();
             break;
@@ -1380,13 +1397,15 @@
         S.nextToken();
         JCExpression result;
         if (S.token() == EXTENDS) {
-            TypeBoundKind t = to(F.at(S.pos()).TypeBoundKind(BoundKind.EXTENDS));
+            TypeBoundKind t = to(F.at(pos).TypeBoundKind(BoundKind.EXTENDS));
             S.nextToken();
-            result = F.at(pos).Wildcard(t, parseType());
+            JCExpression bound = parseType();
+            result = F.at(pos).Wildcard(t, bound);
         } else if (S.token() == SUPER) {
-            TypeBoundKind t = to(F.at(S.pos()).TypeBoundKind(BoundKind.SUPER));
+            TypeBoundKind t = to(F.at(pos).TypeBoundKind(BoundKind.SUPER));
             S.nextToken();
-            result = F.at(pos).Wildcard(t, parseType());
+            JCExpression bound = parseType();
+            result = F.at(pos).Wildcard(t, bound);
         } else if (S.token() == IDENTIFIER) {
             //error recovery
             reportSyntaxError(S.prevEndPos(), "expected3",
@@ -1396,7 +1415,7 @@
             JCIdent id = toP(F.at(S.pos()).Ident(ident()));
             result = F.at(pos).Erroneous(List.<JCTree>of(wc, id));
         } else {
-            TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(BoundKind.UNBOUND);
+            TypeBoundKind t = toP(F.at(pos).TypeBoundKind(BoundKind.UNBOUND));
             result = toP(F.at(pos).Wildcard(t, null));
         }
         if (!annotations.isEmpty())
@@ -2117,14 +2136,21 @@
         return modifiersOpt(null);
     }
     JCModifiers modifiersOpt(JCModifiers partial) {
-        long flags = (partial == null) ? 0 : partial.flags;
+        long flags;
+        ListBuffer<JCAnnotation> annotations = new ListBuffer<JCAnnotation>();
+        int pos;
+        if (partial == null) {
+            flags = 0;
+            pos = S.pos();
+        } else {
+            flags = partial.flags;
+            annotations.appendList(partial.annotations);
+            pos = partial.pos;
+        }
         if (S.deprecatedFlag()) {
             flags |= Flags.DEPRECATED;
             S.resetDeprecatedFlag();
         }
-        ListBuffer<JCAnnotation> annotations = new ListBuffer<JCAnnotation>();
-        if (partial != null) annotations.appendList(partial.annotations);
-        int pos = S.pos();
         int lastPos = Position.NOPOS;
     loop:
         while (true) {
@@ -2150,12 +2176,12 @@
             if (flag == Flags.ANNOTATION) {
                 checkAnnotations();
                 if (S.token() != INTERFACE) {
-                JCAnnotation ann = annotation(lastPos, AnnotationKind.DEFAULT_ANNO);
-                // if first modifier is an annotation, set pos to annotation's.
-                if (flags == 0 && annotations.isEmpty())
-                    pos = ann.pos;
-                annotations.append(ann);
-                lastPos = ann.pos;
+                    JCAnnotation ann = annotation(lastPos, AnnotationKind.DEFAULT_ANNO);
+                    // if first modifier is an annotation, set pos to annotation's.
+                    if (flags == 0 && annotations.isEmpty())
+                        pos = ann.pos;
+                    annotations.append(ann);
+                    lastPos = ann.pos;
                     flag = 0;
                 }
             }
@@ -2169,7 +2195,7 @@
 
         /* A modifiers tree with no modifier tokens or annotations
          * has no text position. */
-        if (flags == 0 && annotations.isEmpty())
+        if ((flags & Flags.ModifierFlags) == 0 && annotations.isEmpty())
             pos = Position.NOPOS;
 
         JCModifiers mods = F.at(pos).Modifiers(flags, annotations.toList());
@@ -2226,7 +2252,8 @@
             if (t1.getTag() == JCTree.IDENT && S.token() == EQ) {
                 int pos = S.pos();
                 accept(EQ);
-                return toP(F.at(pos).Assign(t1, annotationValue()));
+                JCExpression v = annotationValue();
+                return toP(F.at(pos).Assign(t1, v));
             } else {
                 return t1;
             }
@@ -2543,10 +2570,9 @@
         }
 
         List<JCTree> defs = enumBody(name);
-        JCModifiers newMods =
-            F.at(mods.pos).Modifiers(mods.flags|Flags.ENUM, mods.annotations);
+        mods.flags |= Flags.ENUM;
         JCClassDecl result = toP(F.at(pos).
-            ClassDef(newMods, name, List.<JCTypeParameter>nil(),
+            ClassDef(mods, name, List.<JCTypeParameter>nil(),
                 null, implementing, defs));
         attach(result, dc);
         return result;
@@ -2695,16 +2721,8 @@
             } else {
                 pos = S.pos();
                 List<JCTypeParameter> typarams = typeParametersOpt();
-                // Hack alert:  if there are type arguments but no Modifiers, the start
-                // position will be lost unless we set the Modifiers position.  There
-                // should be an AST node for type parameters (BugId 5005090).
-                if (typarams.length() > 0 && mods.pos == Position.NOPOS) {
-                    mods.pos = pos;
-                }
-
                 List<JCAnnotation> annosAfterParams = annotationsOpt(AnnotationKind.DEFAULT_ANNO);
 
-                Token token = S.token();
                 Name name = S.name();
                 pos = S.pos();
                 JCExpression type;
@@ -2715,7 +2733,11 @@
                     type = to(F.at(pos).TypeIdent(TypeTags.VOID));
                     S.nextToken();
                 } else {
-                    mods.annotations = mods.annotations.appendList(annosAfterParams);
+                    if (annosAfterParams.nonEmpty()) {
+                        mods.annotations = mods.annotations.appendList(annosAfterParams);
+                        if (mods.pos == Position.NOPOS)
+                            mods.pos = mods.annotations.head.pos;
+                    }
                     // method returns types are un-annotated types
                     type = unannotatedType();
                 }
@@ -2813,6 +2835,7 @@
                 }
             }
         }
+
         JCMethodDecl result =
             toP(F.at(pos).MethodDef(mods, name, type, typarams,
                                     params, receiverAnnotations, thrown,
--- a/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Wed Jan 27 14:46:37 2010 -0800
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Fri Jan 29 16:06:51 2010 -0800
@@ -307,8 +307,18 @@
         case(JCTree.POSTINC):
         case(JCTree.POSTDEC):
             return getStartPos(((JCUnary) tree).arg);
-        case(JCTree.ANNOTATED_TYPE):
-            return getStartPos(((JCAnnotatedType) tree).underlyingType);
+        case(JCTree.ANNOTATED_TYPE): {
+            JCAnnotatedType node = (JCAnnotatedType) tree;
+            if (node.annotations.nonEmpty())
+                return getStartPos(node.annotations.head);
+            return getStartPos(node.underlyingType);
+        }
+        case(JCTree.NEWCLASS): {
+            JCNewClass node = (JCNewClass)tree;
+            if (node.encl != null)
+                return getStartPos(node.encl);
+            break;
+        }
         case(JCTree.VARDEF): {
             JCVariableDecl node = (JCVariableDecl)tree;
             if (node.mods.pos != Position.NOPOS) {
@@ -406,6 +416,8 @@
             return getEndPos(((JCUnary) tree).arg, endPositions);
         case(JCTree.WHILELOOP):
             return getEndPos(((JCWhileLoop) tree).body, endPositions);
+        case(JCTree.ANNOTATED_TYPE):
+            return getEndPos(((JCAnnotatedType) tree).underlyingType, endPositions);
         case(JCTree.ERRONEOUS): {
             JCErroneous node = (JCErroneous)tree;
             if (node.errs != null && node.errs.nonEmpty())
--- a/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Wed Jan 27 14:46:37 2010 -0800
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Fri Jan 29 16:06:51 2010 -0800
@@ -480,7 +480,7 @@
 
     public JCModifiers Modifiers(long flags, List<JCAnnotation> annotations) {
         JCModifiers tree = new JCModifiers(flags, annotations);
-        boolean noFlags = (flags & Flags.StandardFlags) == 0;
+        boolean noFlags = (flags & Flags.ModifierFlags) == 0;
         tree.pos = (noFlags && annotations.isEmpty()) ? Position.NOPOS : pos;
         return tree;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/T6654037.java	Fri Jan 29 16:06:51 2010 -0800
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2008-2010 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6654037
+ * @summary JCTree.pos may be incorrect for BinaryTrees
+ */
+
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.tree.JCTree;
+import java.net.URI;
+import java.util.Arrays;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+
+public class T6654037 {
+
+    public static void main(String[] args) throws Exception {
+        final String bootPath = System.getProperty("sun.boot.class.path"); //NOI18N
+        final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
+        assert tool != null;
+
+        String code = "package test; public class Test {private void test() {Object o = null; boolean b = o != null && o instanceof String;} private Test() {}}";
+
+        JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, Arrays.asList("-bootclasspath", bootPath, "-Xjcov"), null, Arrays.asList(new MyFileObject(code)));
+        CompilationUnitTree cut = ct.parse().iterator().next();
+        ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
+        MethodTree method = (MethodTree) clazz.getMembers().get(0);
+        VariableTree condSt = (VariableTree) method.getBody().getStatements().get(1);
+        BinaryTree cond = (BinaryTree) condSt.getInitializer();
+        JCTree condJC = (JCTree) cond;
+
+        if (condJC.pos != 93)
+            throw new IllegalStateException("Unexpected position=" + condJC.pos);
+    }
+
+    static class MyFileObject extends SimpleJavaFileObject {
+        private String text;
+        public MyFileObject(String text) {
+            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+            this.text = text;
+        }
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return text;
+        }
+    }
+}
--- a/test/tools/javac/generics/diamond/neg/Neg01.out	Wed Jan 27 14:46:37 2010 -0800
+++ b/test/tools/javac/generics/diamond/neg/Neg01.out	Fri Jan 29 16:06:51 2010 -0800
@@ -1,31 +1,31 @@
 Neg01.java:18:15: compiler.err.not.within.bounds: java.lang.String
 Neg01.java:18:37: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg01<X>, Neg01<java.lang.String>)
-Neg01.java:19:25: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg01.java:19:15: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg01.java:19:47: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg01.java:20:23: compiler.err.cant.resolve.location.args: kindname.constructor, Neg01, , java.lang.String, kindname.class, Neg01<java.lang.Number>
-Neg01.java:21:23: compiler.err.not.within.bounds: ? super java.lang.String
+Neg01.java:21:15: compiler.err.not.within.bounds: ? super java.lang.String
 Neg01.java:21:45: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg01<X>, Neg01<? super java.lang.String>)
 Neg01.java:23:15: compiler.err.not.within.bounds: java.lang.String
 Neg01.java:23:37: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg01<X>, Neg01<java.lang.String>)
-Neg01.java:24:25: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg01.java:24:15: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg01.java:24:47: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg01.java:25:23: compiler.err.cant.resolve.location.args: kindname.constructor, Neg01, , java.lang.String, kindname.class, Neg01<java.lang.Number>
 Neg01.java:25:38: compiler.err.cant.resolve.location.args: kindname.constructor, Neg01, , , kindname.class, Neg01<java.lang.Number>
-Neg01.java:26:23: compiler.err.not.within.bounds: ? super java.lang.String
+Neg01.java:26:15: compiler.err.not.within.bounds: ? super java.lang.String
 Neg01.java:26:45: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg01<X>, Neg01<? super java.lang.String>)
 Neg01.java:28:15: compiler.err.not.within.bounds: java.lang.String
 Neg01.java:28:37: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg01<X>, Neg01<java.lang.String>)
-Neg01.java:29:25: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg01.java:29:15: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg01.java:29:48: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg01.java:30:24: compiler.err.cant.resolve.location.args: kindname.constructor, Neg01, , java.lang.String,java.lang.String, kindname.class, Neg01<java.lang.Number>
 Neg01.java:31:9: compiler.err.cant.resolve.location: kindname.class, Foo, , , kindname.class, Neg01<X>
 Neg01.java:31:35: compiler.err.cant.resolve.location.args: kindname.constructor, Neg01, , java.lang.String,java.lang.String, kindname.class, Neg01<java.lang.Number>
 Neg01.java:33:15: compiler.err.not.within.bounds: java.lang.String
 Neg01.java:33:38: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg01<X>, Neg01<java.lang.String>)
-Neg01.java:34:25: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg01.java:34:15: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg01.java:34:48: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg01.java:35:24: compiler.err.cant.resolve.location.args: kindname.constructor, Neg01, , java.lang.String,java.lang.String, kindname.class, Neg01<java.lang.Number>
 Neg01.java:35:43: compiler.err.cant.resolve.location.args: kindname.constructor, Neg01, , , kindname.class, Neg01<java.lang.Number>
-Neg01.java:36:23: compiler.err.not.within.bounds: ? super java.lang.String
+Neg01.java:36:15: compiler.err.not.within.bounds: ? super java.lang.String
 Neg01.java:36:46: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg01<X>, Neg01<? super java.lang.String>)
 30 errors
--- a/test/tools/javac/generics/diamond/neg/Neg02.out	Wed Jan 27 14:46:37 2010 -0800
+++ b/test/tools/javac/generics/diamond/neg/Neg02.out	Fri Jan 29 16:06:51 2010 -0800
@@ -1,61 +1,61 @@
 Neg02.java:19:13: compiler.err.not.within.bounds: java.lang.String
 Neg02.java:19:33: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<java.lang.String>)
-Neg02.java:20:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg02.java:20:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg02.java:20:43: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg02.java:21:21: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Neg02.Foo<java.lang.Number>
-Neg02.java:22:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg02.java:22:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg02.java:22:41: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<? super java.lang.String>)
 Neg02.java:24:13: compiler.err.not.within.bounds: java.lang.String
 Neg02.java:24:33: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<java.lang.String>)
-Neg02.java:25:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg02.java:25:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg02.java:25:43: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg02.java:26:21: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Neg02.Foo<java.lang.Number>
 Neg02.java:26:34: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Neg02.Foo<java.lang.Number>
-Neg02.java:27:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg02.java:27:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg02.java:27:41: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<? super java.lang.String>)
 Neg02.java:29:13: compiler.err.not.within.bounds: java.lang.String
 Neg02.java:29:33: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<java.lang.String>)
-Neg02.java:30:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg02.java:30:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg02.java:30:44: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg02.java:31:22: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Neg02.Foo<java.lang.Number>
-Neg02.java:32:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg02.java:32:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg02.java:32:42: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<? super java.lang.String>)
 Neg02.java:34:13: compiler.err.not.within.bounds: java.lang.String
 Neg02.java:34:34: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<java.lang.String>)
-Neg02.java:35:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg02.java:35:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg02.java:35:44: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg02.java:36:22: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Neg02.Foo<java.lang.Number>
 Neg02.java:36:39: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Neg02.Foo<java.lang.Number>
-Neg02.java:37:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg02.java:37:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg02.java:37:42: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<? super java.lang.String>)
 Neg02.java:41:13: compiler.err.not.within.bounds: java.lang.String
 Neg02.java:41:39: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<java.lang.String>)
-Neg02.java:42:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg02.java:42:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg02.java:42:49: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg02.java:43:21: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Neg02.Foo<java.lang.Number>
-Neg02.java:44:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg02.java:44:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg02.java:44:47: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<? super java.lang.String>)
 Neg02.java:46:13: compiler.err.not.within.bounds: java.lang.String
 Neg02.java:46:39: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<java.lang.String>)
-Neg02.java:47:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg02.java:47:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg02.java:47:49: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg02.java:48:21: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Neg02.Foo<java.lang.Number>
 Neg02.java:48:40: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Neg02.Foo<java.lang.Number>
-Neg02.java:49:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg02.java:49:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg02.java:49:47: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<? super java.lang.String>)
 Neg02.java:51:13: compiler.err.not.within.bounds: java.lang.String
 Neg02.java:51:39: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<java.lang.String>)
-Neg02.java:52:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg02.java:52:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg02.java:52:50: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg02.java:53:22: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Neg02.Foo<java.lang.Number>
-Neg02.java:54:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg02.java:54:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg02.java:54:48: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<? super java.lang.String>)
 Neg02.java:56:13: compiler.err.not.within.bounds: java.lang.String
 Neg02.java:56:40: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<java.lang.String>)
-Neg02.java:57:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg02.java:57:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg02.java:57:50: compiler.err.cant.apply.diamond: X, (compiler.misc.no.unique.maximal.instance.exists: X, java.lang.String,java.lang.Number)
 Neg02.java:58:22: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Neg02.Foo<java.lang.Number>
 Neg02.java:58:45: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Neg02.Foo<java.lang.Number>
-Neg02.java:59:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg02.java:59:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg02.java:59:48: compiler.err.cant.apply.diamond: X, (compiler.misc.no.conforming.instance.exists: X, Neg02.Foo<X>, Neg02.Foo<? super java.lang.String>)
 60 errors
--- a/test/tools/javac/generics/diamond/neg/Neg03.out	Wed Jan 27 14:46:37 2010 -0800
+++ b/test/tools/javac/generics/diamond/neg/Neg03.out	Fri Jan 29 16:06:51 2010 -0800
@@ -1,91 +1,91 @@
 Neg03.java:19:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:19:33: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:20:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:20:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:20:43: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:21:21: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:22:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:22:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:22:41: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 Neg03.java:24:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:24:33: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:25:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:25:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:25:43: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:26:21: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
 Neg03.java:26:34: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:27:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:27:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:27:41: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 Neg03.java:29:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:29:33: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:30:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:30:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:30:44: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:31:22: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:32:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:32:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:32:42: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 Neg03.java:34:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:34:34: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:35:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:35:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:35:44: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:36:22: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
 Neg03.java:36:39: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:37:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:37:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:37:42: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 Neg03.java:41:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:41:42: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:42:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:42:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:42:52: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:43:21: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:44:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:44:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:44:50: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 Neg03.java:46:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:46:42: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:47:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:47:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:47:52: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:48:21: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
 Neg03.java:48:43: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:49:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:49:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:49:50: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 Neg03.java:51:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:51:42: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:52:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:52:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:52:53: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:53:22: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:54:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:54:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:54:51: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 Neg03.java:56:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:56:43: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:57:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:57:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:57:53: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:58:22: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
 Neg03.java:58:48: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:59:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:59:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:59:51: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 Neg03.java:63:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:63:28: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:64:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:64:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:64:38: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:65:23: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:66:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:66:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:66:36: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 Neg03.java:68:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:68:28: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:69:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:69:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:69:38: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:70:23: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
 Neg03.java:70:36: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:71:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:71:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:71:36: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 Neg03.java:73:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:73:28: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:74:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:74:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:74:39: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:75:24: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:76:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:76:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:76:37: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 Neg03.java:78:13: compiler.err.not.within.bounds: java.lang.String
 Neg03.java:78:29: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<java.lang.String>)
-Neg03.java:79:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg03.java:79:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg03.java:79:39: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg03.java:80:24: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Neg03<U>.Foo<java.lang.Number>
 Neg03.java:80:41: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Neg03<U>.Foo<java.lang.Number>
-Neg03.java:81:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg03.java:81:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg03.java:81:37: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Neg03<U>.Foo<V>, Neg03<U>.Foo<? super java.lang.String>)
 90 errors
--- a/test/tools/javac/generics/diamond/neg/Neg04.out	Wed Jan 27 14:46:37 2010 -0800
+++ b/test/tools/javac/generics/diamond/neg/Neg04.out	Fri Jan 29 16:06:51 2010 -0800
@@ -1,31 +1,31 @@
 Neg04.java:18:13: compiler.err.not.within.bounds: java.lang.String
 Neg04.java:18:33: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Foo<V>, Foo<java.lang.String>)
-Neg04.java:19:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg04.java:19:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg04.java:19:43: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg04.java:20:21: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Foo<java.lang.Number>
-Neg04.java:21:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg04.java:21:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg04.java:21:41: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Foo<V>, Foo<? super java.lang.String>)
 Neg04.java:23:13: compiler.err.not.within.bounds: java.lang.String
 Neg04.java:23:33: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Foo<V>, Foo<java.lang.String>)
-Neg04.java:24:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg04.java:24:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg04.java:24:43: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg04.java:25:21: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String, kindname.class, Foo<java.lang.Number>
 Neg04.java:25:34: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Foo<java.lang.Number>
-Neg04.java:26:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg04.java:26:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg04.java:26:41: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Foo<V>, Foo<? super java.lang.String>)
 Neg04.java:28:13: compiler.err.not.within.bounds: java.lang.String
 Neg04.java:28:33: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Foo<V>, Foo<java.lang.String>)
-Neg04.java:29:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg04.java:29:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg04.java:29:44: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg04.java:30:22: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Foo<java.lang.Number>
-Neg04.java:31:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg04.java:31:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg04.java:31:42: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Foo<V>, Foo<? super java.lang.String>)
 Neg04.java:33:13: compiler.err.not.within.bounds: java.lang.String
 Neg04.java:33:34: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Foo<V>, Foo<java.lang.String>)
-Neg04.java:34:23: compiler.err.not.within.bounds: ? extends java.lang.String
+Neg04.java:34:13: compiler.err.not.within.bounds: ? extends java.lang.String
 Neg04.java:34:44: compiler.err.cant.apply.diamond: V, (compiler.misc.no.unique.maximal.instance.exists: V, java.lang.String,java.lang.Number)
 Neg04.java:35:22: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , java.lang.String,java.lang.String, kindname.class, Foo<java.lang.Number>
 Neg04.java:35:39: compiler.err.cant.resolve.location.args: kindname.constructor, Foo, , , kindname.class, Foo<java.lang.Number>
-Neg04.java:36:21: compiler.err.not.within.bounds: ? super java.lang.String
+Neg04.java:36:13: compiler.err.not.within.bounds: ? super java.lang.String
 Neg04.java:36:42: compiler.err.cant.apply.diamond: V, (compiler.misc.no.conforming.instance.exists: V, Foo<V>, Foo<? super java.lang.String>)
 30 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/treepostests/TreePosTest.java	Fri Jan 29 16:06:51 2010 -0800
@@ -0,0 +1,752 @@
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultHighlighter;
+import javax.swing.text.Highlighter;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCNewClass;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.TreeInfo;
+import com.sun.tools.javac.tree.TreeScanner;
+
+import static com.sun.tools.javac.util.Position.NOPOS;
+
+/**
+ * Utility and test program to check validity of tree positions for tree nodes.
+ * The program can be run standalone, or as a jtreg test.  In standalone mode,
+ * errors can be displayed in a gui viewer. For info on command line args,
+ * run program with no args.
+ *
+ * <p>
+ * jtreg: Note that by using the -r switch in the test description below, this test
+ * will process all java files in the langtools/test directory, thus implicitly
+ * covering any new language features that may be tested in this test suite.
+ */
+
+/*
+ * @test
+ * @bug 6919889
+ * @summary assorted position errors in compiler syntax trees
+ * @run main TreePosTest -q -r -ef ./tools/javac/typeAnnotations .
+ */
+public class TreePosTest {
+    /**
+     * Main entry point.
+     * If test.src is set, program runs in jtreg mode, and will throw an Error
+     * if any errors arise, otherwise System.exit will be used, unless the gui
+     * viewer is being used. In jtreg mode, the default base directory for file
+     * args is the value of ${test.src}. In jtreg mode, the -r option can be
+     * given to change the default base directory to the root test directory.
+     */
+    public static void main(String... args) {
+        String testSrc = System.getProperty("test.src");
+        File baseDir = (testSrc == null) ? null : new File(testSrc);
+        boolean ok = new TreePosTest().run(baseDir, args);
+        if (!ok) {
+            if (testSrc != null)  // jtreg mode
+                throw new Error("failed");
+            else
+                System.exit(1);
+        }
+    }
+
+    /**
+     * Run the program. A base directory can be provided for file arguments.
+     * In jtreg mode, the -r option can be given to change the default base
+     * directory to the test root directory. For other options, see usage().
+     * @param baseDir base directory for any file arguments.
+     * @param args command line args
+     * @return true if successful or in gui mode
+     */
+    boolean run(File baseDir, String... args) {
+        if (args.length == 0) {
+            usage(System.out);
+            return true;
+        }
+
+        List<File> files = new ArrayList<File>();
+        for (int i = 0; i < args.length; i++) {
+            String arg = args[i];
+            if (arg.equals("-encoding") && i + 1 < args.length)
+                encoding = args[++i];
+            else if (arg.equals("-gui"))
+                gui = true;
+            else if (arg.equals("-q"))
+                quiet = true;
+            else if (arg.equals("-v"))
+                verbose = true;
+            else if (arg.equals("-t") && i + 1 < args.length)
+                tags.add(args[++i]);
+            else if (arg.equals("-ef") && i + 1 < args.length)
+                excludeFiles.add(new File(baseDir, args[++i]));
+            else if (arg.equals("-r")) {
+                if (excludeFiles.size() > 0)
+                    throw new Error("-r must be used before -ef");
+                File d = baseDir;
+                while (!new File(d, "TEST.ROOT").exists()) {
+                    d = d.getParentFile();
+                    if (d == null)
+                        throw new Error("cannot find TEST.ROOT");
+                }
+                baseDir = d;
+            }
+            else if (arg.startsWith("-"))
+                throw new Error("unknown option: " + arg);
+            else {
+                while (i < args.length)
+                    files.add(new File(baseDir, args[i++]));
+            }
+        }
+
+        for (File file: files) {
+            if (file.exists())
+                test(file);
+            else
+                error("File not found: " + file);
+        }
+
+        if (fileCount != 1)
+            System.err.println(fileCount + " files read");
+        if (errors > 0)
+            System.err.println(errors + " errors");
+
+        return (gui || errors == 0);
+    }
+
+    /**
+     * Print command line help.
+     * @param out output stream
+     */
+    void usage(PrintStream out) {
+        out.println("Usage:");
+        out.println("  java TreePosTest options... files...");
+        out.println("");
+        out.println("where options include:");
+        out.println("-gui      Display returns in a GUI viewer");
+        out.println("-q        Quiet: don't report on inapplicable files");
+        out.println("-v        Verbose: report on files as they are being read");
+        out.println("-t tag    Limit checks to tree nodes with this tag");
+        out.println("          Can be repeated if desired");
+        out.println("-ef file  Exclude file or directory");
+        out.println("");
+        out.println("files may be directories or files");
+        out.println("directories will be scanned recursively");
+        out.println("non java files, or java files which cannot be parsed, will be ignored");
+        out.println("");
+    }
+
+    /**
+     * Test a file. If the file is a directory, it will be recursively scanned
+     * for java files.
+     * @param file the file or directory to test
+     */
+    void test(File file) {
+        if (excludeFiles.contains(file)) {
+            if (!quiet)
+                error("File " + file + " excluded");
+            return;
+        }
+
+        if (file.isDirectory()) {
+            for (File f: file.listFiles()) {
+                test(f);
+            }
+            return;
+        }
+
+        if (file.isFile() && file.getName().endsWith(".java")) {
+            try {
+                if (verbose)
+                    System.err.println(file);
+                fileCount++;
+                PosTester p = new PosTester();
+                p.test(read(file));
+            } catch (ParseException e) {
+                if (!quiet) {
+                    error("Error parsing " + file + "\n" + e.getMessage());
+                }
+            } catch (IOException e) {
+                error("Error reading " + file + ": " + e);
+            }
+            return;
+        }
+
+        if (!quiet)
+            error("File " + file + " ignored");
+    }
+
+    /**
+     * Read a file.
+     * @param file the file to be read
+     * @return the tree for the content of the file
+     * @throws IOException if any IO errors occur
+     * @throws TreePosTest.ParseException if any errors occur while parsing the file
+     */
+    JCCompilationUnit read(File file) throws IOException, ParseException {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        Reporter r = new Reporter(pw);
+        JavacTool tool = JavacTool.create();
+        Charset cs = (encoding == null ? null : Charset.forName(encoding));
+        StandardJavaFileManager fm = tool.getStandardFileManager(r, null, null);
+        Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(file);
+        JavacTask task = tool.getTask(pw, fm, r, Collections.<String>emptyList(), null, files);
+        Iterable<? extends CompilationUnitTree> trees = task.parse();
+        pw.flush();
+        if (r.errors > 0)
+            throw new ParseException(sw.toString());
+        Iterator<? extends CompilationUnitTree> iter = trees.iterator();
+        if (!iter.hasNext())
+            throw new Error("no trees found");
+        JCCompilationUnit t = (JCCompilationUnit) iter.next();
+        if (iter.hasNext())
+            throw new Error("too many trees found");
+        return t;
+    }
+
+    /**
+     * Report an error. When the program is complete, the program will either
+     * exit or throw an Error if any errors have been reported.
+     * @param msg the error message
+     */
+    void error(String msg) {
+        System.err.println(msg);
+        errors++;
+    }
+
+    /** Number of files that have been analyzed. */
+    int fileCount;
+    /** Number of errors reported. */
+    int errors;
+    /** Flag: don't report irrelevant files. */
+    boolean quiet;
+    /** Flag: report files as they are processed. */
+    boolean verbose;
+    /** Flag: show errors in GUI viewer. */
+    boolean gui;
+    /** Option: encoding for test files. */
+    String encoding;
+    /** The GUI viewer for errors. */
+    Viewer viewer;
+    /** The set of tags for tree nodes to be analyzed; if empty, all tree nodes
+     * are analyzed. */
+    Set<String> tags = new HashSet<String>();
+    /** Set of files and directories to be excluded from analysis. */
+    Set<File> excludeFiles = new HashSet<File>();
+    /** Table of printable names for tree tag values. */
+    TagNames tagNames = new TagNames();
+
+    /**
+     * Main class for testing assertions concerning tree positions for tree nodes.
+     */
+    private class PosTester extends TreeScanner {
+        void test(JCCompilationUnit tree) {
+            sourcefile = tree.sourcefile;
+            endPosTable = tree.endPositions;
+            encl = new Info();
+            tree.accept(this);
+        }
+
+        @Override
+        public void scan(JCTree tree) {
+            if (tree == null)
+                return;
+
+            Info self = new Info(tree, endPosTable);
+            if (check(self)) {
+                // Modifiers nodes are present throughout the tree even where
+                // there is no corresponding source text.
+                // Redundant semicolons in a class definition can cause empty
+                // initializer blocks with no positions.
+                if ((self.tag == JCTree.MODIFIERS || self.tag == JCTree.BLOCK)
+                        && self.pos == NOPOS) {
+                    // If pos is NOPOS, so should be the start and end positions
+                    check("start == NOPOS", encl, self, self.start == NOPOS);
+                    check("end == NOPOS", encl, self, self.end == NOPOS);
+                } else {
+                    // For this node, start , pos, and endpos should be all defined
+                    check("start != NOPOS", encl, self, self.start != NOPOS);
+                    check("pos != NOPOS", encl, self, self.pos != NOPOS);
+                    check("end != NOPOS", encl, self, self.end != NOPOS);
+                    // The following should normally be ordered
+                    // encl.start <= start <= pos <= end <= encl.end
+                    // In addition, the position of the enclosing node should be
+                    // within this node.
+                    // The primary exceptions are for array type nodes, because of the
+                    // need to support legacy syntax:
+                    //    e.g.    int a[];    int[] b[];    int f()[] { return null; }
+                    // and because of inconsistent nesting of left and right of
+                    // array declarations:
+                    //    e.g.    int[][] a = new int[2][];
+                    check("encl.start <= start", encl, self, encl.start <= self.start);
+                    check("start <= pos", encl, self, self.start <= self.pos);
+                    if (!(self.tag == JCTree.TYPEARRAY
+                            && (encl.tag == JCTree.VARDEF || encl.tag == JCTree.TYPEARRAY))) {
+                        check("encl.pos <= start || end <= encl.pos",
+                                encl, self, encl.pos <= self.start || self.end <= encl.pos);
+                    }
+                    check("pos <= end", encl, self, self.pos <= self.end);
+                    if (!(self.tag == JCTree.TYPEARRAY && encl.tag == JCTree.TYPEARRAY)) {
+                        check("end <= encl.end", encl, self, self.end <= encl.end);
+                    }
+                }
+            }
+
+            Info prevEncl = encl;
+            encl = self;
+            tree.accept(this);
+            encl = prevEncl;
+        }
+
+        @Override
+        public void visitVarDef(JCVariableDecl tree) {
+            // enum member declarations are desugared in the parser and have
+            // ill-defined semantics for tree positions, so for now, we
+            // skip the synthesized bits and just check parts which came from
+            // the original source text
+            if ((tree.mods.flags & Flags.ENUM) != 0) {
+                scan(tree.mods);
+                if (tree.init != null) {
+                    if (tree.init.getTag() == JCTree.NEWCLASS) {
+                        JCNewClass init = (JCNewClass) tree.init;
+                        if (init.args != null && init.args.nonEmpty()) {
+                            scan(init.args);
+                        }
+                        if (init.def != null && init.def.defs != null) {
+                            scan(init.def.defs);
+                        }
+                    }
+                }
+            } else
+                super.visitVarDef(tree);
+        }
+
+        boolean check(Info x) {
+            return tags.size() == 0 || tags.contains(tagNames.get(x.tag));
+        }
+
+        void check(String label, Info encl, Info self, boolean ok) {
+            if (!ok) {
+                if (gui) {
+                    if (viewer == null)
+                        viewer = new Viewer();
+                    viewer.addEntry(sourcefile, label, encl, self);
+                }
+
+                String s = self.tree.toString();
+                String msg = sourcefile.getName() + ": " + label + ": " +
+                        "encl:" + encl + " this:" + self + "\n" +
+                        s.substring(0, Math.min(80, s.length())).replaceAll("[\r\n]+", " ");
+                error(msg);
+            }
+        }
+
+        JavaFileObject sourcefile;
+        Map<JCTree, Integer> endPosTable;
+        Info encl;
+
+    }
+
+    /**
+     * Utility class providing easy access to position and other info for a tree node.
+     */
+    private class Info {
+        Info() {
+            tree = null;
+            tag = JCTree.ERRONEOUS;
+            start = 0;
+            pos = 0;
+            end = Integer.MAX_VALUE;
+        }
+
+        Info(JCTree tree, Map<JCTree, Integer> endPosTable) {
+            this.tree = tree;
+            tag = tree.getTag();
+            start = TreeInfo.getStartPos(tree);
+            pos = tree.pos;
+            end = TreeInfo.getEndPos(tree, endPosTable);
+        }
+
+        @Override
+        public String toString() {
+            return tagNames.get(tree.getTag()) + "[start:" + start + ",pos:" + pos + ",end:" + end + "]";
+        }
+
+        final JCTree tree;
+        final int tag;
+        final int start;
+        final int pos;
+        final int end;
+    }
+
+    /**
+     * Names for tree tags.
+     * javac does not provide an API to convert tag values to strings, so this class uses
+     * reflection to determine names of public static final int values in JCTree.
+     */
+    private static class TagNames {
+        String get(int tag) {
+            if (map == null) {
+                map = new HashMap<Integer, String>();
+                Class c = JCTree.class;
+                for (Field f : c.getDeclaredFields()) {
+                    if (f.getType().equals(int.class)) {
+                        int mods = f.getModifiers();
+                        if (Modifier.isPublic(mods) && Modifier.isStatic(mods) && Modifier.isFinal(mods)) {
+                            try {
+                                map.put(f.getInt(null), f.getName());
+                            } catch (IllegalAccessException e) {
+                            }
+                        }
+                    }
+                }
+            }
+            String name = map.get(tag);
+            return (name == null) ? "??" : name;
+        }
+
+        private Map<Integer, String> map;
+    }
+
+    /**
+     * Thrown when errors are found parsing a java file.
+     */
+    private static class ParseException extends Exception {
+        ParseException(String msg) {
+            super(msg);
+        }
+    }
+
+    /**
+     * DiagnosticListener to report diagnostics and count any errors that occur.
+     */
+    private static class Reporter implements DiagnosticListener<JavaFileObject> {
+        Reporter(PrintWriter out) {
+            this.out = out;
+        }
+
+        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+            out.println(diagnostic);
+            switch (diagnostic.getKind()) {
+                case ERROR:
+                    errors++;
+            }
+        }
+        int errors;
+        PrintWriter out;
+    }
+
+    /**
+     * GUI viewer for issues found by TreePosTester. The viewer provides a drop
+     * down list for selecting error conditions, a header area providing details
+     * about an error, and a text area with the ranges of text highlighted as
+     * appropriate.
+     */
+    private class Viewer extends JFrame {
+        /**
+         * Create a viewer.
+         */
+        Viewer() {
+            initGUI();
+        }
+
+        /**
+         * Add another entry to the list of errors.
+         * @param file The file containing the error
+         * @param check The condition that was being tested, and which failed
+         * @param encl the enclosing tree node
+         * @param self the tree node containing the error
+         */
+        void addEntry(JavaFileObject file, String check, Info encl, Info self) {
+            Entry e = new Entry(file, check, encl, self);
+            DefaultComboBoxModel m = (DefaultComboBoxModel) entries.getModel();
+            m.addElement(e);
+            if (m.getSize() == 1)
+                entries.setSelectedItem(e);
+        }
+
+        /**
+         * Initialize the GUI window.
+         */
+        private void initGUI() {
+            JPanel head = new JPanel(new GridBagLayout());
+            GridBagConstraints lc = new GridBagConstraints();
+            GridBagConstraints fc = new GridBagConstraints();
+            fc.anchor = GridBagConstraints.WEST;
+            fc.fill = GridBagConstraints.HORIZONTAL;
+            fc.gridwidth = GridBagConstraints.REMAINDER;
+
+            entries = new JComboBox();
+            entries.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    showEntry((Entry) entries.getSelectedItem());
+                }
+            });
+            fc.insets.bottom = 10;
+            head.add(entries, fc);
+            fc.insets.bottom = 0;
+            head.add(new JLabel("check:"), lc);
+            head.add(checkField = createTextField(80), fc);
+            fc.fill = GridBagConstraints.NONE;
+            head.add(setBackground(new JLabel("encl:"), enclColor), lc);
+            head.add(enclPanel = new InfoPanel(), fc);
+            head.add(setBackground(new JLabel("self:"), selfColor), lc);
+            head.add(selfPanel = new InfoPanel(), fc);
+            add(head, BorderLayout.NORTH);
+
+            body = new JTextArea();
+            body.setFont(Font.decode(Font.MONOSPACED));
+            body.addCaretListener(new CaretListener() {
+                public void caretUpdate(CaretEvent e) {
+                    int dot = e.getDot();
+                    int mark = e.getMark();
+                    if (dot == mark)
+                        statusText.setText("dot: " + dot);
+                    else
+                        statusText.setText("dot: " + dot + ", mark:" + mark);
+                }
+            });
+            JScrollPane p = new JScrollPane(body,
+                    JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+                    JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+            p.setPreferredSize(new Dimension(640, 480));
+            add(p, BorderLayout.CENTER);
+
+            statusText = createTextField(80);
+            add(statusText, BorderLayout.SOUTH);
+
+            pack();
+            setLocationRelativeTo(null); // centered on screen
+            setVisible(true);
+            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        }
+
+        /** Show an entry that has been selected. */
+        private void showEntry(Entry e) {
+            try {
+                // update simple fields
+                setTitle(e.file.getName());
+                checkField.setText(e.check);
+                enclPanel.setInfo(e.encl);
+                selfPanel.setInfo(e.self);
+                // show file text with highlights
+                body.setText(e.file.getCharContent(true).toString());
+                Highlighter highlighter = body.getHighlighter();
+                highlighter.removeAllHighlights();
+                addHighlight(highlighter, e.encl, enclColor);
+                addHighlight(highlighter, e.self, selfColor);
+                scroll(body, getMinPos(enclPanel.info, selfPanel.info));
+            } catch (IOException ex) {
+                body.setText("Cannot read " + e.file.getName() + ": " + e);
+            }
+        }
+
+        /** Create a test field. */
+        private JTextField createTextField(int width) {
+            JTextField f = new JTextField(width);
+            f.setEditable(false);
+            f.setBorder(null);
+            return f;
+        }
+
+        /** Add a highlighted region based on the positions in an Info object. */
+        private void addHighlight(Highlighter h, Info info, Color c) {
+            int start = info.start;
+            int end = info.end;
+            if (start == -1 && end == -1)
+                return;
+            if (start == -1)
+                start = end;
+            if (end == -1)
+                end = start;
+            try {
+                h.addHighlight(info.start, info.end,
+                        new DefaultHighlighter.DefaultHighlightPainter(c));
+                if (info.pos != -1) {
+                    Color c2 = new Color(c.getRed(), c.getGreen(), c.getBlue(), (int)(.4f * 255)); // 40%
+                    h.addHighlight(info.pos, info.pos + 1,
+                        new DefaultHighlighter.DefaultHighlightPainter(c2));
+                }
+            } catch (BadLocationException e) {
+                e.printStackTrace();
+            }
+        }
+
+        /** Get the minimum valid position in a set of info objects. */
+        private int getMinPos(Info... values) {
+            int i = Integer.MAX_VALUE;
+            for (Info info: values) {
+                if (info.start >= 0) i = Math.min(i, info.start);
+                if (info.pos   >= 0) i = Math.min(i, info.pos);
+                if (info.end   >= 0) i = Math.min(i, info.end);
+            }
+            return (i == Integer.MAX_VALUE) ? 0 : i;
+        }
+
+        /** Set the background on a component. */
+        private JComponent setBackground(JComponent comp, Color c) {
+            comp.setOpaque(true);
+            comp.setBackground(c);
+            return comp;
+        }
+
+        /** Scroll a text area to display a given position near the middle of the visible area. */
+        private void scroll(final JTextArea t, final int pos) {
+            // Using invokeLater appears to give text a chance to sort itself out
+            // before the scroll happens; otherwise scrollRectToVisible doesn't work.
+            // Maybe there's a better way to sync with the text...
+            EventQueue.invokeLater(new Runnable() {
+                public void run() {
+                    try {
+                        Rectangle r = t.modelToView(pos);
+                        JScrollPane p = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, t);
+                        r.y = Math.max(0, r.y - p.getHeight() * 2 / 5);
+                        r.height += p.getHeight() * 4 / 5;
+                        t.scrollRectToVisible(r);
+                    } catch (BadLocationException ignore) {
+                    }
+                }
+            });
+        }
+
+        private JComboBox entries;
+        private JTextField checkField;
+        private InfoPanel enclPanel;
+        private InfoPanel selfPanel;
+        private JTextArea body;
+        private JTextField statusText;
+
+        private Color selfColor = new Color(0.f, 1.f, 0.f, 0.2f); // 20% green
+        private Color enclColor = new Color(1.f, 0.f, 0.f, 0.2f); // 20% red
+
+        /** Panel to display an Info object. */
+        private class InfoPanel extends JPanel {
+            InfoPanel() {
+                add(tagName = createTextField(20));
+                add(new JLabel("start:"));
+                add(addListener(start = createTextField(6)));
+                add(new JLabel("pos:"));
+                add(addListener(pos = createTextField(6)));
+                add(new JLabel("end:"));
+                add(addListener(end = createTextField(6)));
+            }
+
+            void setInfo(Info info) {
+                this.info = info;
+                tagName.setText(tagNames.get(info.tag));
+                start.setText(String.valueOf(info.start));
+                pos.setText(String.valueOf(info.pos));
+                end.setText(String.valueOf(info.end));
+            }
+
+            JTextField addListener(final JTextField f) {
+                f.addMouseListener(new MouseAdapter() {
+                    @Override
+                    public void mouseClicked(MouseEvent e) {
+                        body.setCaretPosition(Integer.valueOf(f.getText()));
+                        body.getCaret().setVisible(true);
+                    }
+                });
+                return f;
+            }
+
+            Info info;
+            JTextField tagName;
+            JTextField start;
+            JTextField pos;
+            JTextField end;
+        }
+
+        /** Object to record information about an error to be displayed. */
+        private class Entry {
+            Entry(JavaFileObject file, String check, Info encl, Info self) {
+                this.file = file;
+                this.check = check;
+                this.encl = encl;
+                this.self= self;
+            }
+
+            @Override
+            public String toString() {
+                return file.getName() + " " + check + " " + getMinPos(encl, self);
+            }
+
+            final JavaFileObject file;
+            final String check;
+            final Info encl;
+            final Info self;
+        }
+    }
+}
+