aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcushon <cushon@google.com>2016-10-18 10:51:48 -0700
committerLiam Miller-Cushon <cushon@google.com>2016-10-18 11:35:31 -0700
commitb0445ae9c37a1fab07fb5f234653e8ea072bf9fc (patch)
tree6e8fe4a070d506e6ec300514a1e344986b63298e
parent75946a85fe655bafc006b96e4ddf01990d5f2f7c (diff)
downloadturbine-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
-rw-r--r--java/com/google/turbine/binder/Binder.java3
-rw-r--r--java/com/google/turbine/binder/CanonicalTypeBinder.java3
-rw-r--r--java/com/google/turbine/binder/ConstBinder.java3
-rw-r--r--java/com/google/turbine/binder/ConstEvaluator.java14
-rw-r--r--java/com/google/turbine/binder/HierarchyBinder.java45
-rw-r--r--java/com/google/turbine/binder/TypeBinder.java179
-rw-r--r--java/com/google/turbine/binder/bound/PackageSourceBoundClass.java13
-rw-r--r--java/com/google/turbine/binder/bound/SourceHeaderBoundClass.java6
-rw-r--r--java/com/google/turbine/binder/bound/SourceTypeBoundClass.java16
-rw-r--r--java/com/google/turbine/diag/LineMap.java25
-rw-r--r--java/com/google/turbine/diag/SourceFile.java (renamed from java/com/google/turbine/parse/ParseError.java)28
-rw-r--r--java/com/google/turbine/diag/TurbineError.java58
-rw-r--r--java/com/google/turbine/parse/ConstExpressionParser.java53
-rw-r--r--java/com/google/turbine/parse/IteratorLexer.java13
-rw-r--r--java/com/google/turbine/parse/Lexer.java5
-rw-r--r--java/com/google/turbine/parse/Parser.java107
-rw-r--r--java/com/google/turbine/parse/StreamLexer.java48
-rw-r--r--java/com/google/turbine/parse/UnicodeEscapePreprocessor.java17
-rw-r--r--java/com/google/turbine/tree/Tree.java101
-rw-r--r--javatests/com/google/turbine/binder/BinderErrorTest.java136
-rw-r--r--javatests/com/google/turbine/lower/IntegrationTestSupport.java9
-rw-r--r--javatests/com/google/turbine/lower/LowerTest.java6
-rw-r--r--javatests/com/google/turbine/parse/ExpressionParserTest.java4
-rw-r--r--javatests/com/google/turbine/parse/LexerTest.java3
-rw-r--r--javatests/com/google/turbine/parse/ParseErrorTest.java24
-rw-r--r--javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java7
-rw-r--r--javatests/com/google/turbine/parse/VariableInitializerTest.java3
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);