diff options
author | cushon <cushon@google.com> | 2016-10-18 10:51:48 -0700 |
---|---|---|
committer | Liam Miller-Cushon <cushon@google.com> | 2016-10-18 11:35:31 -0700 |
commit | b0445ae9c37a1fab07fb5f234653e8ea072bf9fc (patch) | |
tree | 6e8fe4a070d506e6ec300514a1e344986b63298e | |
parent | 75946a85fe655bafc006b96e4ddf01990d5f2f7c (diff) | |
download | turbine-b0445ae9c37a1fab07fb5f234653e8ea072bf9fc.tar.gz |
Diagnostic improvements
Store position information in the AST, and use it to emit better errors
during binding. Refactor some passes to be non-static to make it easier
to pass context needed to format diagnostics.
MOE_MIGRATED_REVID=136492057
27 files changed, 648 insertions, 281 deletions
diff --git a/java/com/google/turbine/binder/Binder.java b/java/com/google/turbine/binder/Binder.java index 833ba4c..907da71 100644 --- a/java/com/google/turbine/binder/Binder.java +++ b/java/com/google/turbine/binder/Binder.java @@ -175,7 +175,8 @@ public class Binder { CompoundScope scope = topLevel.append(packageScope).append(importScope); for (ClassSymbol sym : entry.getValue()) { - env.putIfAbsent(sym, new PackageSourceBoundClass(ienv.get(sym), scope, memberImports)); + env.putIfAbsent( + sym, new PackageSourceBoundClass(ienv.get(sym), scope, memberImports, unit.source())); } } return env.build(); diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java index 797955d..201cb13 100644 --- a/java/com/google/turbine/binder/CanonicalTypeBinder.java +++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java @@ -67,7 +67,8 @@ public class CanonicalTypeBinder { base.scope(), base.memberImports(), base.retention(), - base.annotations()); + base.annotations(), + base.source()); } private static ImmutableList<FieldInfo> fields( diff --git a/java/com/google/turbine/binder/ConstBinder.java b/java/com/google/turbine/binder/ConstBinder.java index cbc94c6..4b82e57 100644 --- a/java/com/google/turbine/binder/ConstBinder.java +++ b/java/com/google/turbine/binder/ConstBinder.java @@ -70,7 +70,8 @@ public class ConstBinder { base.scope(), base.memberImports(), bindRetention(base.kind(), annos), - annos); + annos, + base.source()); } private ImmutableList<MethodInfo> bindMethods(ImmutableList<MethodInfo> methods) { diff --git a/java/com/google/turbine/binder/ConstEvaluator.java b/java/com/google/turbine/binder/ConstEvaluator.java index 3a95cd4..118b760 100644 --- a/java/com/google/turbine/binder/ConstEvaluator.java +++ b/java/com/google/turbine/binder/ConstEvaluator.java @@ -32,6 +32,7 @@ import com.google.turbine.binder.lookup.LookupKey; import com.google.turbine.binder.lookup.LookupResult; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.FieldSymbol; +import com.google.turbine.diag.TurbineError; import com.google.turbine.model.Const; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.model.TurbineFlag; @@ -147,7 +148,8 @@ public class ConstEvaluator { case CLASS_TY: { ClassTy classTy = (ClassTy) t.type(); - ClassSymbol classSym = HierarchyBinder.resolveClass(env, owner.scope(), sym, classTy); + ClassSymbol classSym = + HierarchyBinder.resolveClass(owner.source(), env, owner.scope(), sym, classTy); return new Const.ClassValue(Type.ClassTy.asNonParametricClassTy(classSym)); } default: @@ -809,6 +811,9 @@ public class ConstEvaluator { expr = arg; } Type ty = template.get(key); + if (ty == null) { + throw error(arg.position(), "cannot resolve %s", key); + } Const value = evalAnnotationValue(expr, ty); values.put(key, value); } @@ -834,6 +839,9 @@ public class ConstEvaluator { } Const evalAnnotationValue(Tree tree, Type ty) { + if (ty == null) { + throw error(tree.position(), "could not evaluate"); + } Const value = eval(tree); switch (ty.tyKind()) { case PRIM_TY: @@ -853,4 +861,8 @@ public class ConstEvaluator { throw new AssertionError(ty.tyKind()); } } + + private TurbineError error(int position, String message, Object... args) { + return TurbineError.format(owner.source(), position, message, args); + } } diff --git a/java/com/google/turbine/binder/HierarchyBinder.java b/java/com/google/turbine/binder/HierarchyBinder.java index 98ea74d..91f10ab 100644 --- a/java/com/google/turbine/binder/HierarchyBinder.java +++ b/java/com/google/turbine/binder/HierarchyBinder.java @@ -29,6 +29,8 @@ import com.google.turbine.binder.lookup.LookupResult; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.Symbol; import com.google.turbine.binder.sym.TyVarSymbol; +import com.google.turbine.diag.SourceFile; +import com.google.turbine.diag.TurbineError; import com.google.turbine.model.TurbineFlag; import com.google.turbine.model.TurbineTyKind; import com.google.turbine.model.TurbineVisibility; @@ -44,6 +46,23 @@ public class HierarchyBinder { Symbol origin, PackageSourceBoundClass base, Env<ClassSymbol, ? extends HeaderBoundClass> env) { + return new HierarchyBinder(origin, base, env).bind(); + } + + private final Symbol owner; + private final PackageSourceBoundClass base; + private final Env<ClassSymbol, ? extends HeaderBoundClass> env; + + private HierarchyBinder( + Symbol owner, + PackageSourceBoundClass base, + Env<ClassSymbol, ? extends HeaderBoundClass> env) { + this.owner = owner; + this.base = base; + this.env = env; + } + + private SourceHeaderBoundClass bind() { Tree.TyDecl decl = base.decl(); int access = 0; @@ -68,11 +87,11 @@ public class HierarchyBinder { } // types declared in interfaces annotations are implicitly public (JLS 9.5) - if (enclosedByInterface(env, base)) { + if (enclosedByInterface(base)) { access = TurbineVisibility.PUBLIC.setAccess(access); } - if ((access & TurbineFlag.ACC_STATIC) == 0 && implicitStatic(env, base)) { + if ((access & TurbineFlag.ACC_STATIC) == 0 && implicitStatic(base)) { access |= TurbineFlag.ACC_STATIC; } @@ -82,7 +101,7 @@ public class HierarchyBinder { ClassSymbol superclass; if (decl.xtnds().isPresent()) { - superclass = resolveClass(env, base.scope(), base.owner(), decl.xtnds().get()); + superclass = resolveClass(base.source(), env, base.scope(), base.owner(), decl.xtnds().get()); } else { switch (decl.tykind()) { case ENUM: @@ -107,7 +126,7 @@ public class HierarchyBinder { ImmutableList.Builder<ClassSymbol> interfaces = ImmutableList.builder(); if (!decl.impls().isEmpty()) { for (Tree.ClassTy i : decl.impls()) { - ClassSymbol result = resolveClass(env, base.scope(), base.owner(), i); + ClassSymbol result = resolveClass(base.source(), env, base.scope(), base.owner(), i); if (result == null) { throw new AssertionError(i); } @@ -121,7 +140,7 @@ public class HierarchyBinder { ImmutableMap.Builder<String, TyVarSymbol> typeParameters = ImmutableMap.builder(); for (Tree.TyParam p : decl.typarams()) { - typeParameters.put(p.name(), new TyVarSymbol(origin, p.name())); + typeParameters.put(p.name(), new TyVarSymbol(owner, p.name())); } return new SourceHeaderBoundClass( @@ -132,8 +151,7 @@ public class HierarchyBinder { * Nested enums (JLS 8.9) and types nested within interfaces and annotations (JLS 9.5) are * implicitly static */ - private static boolean implicitStatic( - Env<ClassSymbol, ? extends HeaderBoundClass> env, BoundClass c) { + private boolean implicitStatic(BoundClass c) { if (c.kind() == TurbineTyKind.ENUM) { return true; } @@ -154,8 +172,7 @@ public class HierarchyBinder { } /** Returns true if the given type is declared in an interface. */ - private static boolean enclosedByInterface( - Env<ClassSymbol, ? extends HeaderBoundClass> env, BoundClass c) { + private boolean enclosedByInterface(BoundClass c) { while (c.owner() != null) { c = env.get(c.owner()); switch (c.kind()) { @@ -192,6 +209,7 @@ public class HierarchyBinder { * non-canonical qualified type names. */ public static ClassSymbol resolveClass( + SourceFile source, Env<ClassSymbol, ? extends HeaderBoundClass> env, CompoundScope enclscope, ClassSymbol owner, @@ -205,7 +223,7 @@ public class HierarchyBinder { // Resolve the base symbol in the qualified name. LookupResult result = lookup(env, enclscope, owner, new LookupKey(flat)); if (result == null) { - return null; + throw TurbineError.format(source, ty.position(), String.format("symbol not found %s\n", ty)); } // Resolve pieces in the qualified name referring to member types. // This needs to consider member type declarations inherited from supertypes and interfaces. @@ -213,7 +231,8 @@ public class HierarchyBinder { for (String bit : result.remaining()) { sym = Resolve.resolve(env, sym, bit); if (sym == null) { - return null; + throw TurbineError.format( + source, ty.position(), String.format("symbol not found %s\n", bit)); } } return sym; @@ -238,4 +257,8 @@ public class HierarchyBinder { // qualified name resolution). return parent.lookup(lookup); } + + private TurbineError error(int position, String message, Object... args) { + return TurbineError.format(base.source(), position, message, args); + } } diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java index f141002..5ba0fb4 100644 --- a/java/com/google/turbine/binder/TypeBinder.java +++ b/java/com/google/turbine/binder/TypeBinder.java @@ -39,6 +39,7 @@ import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.binder.sym.MethodSymbol; import com.google.turbine.binder.sym.Symbol; import com.google.turbine.binder.sym.TyVarSymbol; +import com.google.turbine.diag.TurbineError; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.model.TurbineFlag; import com.google.turbine.model.TurbineTyKind; @@ -124,8 +125,22 @@ public class TypeBinder { /** Creates {@link SourceTypeBoundClass} nodes for a compilation. */ public static SourceTypeBoundClass bind( - final Env<ClassSymbol, HeaderBoundClass> env, ClassSymbol sym, SourceHeaderBoundClass base) { + Env<ClassSymbol, HeaderBoundClass> env, ClassSymbol sym, SourceHeaderBoundClass base) { + return new TypeBinder(env, sym, base).bind(); + } + + private final Env<ClassSymbol, HeaderBoundClass> env; + private final ClassSymbol owner; + private final SourceHeaderBoundClass base; + + public TypeBinder( + Env<ClassSymbol, HeaderBoundClass> env, ClassSymbol owner, SourceHeaderBoundClass base) { + this.env = env; + this.owner = owner; + this.base = base; + } + private SourceTypeBoundClass bind() { // This method uses two scopes. This first one is built up as we process the signature // and its elements become visible to subsequent elements (e.g. type parameters can // refer to previous declared type parameters, the superclass type can refer to @@ -133,12 +148,12 @@ public class TypeBinder { // once the signature is fully determined. CompoundScope enclosingScope = base.scope(); enclosingScope = enclosingScope.append(new ClassMemberScope(base.owner(), env)); - enclosingScope = enclosingScope.append(new SingletonScope(base.decl().name(), sym)); + enclosingScope = enclosingScope.append(new SingletonScope(base.decl().name(), owner)); // type parameters can refer to each other in f-bounds, so update the scope first enclosingScope = enclosingScope.append(new MapScope(base.typeParameters())); final ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes = - bindTyParams(env, base.decl().typarams(), enclosingScope, base.typeParameters()); + bindTyParams(base.decl().typarams(), enclosingScope, base.typeParameters()); Type.ClassTy superClassType; switch (base.kind()) { @@ -149,7 +164,7 @@ public class TypeBinder { new Type.ClassTy.SimpleClassTy( ClassSymbol.ENUM, ImmutableList.of( - new Type.ConcreteTyArg(Type.ClassTy.asNonParametricClassTy(sym)))))); + new Type.ConcreteTyArg(Type.ClassTy.asNonParametricClassTy(owner)))))); break; case ANNOTATION: superClassType = Type.ClassTy.asNonParametricClassTy(ClassSymbol.ANNOTATION); @@ -157,8 +172,7 @@ public class TypeBinder { case CLASS: case INTERFACE: if (base.decl().xtnds().isPresent()) { - superClassType = - (Type.ClassTy) bindClassTy(env, enclosingScope, base.decl().xtnds().get()); + superClassType = (Type.ClassTy) bindClassTy(enclosingScope, base.decl().xtnds().get()); // Members inherited from the superclass are visible to interface types. // (The same is not true for interface types declared before other interface // types, at least according to javac.) @@ -184,18 +198,18 @@ public class TypeBinder { ImmutableList.Builder<Type.ClassTy> interfaceTypes = ImmutableList.builder(); for (Tree.ClassTy i : base.decl().impls()) { - interfaceTypes.add((Type.ClassTy) bindClassTy(env, enclosingScope, i)); + interfaceTypes.add((Type.ClassTy) bindClassTy(enclosingScope, i)); } - CompoundScope scope = base.scope().append(new ClassMemberScope(sym, env)); + CompoundScope scope = base.scope().append(new ClassMemberScope(owner, env)); - List<MethodInfo> methods = bindMethods(base, env, scope, sym, base.decl().members()); - addSyntheticMethods(sym, base, methods); + List<MethodInfo> methods = bindMethods(scope, base.decl().members()); + addSyntheticMethods(methods); - ImmutableList<FieldInfo> fields = bindFields(base, env, scope, sym, base.decl().members()); + ImmutableList<FieldInfo> fields = bindFields(scope, base.decl().members()); ImmutableList<TypeBoundClass.AnnoInfo> annotations = - bindAnnotations(env, scope, base.decl().annos()); + bindAnnotations(scope, base.decl().annos()); return new SourceTypeBoundClass( interfaceTypes.build(), @@ -213,39 +227,38 @@ public class TypeBinder { scope, base.memberImports(), /*retention*/ null, - annotations); + annotations, + base.source()); } /** Add synthetic and implicit methods, including default constructors and enum methods. */ - static void addSyntheticMethods( - ClassSymbol sym, HeaderBoundClass base, List<MethodInfo> methods) { + void addSyntheticMethods(List<MethodInfo> methods) { switch (base.kind()) { case CLASS: - maybeAddDefaultConstructor(sym, base, methods); + maybeAddDefaultConstructor(methods); break; case ENUM: - addEnumMethods(sym, methods); + addEnumMethods(methods); break; default: break; } } - private static void maybeAddDefaultConstructor( - ClassSymbol owner, HeaderBoundClass info, List<MethodInfo> methods) { + private void maybeAddDefaultConstructor(List<MethodInfo> methods) { if (hasConstructor(methods)) { return; } ImmutableList<ParamInfo> formals; - if (hasEnclosingInstance(info)) { + if (hasEnclosingInstance(base)) { formals = ImmutableList.of( new ParamInfo( - Type.ClassTy.asNonParametricClassTy(info.owner()), ImmutableList.of(), true)); + Type.ClassTy.asNonParametricClassTy(base.owner()), ImmutableList.of(), true)); } else { formals = ImmutableList.of(); } - int access = TurbineVisibility.fromAccess(info.access()).flag(); + int access = TurbineVisibility.fromAccess(base.access()).flag(); access |= TurbineFlag.ACC_SYNTH_CTOR; methods.add( new MethodInfo( @@ -260,7 +273,7 @@ public class TypeBinder { ImmutableList.of())); } - private static void addEnumMethods(ClassSymbol owner, List<MethodInfo> methods) { + private void addEnumMethods(List<MethodInfo> methods) { if (!hasConstructor(methods)) { methods.add( new MethodInfo( @@ -313,11 +326,8 @@ public class TypeBinder { } /** Bind type parameter types. */ - private static ImmutableMap<TyVarSymbol, TyVarInfo> bindTyParams( - Env<ClassSymbol, HeaderBoundClass> env, - ImmutableList<Tree.TyParam> trees, - CompoundScope scope, - Map<String, TyVarSymbol> symbols) { + private ImmutableMap<TyVarSymbol, TyVarInfo> bindTyParams( + ImmutableList<Tree.TyParam> trees, CompoundScope scope, Map<String, TyVarSymbol> symbols) { ImmutableMap.Builder<TyVarSymbol, TyVarInfo> result = ImmutableMap.builder(); for (Tree.TyParam tree : trees) { TyVarSymbol sym = symbols.get(tree.name()); @@ -325,8 +335,8 @@ public class TypeBinder { ImmutableList.Builder<Type> interfaceBounds = ImmutableList.builder(); boolean first = true; for (Tree bound : tree.bounds()) { - Type ty = bindTy(env, scope, bound); - if (first && !isInterface(env, ty)) { + Type ty = bindTy(scope, bound); + if (first && !isInterface(ty)) { classBound = ty; } else { interfaceBounds.add(ty); @@ -338,7 +348,7 @@ public class TypeBinder { return result.build(); } - private static boolean isInterface(Env<ClassSymbol, HeaderBoundClass> env, Type ty) { + private boolean isInterface(Type ty) { if (ty.tyKind() != Type.TyKind.CLASS_TY) { return false; } @@ -346,27 +356,17 @@ public class TypeBinder { return hi.kind() == TurbineTyKind.INTERFACE; } - private static List<MethodInfo> bindMethods( - HeaderBoundClass base, - Env<ClassSymbol, HeaderBoundClass> env, - CompoundScope scope, - ClassSymbol sym, - ImmutableList<Tree> members) { + private List<MethodInfo> bindMethods(CompoundScope scope, ImmutableList<Tree> members) { List<MethodInfo> methods = new ArrayList<>(); for (Tree member : members) { if (member.kind() == Tree.Kind.METH_DECL) { - methods.add(bindMethod(base, env, scope, sym, (Tree.MethDecl) member)); + methods.add(bindMethod(scope, (Tree.MethDecl) member)); } } return methods; } - private static MethodInfo bindMethod( - HeaderBoundClass base, - Env<ClassSymbol, HeaderBoundClass> env, - CompoundScope scope, - ClassSymbol owner, - Tree.MethDecl t) { + private MethodInfo bindMethod(CompoundScope scope, Tree.MethDecl t) { MethodSymbol sym = new MethodSymbol(owner, t.name()); @@ -374,7 +374,7 @@ public class TypeBinder { { ImmutableMap.Builder<String, TyVarSymbol> builder = ImmutableMap.builder(); for (Tree.TyParam pt : t.typarams()) { - builder.put(pt.name(), new TyVarSymbol(sym, pt.name())); + builder.put(pt.name(), new TyVarSymbol(owner, pt.name())); } typeParameters = builder.build(); } @@ -382,11 +382,11 @@ public class TypeBinder { // type parameters can refer to each other in f-bounds, so update the scope first scope = scope.append(new MapScope(typeParameters)); ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes = - bindTyParams(env, t.typarams(), scope, typeParameters); + bindTyParams(t.typarams(), scope, typeParameters); Type returnType; if (t.ret().isPresent()) { - returnType = bindTy(env, scope, t.ret().get()); + returnType = bindTy(scope, t.ret().get()); } else { returnType = Type.VOID; } @@ -412,13 +412,11 @@ public class TypeBinder { for (Tree.VarDecl p : t.params()) { parameters.add( new ParamInfo( - bindTy(env, scope, p.ty()), - bindAnnotations(env, scope, p.annos()), - /*synthetic*/ false)); + bindTy(scope, p.ty()), bindAnnotations(scope, p.annos()), /*synthetic*/ false)); } ImmutableList.Builder<Type> exceptions = ImmutableList.builder(); for (Tree.ClassTy p : t.exntys()) { - exceptions.add(bindClassTy(env, scope, p)); + exceptions.add(bindClassTy(scope, p)); } int access = 0; @@ -443,7 +441,7 @@ public class TypeBinder { break; } - ImmutableList<TypeBoundClass.AnnoInfo> annotations = bindAnnotations(env, scope, t.annos()); + ImmutableList<TypeBoundClass.AnnoInfo> annotations = bindAnnotations(scope, t.annos()); return new MethodInfo( sym, typeParameterTypes, @@ -462,35 +460,25 @@ public class TypeBinder { && ((base.access() & TurbineFlag.ACC_STATIC) == 0); } - private static ImmutableList<FieldInfo> bindFields( - HeaderBoundClass base, - Env<ClassSymbol, HeaderBoundClass> env, - CompoundScope scope, - ClassSymbol sym, - ImmutableList<Tree> members) { + private ImmutableList<FieldInfo> bindFields(CompoundScope scope, ImmutableList<Tree> members) { ImmutableList.Builder<FieldInfo> fields = ImmutableList.builder(); for (Tree member : members) { if (member.kind() == Tree.Kind.VAR_DECL) { - fields.add(bindField(base, env, scope, sym, (Tree.VarDecl) member)); + fields.add(bindField(scope, (Tree.VarDecl) member)); } } return fields.build(); } - private static FieldInfo bindField( - HeaderBoundClass hi, - Env<ClassSymbol, HeaderBoundClass> env, - CompoundScope scope, - ClassSymbol owner, - Tree.VarDecl decl) { + private FieldInfo bindField(CompoundScope scope, Tree.VarDecl decl) { FieldSymbol sym = new FieldSymbol(owner, decl.name()); - Type type = bindTy(env, scope, decl.ty()); - ImmutableList<TypeBoundClass.AnnoInfo> annotations = bindAnnotations(env, scope, decl.annos()); + Type type = bindTy(scope, decl.ty()); + ImmutableList<TypeBoundClass.AnnoInfo> annotations = bindAnnotations(scope, decl.annos()); int access = 0; for (TurbineModifier m : decl.mods()) { access |= m.flag(); } - switch (hi.kind()) { + switch (base.kind()) { case INTERFACE: case ANNOTATION: access |= TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL | TurbineFlag.ACC_STATIC; @@ -501,8 +489,8 @@ public class TypeBinder { return new FieldInfo(sym, type, access, annotations, decl, null); } - private static ImmutableList<TypeBoundClass.AnnoInfo> bindAnnotations( - Env<ClassSymbol, HeaderBoundClass> env, CompoundScope scope, ImmutableList<Tree.Anno> trees) { + private ImmutableList<TypeBoundClass.AnnoInfo> bindAnnotations( + CompoundScope scope, ImmutableList<Tree.Anno> trees) { ImmutableList.Builder<TypeBoundClass.AnnoInfo> result = ImmutableList.builder(); for (Tree.Anno tree : trees) { LookupResult lookupResult = scope.lookup(new LookupKey(tree.name())); @@ -515,33 +503,32 @@ public class TypeBinder { return result.build(); } - private static ImmutableList<Type.TyArg> bindTyArgs( - Env<ClassSymbol, HeaderBoundClass> env, CompoundScope scope, ImmutableList<Tree.Type> targs) { + private ImmutableList<Type.TyArg> bindTyArgs( + CompoundScope scope, ImmutableList<Tree.Type> targs) { ImmutableList.Builder<Type.TyArg> result = ImmutableList.builder(); for (Tree.Type ty : targs) { - result.add(bindTyArg(env, scope, ty)); + result.add(bindTyArg(scope, ty)); } return result.build(); } - private static Type.TyArg bindTyArg( - Env<ClassSymbol, HeaderBoundClass> env, CompoundScope scope, Tree.Type ty) { + private Type.TyArg bindTyArg(CompoundScope scope, Tree.Type ty) { switch (ty.kind()) { case WILD_TY: - return bindWildTy(env, scope, (Tree.WildTy) ty); + return bindWildTy(scope, (Tree.WildTy) ty); default: - return new Type.ConcreteTyArg(bindTy(env, scope, ty)); + return new Type.ConcreteTyArg(bindTy(scope, ty)); } } - private static Type bindTy(Env<ClassSymbol, HeaderBoundClass> env, CompoundScope scope, Tree t) { + private Type bindTy(CompoundScope scope, Tree t) { switch (t.kind()) { case CLASS_TY: - return bindClassTy(env, scope, (Tree.ClassTy) t); + return bindClassTy(scope, (Tree.ClassTy) t); case PRIM_TY: return bindPrimTy((Tree.PrimTy) t); case ARR_TY: - return bindArrTy(env, scope, (Tree.ArrTy) t); + return bindArrTy(scope, (Tree.ArrTy) t); case VOID_TY: return Type.VOID; default: @@ -549,8 +536,7 @@ public class TypeBinder { } } - private static Type bindClassTy( - Env<ClassSymbol, HeaderBoundClass> env, CompoundScope scope, Tree.ClassTy t) { + private Type bindClassTy(CompoundScope scope, Tree.ClassTy t) { // flatten the components of a qualified class type ArrayList<Tree.ClassTy> flat; { @@ -567,12 +553,15 @@ public class TypeBinder { } // resolve the prefix to a symbol LookupResult result = scope.lookup(new LookupKey(names)); + if (result == null) { + throw error(t.position(), "symbol not found %s", t); + } Verify.verifyNotNull(result, "%s", names); Symbol sym = result.sym(); switch (sym.symKind()) { case CLASS: // resolve any remaining types in the qualified name, and their type arguments - return bindClassTyRest(env, scope, flat, names, result, (ClassSymbol) sym); + return bindClassTyRest(scope, flat, names, result, (ClassSymbol) sym); case TY_PARAM: Verify.verify(result.remaining().isEmpty(), "%s", result.remaining()); return new Type.TyVar((TyVarSymbol) sym); @@ -581,8 +570,7 @@ public class TypeBinder { } } - private static Type bindClassTyRest( - Env<ClassSymbol, HeaderBoundClass> env, + private Type bindClassTyRest( CompoundScope scope, ArrayList<Tree.ClassTy> flat, ArrayList<String> bits, @@ -590,12 +578,11 @@ public class TypeBinder { ClassSymbol sym) { int idx = bits.size() - result.remaining().size() - 1; ImmutableList.Builder<Type.ClassTy.SimpleClassTy> classes = ImmutableList.builder(); - classes.add( - new Type.ClassTy.SimpleClassTy(sym, bindTyArgs(env, scope, flat.get(idx++).tyargs()))); + classes.add(new Type.ClassTy.SimpleClassTy(sym, bindTyArgs(scope, flat.get(idx++).tyargs()))); for (; idx < flat.size(); idx++) { Tree.ClassTy curr = flat.get(idx); sym = Resolve.resolve(env, sym, curr.name()); - classes.add(new Type.ClassTy.SimpleClassTy(sym, bindTyArgs(env, scope, curr.tyargs()))); + classes.add(new Type.ClassTy.SimpleClassTy(sym, bindTyArgs(scope, curr.tyargs()))); } return new Type.ClassTy(classes.build()); } @@ -604,20 +591,22 @@ public class TypeBinder { return new Type.PrimTy(t.tykind()); } - private static Type bindArrTy( - Env<ClassSymbol, HeaderBoundClass> env, CompoundScope scope, Tree.ArrTy t) { + private Type bindArrTy(CompoundScope scope, Tree.ArrTy t) { verify(t.elem().kind() != Tree.Kind.ARR_TY); - return new Type.ArrayTy(t.dim(), bindTy(env, scope, t.elem())); + return new Type.ArrayTy(t.dim(), bindTy(scope, t.elem())); } - private static Type.TyArg bindWildTy( - Env<ClassSymbol, HeaderBoundClass> env, CompoundScope scope, Tree.WildTy t) { + private Type.TyArg bindWildTy(CompoundScope scope, Tree.WildTy t) { if (t.lower().isPresent()) { - return new Type.WildLowerBoundedTy(bindTy(env, scope, t.lower().get())); + return new Type.WildLowerBoundedTy(bindTy(scope, t.lower().get())); } else if (t.upper().isPresent()) { - return new Type.WildUpperBoundedTy(bindTy(env, scope, t.upper().get())); + return new Type.WildUpperBoundedTy(bindTy(scope, t.upper().get())); } else { return Type.WILD_TY; } } + + private TurbineError error(int position, String message, Object... args) { + return TurbineError.format(base.source(), position, message, args); + } } diff --git a/java/com/google/turbine/binder/bound/PackageSourceBoundClass.java b/java/com/google/turbine/binder/bound/PackageSourceBoundClass.java index 7737e25..6b708f9 100644 --- a/java/com/google/turbine/binder/bound/PackageSourceBoundClass.java +++ b/java/com/google/turbine/binder/bound/PackageSourceBoundClass.java @@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableMap; import com.google.turbine.binder.lookup.CompoundScope; import com.google.turbine.binder.lookup.MemberImportIndex; import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.diag.SourceFile; import com.google.turbine.model.TurbineTyKind; import com.google.turbine.tree.Tree; @@ -28,12 +29,17 @@ public class PackageSourceBoundClass implements BoundClass { private final SourceBoundClass base; private final CompoundScope scope; private final MemberImportIndex memberImports; + private final SourceFile source; public PackageSourceBoundClass( - SourceBoundClass base, CompoundScope scope, MemberImportIndex memberImports) { + SourceBoundClass base, + CompoundScope scope, + MemberImportIndex memberImports, + SourceFile source) { this.base = base; this.scope = scope; this.memberImports = memberImports; + this.source = source; } public Tree.TyDecl decl() { @@ -63,4 +69,9 @@ public class PackageSourceBoundClass implements BoundClass { public MemberImportIndex memberImports() { return memberImports; } + + /** The source file. */ + public SourceFile source() { + return source; + } } diff --git a/java/com/google/turbine/binder/bound/SourceHeaderBoundClass.java b/java/com/google/turbine/binder/bound/SourceHeaderBoundClass.java index a30daac..f8f5d02 100644 --- a/java/com/google/turbine/binder/bound/SourceHeaderBoundClass.java +++ b/java/com/google/turbine/binder/bound/SourceHeaderBoundClass.java @@ -22,6 +22,7 @@ import com.google.turbine.binder.lookup.CompoundScope; import com.google.turbine.binder.lookup.MemberImportIndex; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.TyVarSymbol; +import com.google.turbine.diag.SourceFile; import com.google.turbine.model.TurbineTyKind; import com.google.turbine.tree.Tree; @@ -96,4 +97,9 @@ public class SourceHeaderBoundClass implements HeaderBoundClass { public MemberImportIndex memberImports() { return base.memberImports(); } + + /** The source file. */ + public SourceFile source() { + return base.source(); + } } diff --git a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java index e86af7e..994c481 100644 --- a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java +++ b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java @@ -22,8 +22,10 @@ import com.google.turbine.binder.lookup.CompoundScope; import com.google.turbine.binder.lookup.MemberImportIndex; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.TyVarSymbol; +import com.google.turbine.diag.SourceFile; import com.google.turbine.model.TurbineTyKind; import com.google.turbine.type.Type; +import com.google.turbine.type.Type.ClassTy; import java.lang.annotation.RetentionPolicy; import javax.annotation.Nullable; @@ -48,10 +50,11 @@ public class SourceTypeBoundClass implements TypeBoundClass { private final MemberImportIndex memberImports; private final RetentionPolicy retention; private final ImmutableList<AnnoInfo> annotations; + private final SourceFile source; public SourceTypeBoundClass( - ImmutableList<Type.ClassTy> interfaceTypes, - Type.ClassTy superClassType, + ImmutableList<ClassTy> interfaceTypes, + ClassTy superClassType, ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes, int access, ImmutableList<MethodInfo> methods, @@ -65,7 +68,8 @@ public class SourceTypeBoundClass implements TypeBoundClass { CompoundScope scope, MemberImportIndex memberImports, RetentionPolicy retention, - ImmutableList<AnnoInfo> annotations) { + ImmutableList<AnnoInfo> annotations, + SourceFile source) { this.interfaceTypes = interfaceTypes; this.superClassType = superClassType; this.typeParameterTypes = typeParameterTypes; @@ -82,6 +86,7 @@ public class SourceTypeBoundClass implements TypeBoundClass { this.memberImports = memberImports; this.retention = retention; this.annotations = annotations; + this.source = source; } @Override @@ -167,4 +172,9 @@ public class SourceTypeBoundClass implements TypeBoundClass { public ImmutableList<AnnoInfo> annotations() { return annotations; } + + /** The source file. */ + public SourceFile source() { + return source; + } } diff --git a/java/com/google/turbine/diag/LineMap.java b/java/com/google/turbine/diag/LineMap.java index 0805de5..7a39aba 100644 --- a/java/com/google/turbine/diag/LineMap.java +++ b/java/com/google/turbine/diag/LineMap.java @@ -18,7 +18,6 @@ package com.google.turbine.diag; import static com.google.common.base.Preconditions.checkArgument; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableRangeMap; import com.google.common.collect.Range; @@ -62,30 +61,22 @@ public class LineMap { return new LineMap(source, builder.build()); } - /** The zero-indexed column number of the given souce position. */ + /** The zero-indexed column number of the given source position. */ public int column(int position) { - checkArgument(0 <= position && position < source.length()); + checkArgument(0 <= position && position < source.length(), "%s", position); return position - lines.getEntry(position).getKey().lowerEndpoint(); } - /** The one-indexed line number of the given souce position. */ + /** The one-indexed line number of the given source position. */ public int lineNumber(int position) { - checkArgument(0 <= position && position < source.length()); + checkArgument(0 <= position && position < source.length(), "%s", position); return lines.get(position); } - /** Formats a diagnostic at the given source position. */ - public String formatDiagnostic(int position, String message) { - checkArgument(0 <= position && position < source.length()); + /** The one-indexed line of the given source position. */ + public String line(int position) { + checkArgument(0 <= position && position < source.length(), "%s", position); Range<Integer> range = lines.getEntry(position).getKey(); - StringBuilder sb = new StringBuilder(); - sb.append(lineNumber(position)).append(':'); - int column = column(position); - sb.append(column).append(": "); - sb.append(message).append(System.lineSeparator()); - String line = source.substring(range.lowerEndpoint(), range.upperEndpoint()); - sb.append(line.trim()).append(System.lineSeparator()); - sb.append(Strings.repeat(" ", column)).append('^'); - return sb.toString(); + return source.substring(range.lowerEndpoint(), range.upperEndpoint()); } } diff --git a/java/com/google/turbine/parse/ParseError.java b/java/com/google/turbine/diag/SourceFile.java index f3cc8fb..cb4133b 100644 --- a/java/com/google/turbine/parse/ParseError.java +++ b/java/com/google/turbine/diag/SourceFile.java @@ -14,20 +14,26 @@ * limitations under the License. */ -package com.google.turbine.parse; +package com.google.turbine.diag; -/** A parse error. */ -public class ParseError extends Error { - private final int position; +/** A source file. */ +public class SourceFile { - /** @param position the character offset of the parser error. */ - public ParseError(int position, String message) { - super(message); - this.position = position; + private final String path; + private final String source; + + public SourceFile(String path, String source) { + this.path = path; + this.source = source; + } + + /** The path. */ + public String path() { + return path; } - /** Returns the UTF-16 code unit offset of the error. */ - public int position() { - return position; + /** The source. */ + public String source() { + return source; } } diff --git a/java/com/google/turbine/diag/TurbineError.java b/java/com/google/turbine/diag/TurbineError.java new file mode 100644 index 0000000..19b5991 --- /dev/null +++ b/java/com/google/turbine/diag/TurbineError.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.diag; + +import static com.google.common.base.MoreObjects.firstNonNull; + +import com.google.common.base.CharMatcher; +import com.google.common.base.Strings; + +/** A compilation error. */ +public class TurbineError extends Error { + + /** + * Formats a diagnostic. + * + * @param source the source file + * @param position the diagnostic position + * @param format a printf-style format string + * @param args format args + */ + public static TurbineError format( + SourceFile source, int position, String format, Object... args) { + String path = firstNonNull(source.path(), "<>"); + LineMap lineMap = LineMap.create(source.source()); + int lineNumber = lineMap.lineNumber(position); + int column = lineMap.column(position); + String message = String.format(format, args); + + StringBuilder sb = new StringBuilder(path).append(": "); + sb.append(lineNumber).append(':').append(column).append(": "); + sb.append(message.trim()).append(System.lineSeparator()); + sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(lineMap.line(position))) + .append(System.lineSeparator()); + sb.append(Strings.repeat(" ", column)).append('^'); + String diagnostic = sb.toString(); + + return new TurbineError(path, lineMap, column, message, diagnostic); + } + + private TurbineError( + String path, LineMap lineMap, int column, String message, String diagnostic) { + super(diagnostic); + } +} diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java index c3e4e88..7749b71 100644 --- a/java/com/google/turbine/parse/ConstExpressionParser.java +++ b/java/com/google/turbine/parse/ConstExpressionParser.java @@ -21,13 +21,13 @@ import static com.google.common.collect.Iterables.getOnlyElement; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.primitives.Ints; +import com.google.turbine.diag.TurbineError; import com.google.turbine.model.Const; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.ClassLiteral; import com.google.turbine.tree.Tree.ClassTy; import com.google.turbine.tree.Tree.Expression; -import com.google.turbine.tree.Tree.VoidTy; import com.google.turbine.tree.TurbineOperatorKind; import javax.annotation.Nullable; @@ -35,11 +35,12 @@ import javax.annotation.Nullable; public class ConstExpressionParser { Token token; + private int position; private final Lexer lexer; public ConstExpressionParser(Lexer lexer) { this.lexer = lexer; - token = lexer.next(); + eat(); } private static TurbineOperatorKind operator(Token token) { @@ -104,10 +105,12 @@ public class ConstExpressionParser { return finishLiteral(TurbineConstantTypeKind.FLOAT, negate); case TRUE: eat(); - return new Tree.Literal(TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(true)); + return new Tree.Literal( + position, TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(true)); case FALSE: eat(); - return new Tree.Literal(TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(false)); + return new Tree.Literal( + position, TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(false)); case CHAR_LITERAL: return finishLiteral(TurbineConstantTypeKind.CHAR, negate); case STRING_LITERAL: @@ -148,7 +151,7 @@ public class ConstExpressionParser { case BOOLEAN: return primitiveClassLiteral(TurbineConstantTypeKind.BOOLEAN); case VOID: - return primitiveClassLiteral(VoidTy.INSTANCE); + return primitiveClassLiteral(new Tree.VoidTy(position)); case AT: return annotation(); default: @@ -157,7 +160,7 @@ public class ConstExpressionParser { } private Expression primitiveClassLiteral(TurbineConstantTypeKind type) { - return primitiveClassLiteral(new Tree.PrimTy(type)); + return primitiveClassLiteral(new Tree.PrimTy(position, type)); } private Expression primitiveClassLiteral(Tree.Type type) { @@ -170,7 +173,7 @@ public class ConstExpressionParser { return null; } eat(); - return new ClassLiteral(type); + return new ClassLiteral(position, type); } private Tree.Expression maybeCast() { @@ -226,9 +229,7 @@ public class ConstExpressionParser { case NOT: case TILDE: case IDENT: - { - return new Tree.TypeCast(asClassTy(cvar.name()), primary(false)); - } + return new Tree.TypeCast(position, asClassTy(cvar.name()), primary(false)); default: return expr; } @@ -240,19 +241,20 @@ public class ConstExpressionParser { private ClassTy asClassTy(ImmutableList<String> names) { ClassTy cty = null; for (String bit : names) { - cty = new ClassTy(Optional.fromNullable(cty), bit, ImmutableList.<Tree.Type>of()); + cty = new ClassTy(position, Optional.fromNullable(cty), bit, ImmutableList.<Tree.Type>of()); } return cty; } private void eat() { token = lexer.next(); + position = lexer.position(); } private Tree.Expression arrayInitializer() { if (token == Token.RBRACE) { eat(); - return new Tree.ArrayInit(ImmutableList.<Tree.Expression>of()); + return new Tree.ArrayInit(position, ImmutableList.<Tree.Expression>of()); } ImmutableList.Builder<Tree.Expression> exprs = ImmutableList.builder(); @@ -278,7 +280,7 @@ public class ConstExpressionParser { return null; } } - return new Tree.ArrayInit(exprs.build()); + return new Tree.ArrayInit(position, exprs.build()); } /** Finish hex, decimal, octal, and binary integer literals (see JLS 3.10.1). */ @@ -345,10 +347,10 @@ public class ConstExpressionParser { value = new Const.StringValue(text); break; default: - throw error(kind); + throw error("%s", kind); } eat(); - return new Tree.Literal(kind, value); + return new Tree.Literal(position, kind, value); } /** @@ -399,11 +401,12 @@ public class ConstExpressionParser { if (expr == null) { return null; } - return new Tree.Unary(expr, op); + return new Tree.Unary(position, expr, op); } @Nullable private Tree.Expression qualIdent() { + int pos = position; ImmutableList.Builder<String> bits = ImmutableList.builder(); bits.add(lexer.stringValue()); eat(); @@ -416,13 +419,13 @@ public class ConstExpressionParser { case CLASS: // TODO(cushon): only allow in annotations? eat(); - return new Tree.ClassLiteral(asClassTy(bits.build())); + return new Tree.ClassLiteral(pos, asClassTy(bits.build())); default: return null; } eat(); } - return new Tree.ConstVarName(bits.build()); + return new Tree.ConstVarName(pos, bits.build()); } public Tree.Expression expression() { @@ -465,7 +468,7 @@ public class ConstExpressionParser { } else if (op == TurbineOperatorKind.ASSIGN) { term1 = assign(term1, op); } else { - term1 = new Tree.Binary(term1, expression(op.prec()), op); + term1 = new Tree.Binary(position, term1, expression(op.prec()), op); } if (term1 == null) { return null; @@ -483,7 +486,7 @@ public class ConstExpressionParser { } String name = getOnlyElement(names); Tree.Expression rhs = expression(op.prec()); - return new Tree.Assign(name, rhs); + return new Tree.Assign(term1.position(), name, rhs); } private Tree.Expression ternary(Tree.Expression term1) { @@ -499,7 +502,7 @@ public class ConstExpressionParser { if (elseExpr == null) { return null; } - return new Tree.Conditional(term1, thenExpr, elseExpr); + return new Tree.Conditional(position, term1, thenExpr, elseExpr); } private Tree.Expression castTail(TurbineConstantTypeKind ty) { @@ -511,7 +514,7 @@ public class ConstExpressionParser { if (rhs == null) { throw new AssertionError(); } - return new Tree.TypeCast(new Tree.PrimTy(ty), rhs); + return new Tree.TypeCast(position, new Tree.PrimTy(position, ty), rhs); } private Tree.AnnoExpr annotation() { @@ -539,10 +542,10 @@ public class ConstExpressionParser { eat(); } } - return new Tree.AnnoExpr(new Tree.Anno(name, args.build())); + return new Tree.AnnoExpr(position, new Tree.Anno(position, name, args.build())); } - private ParseError error(Object message) { - return new ParseError(lexer.position(), String.valueOf(message)); + private TurbineError error(String message, Object... args) { + return TurbineError.format(lexer.source(), lexer.position(), message, args); } } diff --git a/java/com/google/turbine/parse/IteratorLexer.java b/java/com/google/turbine/parse/IteratorLexer.java index 04b8ab4..823f9bb 100644 --- a/java/com/google/turbine/parse/IteratorLexer.java +++ b/java/com/google/turbine/parse/IteratorLexer.java @@ -16,6 +16,7 @@ package com.google.turbine.parse; +import com.google.turbine.diag.SourceFile; import java.util.Iterator; /** @@ -25,14 +26,21 @@ import java.util.Iterator; */ public class IteratorLexer implements Lexer { + private final SourceFile source; private final Iterator<SavedToken> it; private SavedToken curr; - public IteratorLexer(Iterator<SavedToken> it) { + public IteratorLexer(SourceFile source, Iterator<SavedToken> it) { + this.source = source; this.it = it; } @Override + public SourceFile source() { + return source; + } + + @Override public Token next() { if (it.hasNext()) { curr = it.next(); @@ -48,6 +56,7 @@ public class IteratorLexer implements Lexer { @Override public int position() { - return curr.position; + // TODO(cushon): test expression position EOF handling + return curr != null ? curr.position : -1; } } diff --git a/java/com/google/turbine/parse/Lexer.java b/java/com/google/turbine/parse/Lexer.java index 58b092f..2d8422a 100644 --- a/java/com/google/turbine/parse/Lexer.java +++ b/java/com/google/turbine/parse/Lexer.java @@ -16,6 +16,8 @@ package com.google.turbine.parse; +import com.google.turbine.diag.SourceFile; + /** A Java lexer. */ public interface Lexer { /** Returns the next token in the input stream, or {@code EOF}. */ @@ -26,4 +28,7 @@ public interface Lexer { /** Returns the current position in the input. */ int position(); + + /** Returns the source file for diagnostics. */ + SourceFile source(); } diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java index 9025ef7..83d5e34 100644 --- a/java/com/google/turbine/parse/Parser.java +++ b/java/com/google/turbine/parse/Parser.java @@ -23,6 +23,8 @@ import static com.google.turbine.tree.TurbineModifier.PUBLIC; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.turbine.diag.SourceFile; +import com.google.turbine.diag.TurbineError; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.model.TurbineTyKind; import com.google.turbine.tree.Tree; @@ -56,9 +58,14 @@ public class Parser { private final Lexer lexer; private Token token; + private int position; - public static CompUnit parse(String input) { - return new Parser(new StreamLexer(new UnicodeEscapePreprocessor(input))).compilationUnit(); + public static CompUnit parse(String source) { + return parse(new SourceFile(null, source)); + } + + public static CompUnit parse(SourceFile source) { + return new Parser(new StreamLexer(new UnicodeEscapePreprocessor(source))).compilationUnit(); } private Parser(Lexer lexer) { @@ -150,7 +157,7 @@ public class Parser { break; case EOF: // TODO(cushon): check for dangling modifiers? - return new CompUnit(pkg, imports.build(), decls.build(), null); + return new CompUnit(position, pkg, imports.build(), decls.build(), lexer.source()); case SEMI: // TODO(cushon): check for dangling modifiers? next(); @@ -163,6 +170,7 @@ public class Parser { private void next() { token = lexer.next(); + position = lexer.position(); } private TyDecl interfaceDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { @@ -185,6 +193,7 @@ public class Parser { ImmutableList<Tree> members = classMembers(); eat(Token.RBRACE); return new TyDecl( + position, access, annos, name, @@ -202,6 +211,7 @@ public class Parser { ImmutableList<Tree> members = classMembers(); eat(Token.RBRACE); return new TyDecl( + position, access, annos, name, @@ -227,6 +237,7 @@ public class Parser { ImmutableList.<Tree>builder().addAll(enumMembers(name)).addAll(classMembers()).build(); eat(Token.RBRACE); return new TyDecl( + position, access, annos, name, @@ -265,9 +276,11 @@ public class Parser { maybe(Token.COMMA); result.add( new VarDecl( + position, access, annos.build(), - new ClassTy(Optional.<ClassTy>absent(), enumName, ImmutableList.<Type>of()), + new ClassTy( + position, Optional.<ClassTy>absent(), enumName, ImmutableList.<Type>of()), name, Optional.<Expression>absent())); annos = ImmutableList.builder(); @@ -314,6 +327,7 @@ public class Parser { ImmutableList<Tree> members = classMembers(); eat(Token.RBRACE); return new TyDecl( + position, access, annos, name, @@ -451,7 +465,7 @@ public class Parser { switch (token) { case VOID: { - result = Tree.VoidTy.INSTANCE; + result = new Tree.VoidTy(position); next(); name = eatIdent(); return memberRest(access, annos, typaram, result, name); @@ -480,7 +494,9 @@ public class Parser { } case IDENT: { - result = new ClassTy(Optional.<ClassTy>absent(), ident, ImmutableList.<Type>of()); + result = + new ClassTy( + position, Optional.<ClassTy>absent(), ident, ImmutableList.<Type>of()); name = eatIdent(); return memberRest(access, annos, typaram, result, name); } @@ -494,26 +510,30 @@ public class Parser { } while (maybe(Token.LBRACK)); result = new ArrTy( - new ClassTy(Optional.<ClassTy>absent(), ident, ImmutableList.<Type>of()), + position, + new ClassTy( + position, Optional.<ClassTy>absent(), ident, ImmutableList.<Type>of()), dim); break; } case LT: { - Type ty = new ClassTy(Optional.<ClassTy>absent(), ident, tyargs()); + Type ty = new ClassTy(position, Optional.<ClassTy>absent(), ident, tyargs()); int dim = 0; while (maybe(Token.LBRACK)) { eat(Token.RBRACK); dim++; } if (dim > 0) { - ty = new ArrTy(ty, dim); + ty = new ArrTy(position, ty, dim); } result = ty; break; } case DOT: - result = new ClassTy(Optional.<ClassTy>absent(), ident, ImmutableList.<Type>of()); + result = + new ClassTy( + position, Optional.<ClassTy>absent(), ident, ImmutableList.<Type>of()); break; default: throw error(token); @@ -531,7 +551,7 @@ public class Parser { dim++; } if (dim > 0) { - result = new ArrTy(result, dim); + result = new ArrTy(position, result, dim); } } name = eatIdent(); @@ -544,7 +564,7 @@ public class Parser { case COMMA: { if (!typaram.isEmpty()) { - throw error(typaram); + throw error("%s", typaram); } return fieldRest(access, annos, result, name); } @@ -570,7 +590,7 @@ public class Parser { case COMMA: { if (!typaram.isEmpty()) { - throw error(typaram); + throw error("%s", typaram); } return fieldRest(access, annos, result, name); } @@ -600,7 +620,7 @@ public class Parser { if (next.token == Token.IDENT) { name = next.value; } else { - throw error(next); + throw error("%s", next); } } @@ -613,7 +633,7 @@ public class Parser { dim++; next = it.next(); if (next.token != Token.RBRACK) { - throw error(next); + throw error("%s", next); } if (it.hasNext()) { next = it.next(); @@ -622,11 +642,12 @@ public class Parser { newty = expandDims(ty, dim); } // TODO(cushon): skip more fields that are definitely non-const - Expression init = new ConstExpressionParser(new IteratorLexer(it)).expression(); + Expression init = + new ConstExpressionParser(new IteratorLexer(lexer.source(), it)).expression(); if (init != null && init.kind() == Tree.Kind.ARRAY_INIT) { init = null; } - result.add(new VarDecl(access, annos, newty, name, Optional.fromNullable(init))); + result.add(new VarDecl(position, access, annos, newty, name, Optional.fromNullable(init))); } eat(Token.SEMI); return result.build(); @@ -683,6 +704,7 @@ public class Parser { name = CTOR_NAME; } return new MethDecl( + position, access, annos, typaram, @@ -740,15 +762,15 @@ public class Parser { ty = parseDims(ty); } } - return new VarDecl(access, annos.build(), ty, name, Optional.<Expression>absent()); + return new VarDecl(position, access, annos.build(), ty, name, Optional.<Expression>absent()); } private Type expandDims(Type ty, int extra) { if (ty.kind() == Tree.Kind.ARR_TY) { Type.ArrTy aty = (Type.ArrTy) ty; - return new ArrTy(aty.elem(), aty.dim() + extra); + return new ArrTy(position, aty.elem(), aty.dim() + extra); } else if (extra > 0) { - return new ArrTy(ty, extra); + return new ArrTy(position, ty, extra); } else { return ty; } @@ -801,7 +823,7 @@ public class Parser { next(); bounds = tybounds(); } - acc.add(new TyParam(name, bounds)); + acc.add(new TyParam(position, name, bounds)); switch (token) { case COMMA: eat(Token.COMMA); @@ -829,13 +851,14 @@ public class Parser { } private ClassTy classty(ClassTy ty) { + int pos = position; do { String name = eatIdent(); ImmutableList<Type> tyargs = ImmutableList.of(); if (token == Token.LT) { tyargs = tyargs(); } - ty = new ClassTy(Optional.fromNullable(ty), name, tyargs); + ty = new ClassTy(pos, Optional.fromNullable(ty), name, tyargs); } while (maybe(Token.DOT)); return ty; } @@ -853,20 +876,20 @@ public class Parser { case EXTENDS: next(); Type upper = referenceType(); - acc.add(new WildTy(Optional.of(upper), Optional.<Type>absent())); + acc.add(new WildTy(position, Optional.of(upper), Optional.<Type>absent())); break; case SUPER: next(); Type lower = referenceType(); - acc.add(new WildTy(Optional.<Type>absent(), Optional.of(lower))); + acc.add(new WildTy(position, Optional.<Type>absent(), Optional.of(lower))); break; case COMMA: - acc.add(new WildTy(Optional.<Type>absent(), Optional.<Type>absent())); + acc.add(new WildTy(position, Optional.<Type>absent(), Optional.<Type>absent())); continue OUTER; case GT: case GTGT: case GTGTGT: - acc.add(new WildTy(Optional.<Type>absent(), Optional.<Type>absent())); + acc.add(new WildTy(position, Optional.<Type>absent(), Optional.<Type>absent())); break OUTER; default: throw error(token); @@ -912,35 +935,35 @@ public class Parser { break; case BOOLEAN: next(); - ty = new PrimTy(TurbineConstantTypeKind.BOOLEAN); + ty = new PrimTy(position, TurbineConstantTypeKind.BOOLEAN); break; case BYTE: next(); - ty = new PrimTy(TurbineConstantTypeKind.BYTE); + ty = new PrimTy(position, TurbineConstantTypeKind.BYTE); break; case SHORT: next(); - ty = new PrimTy(TurbineConstantTypeKind.SHORT); + ty = new PrimTy(position, TurbineConstantTypeKind.SHORT); break; case INT: next(); - ty = new PrimTy(TurbineConstantTypeKind.INT); + ty = new PrimTy(position, TurbineConstantTypeKind.INT); break; case LONG: next(); - ty = new PrimTy(TurbineConstantTypeKind.LONG); + ty = new PrimTy(position, TurbineConstantTypeKind.LONG); break; case CHAR: next(); - ty = new PrimTy(TurbineConstantTypeKind.CHAR); + ty = new PrimTy(position, TurbineConstantTypeKind.CHAR); break; case DOUBLE: next(); - ty = new PrimTy(TurbineConstantTypeKind.DOUBLE); + ty = new PrimTy(position, TurbineConstantTypeKind.DOUBLE); break; case FLOAT: next(); - ty = new PrimTy(TurbineConstantTypeKind.FLOAT); + ty = new PrimTy(position, TurbineConstantTypeKind.FLOAT); break; default: throw error(token); @@ -951,7 +974,7 @@ public class Parser { dim++; } if (dim > 0) { - ty = new ArrTy(ty, dim); + ty = new ArrTy(position, ty, dim); } return ty; } @@ -1035,11 +1058,11 @@ public class Parser { } } eat(Token.SEMI); - return new ImportDecl(type.build(), stat, wild); + return new ImportDecl(position, type.build(), stat, wild); } private PkgDecl packageDeclaration() { - PkgDecl result = new PkgDecl(qualIdent()); + PkgDecl result = new PkgDecl(position, qualIdent()); eat(Token.SEMI); return result; } @@ -1066,7 +1089,7 @@ public class Parser { eat(Token.RPAREN); } - return new Anno(name, args.build()); + return new Anno(position, name, args.build()); } private String eatIdent() { @@ -1090,7 +1113,7 @@ public class Parser { return false; } - ParseError error(Token token) { + TurbineError error(Token token) { String message; switch (token) { case IDENT: @@ -1100,10 +1123,10 @@ public class Parser { message = String.format("unexpected token %s", token); break; } - return new ParseError(lexer.position(), message); + return TurbineError.format(lexer.source(), lexer.position(), message, new Object[] {}); } - private ParseError error(Object message) { - return new ParseError(lexer.position(), String.valueOf(message)); + private TurbineError error(String message, Object... args) { + return TurbineError.format(lexer.source(), lexer.position(), message, args); } } diff --git a/java/com/google/turbine/parse/StreamLexer.java b/java/com/google/turbine/parse/StreamLexer.java index cc6d6c2..35dd81d 100644 --- a/java/com/google/turbine/parse/StreamLexer.java +++ b/java/com/google/turbine/parse/StreamLexer.java @@ -19,6 +19,8 @@ package com.google.turbine.parse; import static com.google.turbine.parse.UnicodeEscapePreprocessor.ASCII_SUB; import com.google.common.base.Verify; +import com.google.turbine.diag.SourceFile; +import com.google.turbine.diag.TurbineError; /** A {@link Lexer} that streams input from a {@link UnicodeEscapePreprocessor}. */ public class StreamLexer implements Lexer { @@ -28,6 +30,9 @@ public class StreamLexer implements Lexer { /** The current input character. */ private char ch; + /** The start position of the current token. */ + private int position; + /** The start position of the current numeric literal or identifier token. */ private int readFrom; @@ -65,15 +70,19 @@ public class StreamLexer implements Lexer { @Override public int position() { - // TODO(cushon): this is the position of the character after the last token that was lexed, - // keep track of start positions instead. - return reader.position(); + return position; + } + + @Override + public SourceFile source() { + return reader.source(); } @Override public Token next() { OUTER: while (true) { + position = reader.position(); switch (ch) { case '\r': case '\n': @@ -309,8 +318,7 @@ public class StreamLexer implements Lexer { eat(); return Token.ELLIPSIS; } else { - throw new ParseError( - reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } } case '0': @@ -345,7 +353,7 @@ public class StreamLexer implements Lexer { eat(); return Token.CHAR_LITERAL; } - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } case '"': @@ -379,7 +387,7 @@ public class StreamLexer implements Lexer { } // does not fall through default: - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } } } @@ -459,7 +467,7 @@ public class StreamLexer implements Lexer { } } default: - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } } @@ -566,7 +574,7 @@ public class StreamLexer implements Lexer { eat(); break; default: - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } OUTER: while (true) { @@ -601,7 +609,7 @@ public class StreamLexer implements Lexer { case '9': continue OUTER; default: - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } } case 'A': @@ -638,7 +646,7 @@ public class StreamLexer implements Lexer { if ('0' <= ch && ch <= '9') { eat(); } else { - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } OUTER: while (true) { @@ -650,7 +658,7 @@ public class StreamLexer implements Lexer { if ('0' <= ch && ch <= '9') { continue OUTER; } else { - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } case '0': case '1': @@ -689,7 +697,7 @@ public class StreamLexer implements Lexer { eat(); break; default: - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } OUTER: while (true) { @@ -703,7 +711,7 @@ public class StreamLexer implements Lexer { case '1': continue OUTER; default: - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } case '0': case '1': @@ -740,7 +748,7 @@ public class StreamLexer implements Lexer { eat(); break; default: - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } OUTER: while (true) { @@ -760,7 +768,7 @@ public class StreamLexer implements Lexer { case '7': continue OUTER; default: - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } case '0': case '1': @@ -934,7 +942,7 @@ public class StreamLexer implements Lexer { } case '/': // handled with comments - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); case '%': eat(); @@ -953,7 +961,7 @@ public class StreamLexer implements Lexer { return Token.XOR; } default: - throw new ParseError(reader.position(), String.format("unexpected input: %c", ch)); + throw error("unexpected input: %c", ch); } } @@ -1146,4 +1154,8 @@ public class StreamLexer implements Lexer { return Token.IDENT; } } + + private TurbineError error(String message, Object... args) { + return TurbineError.format(reader.source(), reader.position(), message, args); + } } diff --git a/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java b/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java index 92eef55..2bbd897 100644 --- a/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java +++ b/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java @@ -16,19 +16,24 @@ package com.google.turbine.parse; +import com.google.turbine.diag.SourceFile; +import com.google.turbine.diag.TurbineError; + /** Preprocesses Unicode escape characters in Java source code, as described in JLS ยง3.3. */ public class UnicodeEscapePreprocessor { public static final char ASCII_SUB = 0x1A; + private final SourceFile source; private final String input; private int idx = 0; private char ch; private boolean evenLeadingSlashes = true; - public UnicodeEscapePreprocessor(String input) { - this.input = input; + public UnicodeEscapePreprocessor(SourceFile source) { + this.source = source; + this.input = source.source(); } /** Returns the current position in the input. */ @@ -109,9 +114,9 @@ public class UnicodeEscapePreprocessor { case 'f': return ((d - 'a') + 10); case ASCII_SUB: - throw new ParseError(position(), "unexpected end of input"); + throw new AssertionError("unexpected end of input"); default: - throw new ParseError(position(), String.format("0x%x", (int) d)); + throw TurbineError.format(source, position(), "0x%x", (int) d); } } @@ -126,4 +131,8 @@ public class UnicodeEscapePreprocessor { ch = done() ? ASCII_SUB : input.charAt(idx); idx++; } + + public SourceFile source() { + return source; + } } diff --git a/java/com/google/turbine/tree/Tree.java b/java/com/google/turbine/tree/Tree.java index dceea19..beb4465 100644 --- a/java/com/google/turbine/tree/Tree.java +++ b/java/com/google/turbine/tree/Tree.java @@ -19,6 +19,7 @@ package com.google.turbine.tree; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.turbine.diag.SourceFile; import com.google.turbine.model.Const; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.model.TurbineTyKind; @@ -31,6 +32,16 @@ public abstract class Tree { public abstract <I, O> O accept(Visitor<I, O> visitor, I input); + private final int position; + + protected Tree(int position) { + this.position = position; + } + + public int position() { + return position; + } + @Override public String toString() { return Pretty.pretty(this); @@ -63,17 +74,26 @@ public abstract class Tree { } /** A type use. */ - public abstract static class Type extends Tree {} + public abstract static class Type extends Tree { + public Type(int position) { + super(position); + } + } /** An expression. */ - public abstract static class Expression extends Tree {} + public abstract static class Expression extends Tree { + public Expression(int position) { + super(position); + } + } /** A wildcard type, possibly with an upper or lower bound. */ public static class WildTy extends Type { private final Optional<Type> upper; private final Optional<Type> lower; - public WildTy(Optional<Type> upper, Optional<Type> lower) { + public WildTy(int position, Optional<Type> upper, Optional<Type> lower) { + super(position); this.upper = upper; this.lower = lower; } @@ -112,7 +132,8 @@ public abstract class Tree { private final Type elem; private final int dim; - public ArrTy(Type elem, int dim) { + public ArrTy(int position, Type elem, int dim) { + super(position); this.elem = elem; this.dim = dim; } @@ -147,7 +168,8 @@ public abstract class Tree { public static class PrimTy extends Type { private final TurbineConstantTypeKind tykind; - public PrimTy(TurbineConstantTypeKind tykind) { + public PrimTy(int position, TurbineConstantTypeKind tykind) { + super(position); this.tykind = tykind; } @@ -170,8 +192,6 @@ public abstract class Tree { /** The void type, used only for void-returning methods. */ public static class VoidTy extends Type { - public static final VoidTy INSTANCE = new VoidTy(); - @Override public Kind kind() { return Kind.VOID_TY; @@ -182,7 +202,9 @@ public abstract class Tree { return visitor.visitVoidTy(this, input); } - private VoidTy() {} + public VoidTy(int position) { + super(position); + } } /** A class, enum, interface, or annotation {@link Type}. */ @@ -191,7 +213,8 @@ public abstract class Tree { private final String name; private final ImmutableList<Type> tyargs; - public ClassTy(Optional<ClassTy> base, String name, ImmutableList<Type> tyargs) { + public ClassTy(int position, Optional<ClassTy> base, String name, ImmutableList<Type> tyargs) { + super(position); this.base = base; this.name = name; this.tyargs = tyargs; @@ -232,7 +255,8 @@ public abstract class Tree { private final TurbineConstantTypeKind tykind; private final Const value; - public Literal(TurbineConstantTypeKind tykind, Const value) { + public Literal(int position, TurbineConstantTypeKind tykind, Const value) { + super(position); this.tykind = tykind; this.value = value; } @@ -261,7 +285,8 @@ public abstract class Tree { private final Type ty; private final Expression expr; - public TypeCast(Type ty, Expression expr) { + public TypeCast(int position, Type ty, Expression expr) { + super(position); this.ty = ty; this.expr = expr; } @@ -290,7 +315,8 @@ public abstract class Tree { private final Expression expr; private final TurbineOperatorKind op; - public Unary(Expression expr, TurbineOperatorKind op) { + public Unary(int position, Expression expr, TurbineOperatorKind op) { + super(position); this.expr = expr; this.op = op; } @@ -320,7 +346,8 @@ public abstract class Tree { private final Expression rhs; private final TurbineOperatorKind op; - public Binary(Expression lhs, Expression rhs, TurbineOperatorKind op) { + public Binary(int position, Expression lhs, Expression rhs, TurbineOperatorKind op) { + super(position); this.lhs = lhs; this.rhs = rhs; this.op = op; @@ -353,7 +380,8 @@ public abstract class Tree { public static class ConstVarName extends Expression { private final ImmutableList<String> name; - public ConstVarName(ImmutableList<String> name) { + public ConstVarName(int position, ImmutableList<String> name) { + super(position); this.name = name; } @@ -377,7 +405,8 @@ public abstract class Tree { private final Type type; - public ClassLiteral(Type type) { + public ClassLiteral(int position, Type type) { + super(position); this.type = type; } @@ -401,7 +430,8 @@ public abstract class Tree { private final String name; private final Expression expr; - public Assign(String name, Expression expr) { + public Assign(int position, String name, Expression expr) { + super(position); this.name = name; this.expr = expr; } @@ -431,7 +461,8 @@ public abstract class Tree { private final Expression iftrue; private final Expression iffalse; - public Conditional(Expression cond, Expression iftrue, Expression iffalse) { + public Conditional(int position, Expression cond, Expression iftrue, Expression iffalse) { + super(position); this.cond = cond; this.iftrue = iftrue; this.iffalse = iffalse; @@ -464,7 +495,8 @@ public abstract class Tree { public static class ArrayInit extends Expression { private final ImmutableList<Expression> exprs; - public ArrayInit(ImmutableList<Expression> exprs) { + public ArrayInit(int position, ImmutableList<Expression> exprs) { + super(position); this.exprs = exprs; } @@ -488,17 +520,19 @@ public abstract class Tree { private final Optional<PkgDecl> pkg; private final ImmutableList<ImportDecl> imports; private final ImmutableList<TyDecl> decls; - private final String file; + private final SourceFile source; public CompUnit( + int position, Optional<PkgDecl> pkg, ImmutableList<ImportDecl> imports, ImmutableList<TyDecl> decls, - String file) { + SourceFile source) { + super(position); this.pkg = pkg; this.imports = imports; this.decls = decls; - this.file = file; + this.source = source; } @Override @@ -523,8 +557,8 @@ public abstract class Tree { return decls; } - public String file() { - return file; + public SourceFile source() { + return source; } } @@ -534,7 +568,8 @@ public abstract class Tree { private final boolean stat; private final boolean wild; - public ImportDecl(ImmutableList<String> type, boolean stat, boolean wild) { + public ImportDecl(int position, ImmutableList<String> type, boolean stat, boolean wild) { + super(position); this.type = type; this.stat = stat; this.wild = wild; @@ -574,11 +609,13 @@ public abstract class Tree { private final Optional<Expression> init; public VarDecl( + int position, Set<TurbineModifier> mods, ImmutableList<Anno> annos, Tree ty, String name, Optional<Expression> init) { + super(position); this.mods = ImmutableSet.copyOf(mods); this.annos = annos; this.ty = ty; @@ -629,6 +666,7 @@ public abstract class Tree { private final Optional<Tree> defaultValue; public MethDecl( + int position, Set<TurbineModifier> mods, ImmutableList<Anno> annos, ImmutableList<TyParam> typarams, @@ -637,6 +675,7 @@ public abstract class Tree { ImmutableList<VarDecl> params, ImmutableList<ClassTy> exntys, Optional<Tree> defaultValue) { + super(position); this.mods = ImmutableSet.copyOf(mods); this.annos = annos; this.typarams = typarams; @@ -695,7 +734,8 @@ public abstract class Tree { private final ImmutableList<String> name; private final ImmutableList<Expression> args; - public Anno(ImmutableList<String> name, ImmutableList<Expression> args) { + public Anno(int position, ImmutableList<String> name, ImmutableList<Expression> args) { + super(position); this.name = name; this.args = args; } @@ -727,7 +767,8 @@ public abstract class Tree { private final Anno value; - public AnnoExpr(Anno value) { + public AnnoExpr(int position, Anno value) { + super(position); this.value = value; } @@ -759,6 +800,7 @@ public abstract class Tree { private final TurbineTyKind tykind; public TyDecl( + int position, Set<TurbineModifier> mods, ImmutableList<Anno> annos, String name, @@ -767,6 +809,7 @@ public abstract class Tree { ImmutableList<ClassTy> impls, ImmutableList<Tree> members, TurbineTyKind tykind) { + super(position); this.mods = ImmutableSet.copyOf(mods); this.annos = annos; this.name = name; @@ -825,7 +868,8 @@ public abstract class Tree { private final String name; private final ImmutableList<Tree> bounds; - public TyParam(String name, ImmutableList<Tree> bounds) { + public TyParam(int position, String name, ImmutableList<Tree> bounds) { + super(position); this.name = name; this.bounds = bounds; } @@ -853,7 +897,8 @@ public abstract class Tree { public static class PkgDecl extends Tree { private final ImmutableList<String> name; - public PkgDecl(ImmutableList<String> name) { + public PkgDecl(int position, ImmutableList<String> name) { + super(position); this.name = name; } diff --git a/javatests/com/google/turbine/binder/BinderErrorTest.java b/javatests/com/google/turbine/binder/BinderErrorTest.java new file mode 100644 index 0000000..e77c75e --- /dev/null +++ b/javatests/com/google/turbine/binder/BinderErrorTest.java @@ -0,0 +1,136 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.binder; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.turbine.diag.TurbineError; +import com.google.turbine.parse.Parser; +import com.google.turbine.tree.Tree.CompUnit; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class BinderErrorTest { + + private static final ImmutableList<Path> BOOTCLASSPATH = + ImmutableList.of(Paths.get(System.getProperty("java.home")).resolve("lib/rt.jar")); + + @Parameters(name = "{index}: {0}") + public static Iterable<Object[]> parameters() { + String[][][] testCases = { + { + { + "package a;", // + "public class A extends NoSuch {", + "}", + }, + { + "<>: 2:23: symbol not found NoSuch", + "public class A extends NoSuch {", + " ^", + } + }, + { + { + "package a;", // + "class A {", + "}", + "class B extends A.NoSuch {", + "}", + }, + // TODO(cushon): we'd prefer the caret at NoSuch instead of A + { + "<>: 4:16: symbol not found NoSuch", // + "class B extends A.NoSuch {", + " ^", + } + }, + { + { + "package a;", // + "class A<T> {}", + "class B extends A<NoSuch> {}", + }, + { + "<>: 3:18: symbol not found NoSuch", + "class B extends A<NoSuch> {}", + " ^", + } + }, + { + { + "@interface Anno {}", // + "@Anno(foo=100, bar=200) class Test {}", + }, + { + "<>: 2:6: cannot resolve foo", // + "@Anno(foo=100, bar=200) class Test {}", + " ^", + }, + }, + { + { + "@interface Anno { int foo() default 0; }", // + "@Anno(foo=100, bar=200) class Test {}", + }, + { + "<>: 2:15: cannot resolve bar", // + "@Anno(foo=100, bar=200) class Test {}", + " ^", + }, + }, + }; + return Arrays.asList((Object[][]) testCases); + } + + final String[] source; + final String[] expected; + + public BinderErrorTest(String[] source, String[] expected) { + this.source = source; + this.expected = expected; + } + + @Test + public void test() throws Exception { + try { + Binder.bind(ImmutableList.of(parseLines(source)), Collections.emptyList(), BOOTCLASSPATH) + .units(); + fail(); + } catch (TurbineError e) { + assertThat(e.getMessage()).isEqualTo(lines(expected)); + } + } + + private static CompUnit parseLines(String... lines) { + return Parser.parse(lines(lines)); + } + + private static String lines(String... lines) { + return Joiner.on('\n').join(lines); + } +} diff --git a/javatests/com/google/turbine/lower/IntegrationTestSupport.java b/javatests/com/google/turbine/lower/IntegrationTestSupport.java index ab1cc07..bae7aa3 100644 --- a/javatests/com/google/turbine/lower/IntegrationTestSupport.java +++ b/javatests/com/google/turbine/lower/IntegrationTestSupport.java @@ -27,6 +27,7 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import com.google.turbine.binder.Binder; import com.google.turbine.bytecode.AsmUtils; +import com.google.turbine.diag.SourceFile; import com.google.turbine.parse.Parser; import com.google.turbine.tree.Tree; import com.sun.source.util.JavacTask; @@ -267,7 +268,13 @@ public class IntegrationTestSupport { static Map<String, byte[]> runTurbine( Map<String, String> input, ImmutableList<Path> classpath, Iterable<Path> bootclasspath) throws IOException { - List<Tree.CompUnit> units = input.values().stream().map(Parser::parse).collect(toList()); + List<Tree.CompUnit> units = + input + .entrySet() + .stream() + .map(e -> new SourceFile(e.getKey(), e.getValue())) + .map(Parser::parse) + .collect(toList()); Binder.BindingResult bound = Binder.bind(units, classpath, bootclasspath); return Lower.lowerAll(bound.units(), bound.classPathEnv()); diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java index a64574e..317cb1d 100644 --- a/javatests/com/google/turbine/lower/LowerTest.java +++ b/javatests/com/google/turbine/lower/LowerTest.java @@ -152,7 +152,8 @@ public class LowerTest { null, null, null, - ImmutableList.of()); + ImmutableList.of(), + null); SourceTypeBoundClass i = new SourceTypeBoundClass( @@ -171,7 +172,8 @@ public class LowerTest { null, null, null, - ImmutableList.of()); + ImmutableList.of(), + null); SimpleEnv.Builder<SourceTypeBoundClass> b = SimpleEnv.builder(); b.putIfAbsent(new ClassSymbol("test/Test"), c); diff --git a/javatests/com/google/turbine/parse/ExpressionParserTest.java b/javatests/com/google/turbine/parse/ExpressionParserTest.java index f9a2388..9e1f4ba 100644 --- a/javatests/com/google/turbine/parse/ExpressionParserTest.java +++ b/javatests/com/google/turbine/parse/ExpressionParserTest.java @@ -18,6 +18,7 @@ package com.google.turbine.parse; import static com.google.common.truth.Truth.assertThat; +import com.google.turbine.diag.SourceFile; import com.google.turbine.tree.Tree; import java.util.Arrays; import org.junit.Test; @@ -142,7 +143,8 @@ public class ExpressionParserTest { @Test public void test() { Tree.Expression expression = - new ConstExpressionParser(new StreamLexer(new UnicodeEscapePreprocessor(input))) + new ConstExpressionParser( + new StreamLexer(new UnicodeEscapePreprocessor(new SourceFile(null, input)))) .expression(); if (expected == null) { assertThat(expression).isNull(); diff --git a/javatests/com/google/turbine/parse/LexerTest.java b/javatests/com/google/turbine/parse/LexerTest.java index 3a6e56d..7f9201c 100644 --- a/javatests/com/google/turbine/parse/LexerTest.java +++ b/javatests/com/google/turbine/parse/LexerTest.java @@ -19,6 +19,7 @@ package com.google.turbine.parse; import static com.google.common.truth.Truth.assertThat; import com.google.common.escape.SourceCodeEscapers; +import com.google.turbine.diag.SourceFile; import java.util.ArrayList; import java.util.List; import org.junit.Test; @@ -333,7 +334,7 @@ public class LexerTest { } public static List<String> lex(String input) { - Lexer lexer = new StreamLexer(new UnicodeEscapePreprocessor(input)); + Lexer lexer = new StreamLexer(new UnicodeEscapePreprocessor(new SourceFile(null, input))); List<String> tokens = new ArrayList<>(); Token token; do { diff --git a/javatests/com/google/turbine/parse/ParseErrorTest.java b/javatests/com/google/turbine/parse/ParseErrorTest.java index 1f90b46..23211ce 100644 --- a/javatests/com/google/turbine/parse/ParseErrorTest.java +++ b/javatests/com/google/turbine/parse/ParseErrorTest.java @@ -20,7 +20,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.google.common.base.Joiner; -import com.google.turbine.diag.LineMap; +import com.google.turbine.diag.SourceFile; +import com.google.turbine.diag.TurbineError; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -33,7 +34,9 @@ public class ParseErrorTest { public void expression() { ConstExpressionParser parser = new ConstExpressionParser( - new StreamLexer(new UnicodeEscapePreprocessor(String.valueOf(Long.MAX_VALUE)))); + new StreamLexer( + new UnicodeEscapePreprocessor( + new SourceFile(null, String.valueOf(Long.MAX_VALUE))))); try { parser.expression(); fail("expected parsing to fail"); @@ -48,15 +51,14 @@ public class ParseErrorTest { try { Parser.parse(input); fail("expected parsing to fail"); - } catch (ParseError e) { - // TODO(cushon): the caret position is weird, see the TODO in StreamLexer#position - assertThat(LineMap.create(input).formatDiagnostic(e.position(), e.getMessage())) + } catch (TurbineError e) { + assertThat(e.getMessage()) .isEqualTo( Joiner.on('\n') .join( - "1:18: unexpected token VOID", + "<>: 1:14: unexpected token VOID", "public static void main(String[] args) {}", - " ^")); + " ^")); } } @@ -66,14 +68,14 @@ public class ParseErrorTest { try { Parser.parse(input); fail("expected parsing to fail"); - } catch (ParseError e) { - assertThat(LineMap.create(input).formatDiagnostic(e.position(), e.getMessage())) + } catch (TurbineError e) { + assertThat(e.getMessage()) .isEqualTo( Joiner.on('\n') .join( - "1:11: unexpected identifier 'clas'", // + "<>: 1:7: unexpected identifier 'clas'", // "public clas Test {}", - " ^")); + " ^")); } } } diff --git a/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java b/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java index dd3c0cc..ad88692 100644 --- a/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java +++ b/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java @@ -19,6 +19,7 @@ package com.google.turbine.parse; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import com.google.turbine.diag.SourceFile; import java.util.ArrayList; import java.util.List; import org.junit.Test; @@ -56,20 +57,20 @@ public class UnicodeEscapePreprocessorTest { try { readAll("\\u00"); fail(); - } catch (ParseError e) { + } catch (AssertionError e) { assertThat(e).hasMessage("unexpected end of input"); } try { readAll("\\u"); fail(); - } catch (ParseError e) { + } catch (AssertionError e) { assertThat(e).hasMessage("unexpected end of input"); } } private List<Character> readAll(String input) { - UnicodeEscapePreprocessor reader = new UnicodeEscapePreprocessor(input); + UnicodeEscapePreprocessor reader = new UnicodeEscapePreprocessor(new SourceFile(null, input)); List<Character> result = new ArrayList<>(); for (char ch = reader.next(); ch != UnicodeEscapePreprocessor.ASCII_SUB; ch = reader.next()) { result.add(ch); diff --git a/javatests/com/google/turbine/parse/VariableInitializerTest.java b/javatests/com/google/turbine/parse/VariableInitializerTest.java index bf97b9a..1738e67 100644 --- a/javatests/com/google/turbine/parse/VariableInitializerTest.java +++ b/javatests/com/google/turbine/parse/VariableInitializerTest.java @@ -19,6 +19,7 @@ package com.google.turbine.parse; import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Joiner; +import com.google.turbine.diag.SourceFile; import java.util.Arrays; import java.util.List; import org.junit.Test; @@ -130,7 +131,7 @@ public class VariableInitializerTest { @Test public void test() { - Lexer lexer = new StreamLexer(new UnicodeEscapePreprocessor(input)); + Lexer lexer = new StreamLexer(new UnicodeEscapePreprocessor(new SourceFile(null, input))); List<List<SavedToken>> initializers = new VariableInitializerParser(lexer.next(), lexer).parseInitializers(); assertThat(Joiner.on(", ").join(initializers)).isEqualTo(expected); |