diff options
author | cushon <cushon@google.com> | 2018-04-09 14:12:12 -0700 |
---|---|---|
committer | Liam Miller-Cushon <cushon@google.com> | 2018-04-10 23:17:25 -0700 |
commit | db00737c711b291adf423c503284ce1dc01a13a6 (patch) | |
tree | 4f1e76610bca1ac1d1d38aef158c7247a820b261 | |
parent | be7c38da06932677b2de0864f75444ac8d3d8e7c (diff) | |
download | turbine-db00737c711b291adf423c503284ce1dc01a13a6.tar.gz |
Improve diagnostics for missing enclosing classes
MOE_MIGRATED_REVID=192185656
-rw-r--r-- | java/com/google/turbine/binder/Binder.java | 13 | ||||
-rw-r--r-- | java/com/google/turbine/binder/CanonicalTypeBinder.java | 55 | ||||
-rw-r--r-- | java/com/google/turbine/binder/ModuleBinder.java | 10 | ||||
-rw-r--r-- | java/com/google/turbine/binder/bound/SourceModuleInfo.java | 52 | ||||
-rw-r--r-- | java/com/google/turbine/diag/TurbineError.java | 16 | ||||
-rw-r--r-- | java/com/google/turbine/lower/Lower.java | 38 | ||||
-rw-r--r-- | java/com/google/turbine/types/Canonicalize.java | 56 | ||||
-rw-r--r-- | javatests/com/google/turbine/lower/LowerTest.java | 118 |
8 files changed, 293 insertions, 65 deletions
diff --git a/java/com/google/turbine/binder/Binder.java b/java/com/google/turbine/binder/Binder.java index cbafdef..d4b151f 100644 --- a/java/com/google/turbine/binder/Binder.java +++ b/java/com/google/turbine/binder/Binder.java @@ -31,6 +31,7 @@ import com.google.turbine.binder.bound.PackageSourceBoundClass; import com.google.turbine.binder.bound.PackageSourceBoundModule; import com.google.turbine.binder.bound.SourceBoundClass; import com.google.turbine.binder.bound.SourceHeaderBoundClass; +import com.google.turbine.binder.bound.SourceModuleInfo; import com.google.turbine.binder.bound.SourceTypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; @@ -112,7 +113,7 @@ public class Binder { canonicalizeTypes( syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv)); - ImmutableList<ModuleInfo> boundModules = + ImmutableList<SourceModuleInfo> boundModules = bindModules( modules, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv), @@ -244,7 +245,7 @@ public class Binder { return builder.build(); } - private static ImmutableList<ModuleInfo> bindModules( + private static ImmutableList<SourceModuleInfo> bindModules( SimpleEnv<ModuleSymbol, PackageSourceBoundModule> modules, CompoundEnv<ClassSymbol, TypeBoundClass> env, CompoundEnv<ModuleSymbol, ModuleInfo> moduleEnv, @@ -274,7 +275,7 @@ public class Binder { return null; } }); - ImmutableList.Builder<ModuleInfo> bound = ImmutableList.builder(); + ImmutableList.Builder<SourceModuleInfo> bound = ImmutableList.builder(); for (PackageSourceBoundModule module : modules.asMap().values()) { bound.add(ModuleBinder.bind(module, env, moduleEnv, moduleVersion)); } @@ -378,12 +379,12 @@ public class Binder { /** The result of binding: bound nodes for sources in the compilation, and the classpath. */ public static class BindingResult { private final ImmutableMap<ClassSymbol, SourceTypeBoundClass> units; - private final ImmutableList<ModuleInfo> modules; + private final ImmutableList<SourceModuleInfo> modules; private final CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv; public BindingResult( ImmutableMap<ClassSymbol, SourceTypeBoundClass> units, - ImmutableList<ModuleInfo> modules, + ImmutableList<SourceModuleInfo> modules, CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv) { this.units = units; this.modules = modules; @@ -395,7 +396,7 @@ public class Binder { return units; } - public ImmutableList<ModuleInfo> modules() { + public ImmutableList<SourceModuleInfo> modules() { return modules; } diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java index 2ff56a3..20db0d7 100644 --- a/java/com/google/turbine/binder/CanonicalTypeBinder.java +++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java @@ -27,6 +27,7 @@ import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.TyVarSymbol; +import com.google.turbine.diag.SourceFile; import com.google.turbine.type.Type; import com.google.turbine.type.Type.ClassTy; import com.google.turbine.types.Canonicalize; @@ -41,16 +42,17 @@ public class CanonicalTypeBinder { ClassSymbol sym, SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> env) { ClassTy superClassType = null; if (base.superClassType() != null) { - superClassType = Canonicalize.canonicalizeClassTy(env, base.owner(), base.superClassType()); + superClassType = + Canonicalize.canonicalizeClassTy(base.source(), env, base.owner(), base.superClassType()); } ImmutableList.Builder<ClassTy> interfaceTypes = ImmutableList.builder(); for (ClassTy i : base.interfaceTypes()) { - interfaceTypes.add(Canonicalize.canonicalizeClassTy(env, base.owner(), i)); + interfaceTypes.add(Canonicalize.canonicalizeClassTy(base.source(), env, base.owner(), i)); } ImmutableMap<TyVarSymbol, TyVarInfo> typParamTypes = - typeParameters(env, sym, base.typeParameterTypes()); - ImmutableList<MethodInfo> methods = methods(env, sym, base.methods()); - ImmutableList<FieldInfo> fields = fields(env, sym, base.fields()); + typeParameters(base.source(), env, sym, base.typeParameterTypes()); + ImmutableList<MethodInfo> methods = methods(base.source(), env, sym, base.methods()); + ImmutableList<FieldInfo> fields = fields(base.source(), env, sym, base.fields()); return new SourceTypeBoundClass( interfaceTypes.build(), superClassType, @@ -71,13 +73,16 @@ public class CanonicalTypeBinder { } private static ImmutableList<FieldInfo> fields( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, ImmutableList<FieldInfo> fields) { + SourceFile source, + Env<ClassSymbol, TypeBoundClass> env, + ClassSymbol sym, + ImmutableList<FieldInfo> fields) { ImmutableList.Builder<FieldInfo> result = ImmutableList.builder(); for (FieldInfo base : fields) { result.add( new FieldInfo( base.sym(), - Canonicalize.canonicalize(env, sym, base.type()), + Canonicalize.canonicalize(source, env, sym, base.type()), base.access(), base.annotations(), base.decl(), @@ -87,16 +92,19 @@ public class CanonicalTypeBinder { } private static ImmutableList<MethodInfo> methods( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, ImmutableList<MethodInfo> methods) { + SourceFile source, + Env<ClassSymbol, TypeBoundClass> env, + ClassSymbol sym, + ImmutableList<MethodInfo> methods) { ImmutableList.Builder<MethodInfo> result = ImmutableList.builder(); for (MethodInfo base : methods) { - ImmutableMap<TyVarSymbol, TyVarInfo> tps = typeParameters(env, sym, base.tyParams()); - Type ret = Canonicalize.canonicalize(env, sym, base.returnType()); + ImmutableMap<TyVarSymbol, TyVarInfo> tps = typeParameters(source, env, sym, base.tyParams()); + Type ret = Canonicalize.canonicalize(source, env, sym, base.returnType()); ImmutableList.Builder<ParamInfo> parameters = ImmutableList.builder(); for (ParamInfo parameter : base.parameters()) { - parameters.add(param(env, sym, parameter)); + parameters.add(param(source, env, sym, parameter)); } - ImmutableList<Type> exceptions = canonicalizeList(env, sym, base.exceptions()); + ImmutableList<Type> exceptions = canonicalizeList(source, env, sym, base.exceptions()); result.add( new MethodInfo( base.sym(), @@ -108,40 +116,47 @@ public class CanonicalTypeBinder { base.defaultValue(), base.decl(), base.annotations(), - base.receiver() != null ? param(env, sym, base.receiver()) : null)); + base.receiver() != null ? param(source, env, sym, base.receiver()) : null)); } return result.build(); } private static ParamInfo param( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, ParamInfo base) { + SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, ParamInfo base) { return new ParamInfo( - Canonicalize.canonicalize(env, sym, base.type()), + Canonicalize.canonicalize(source, env, sym, base.type()), base.name(), base.annotations(), base.access()); } private static ImmutableMap<TyVarSymbol, TyVarInfo> typeParameters( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, Map<TyVarSymbol, TyVarInfo> tps) { + SourceFile source, + Env<ClassSymbol, TypeBoundClass> env, + ClassSymbol sym, + Map<TyVarSymbol, TyVarInfo> tps) { ImmutableMap.Builder<TyVarSymbol, TyVarInfo> result = ImmutableMap.builder(); for (Map.Entry<TyVarSymbol, TyVarInfo> e : tps.entrySet()) { TyVarInfo info = e.getValue(); Type superClassBound = null; if (info.superClassBound() != null) { - superClassBound = Canonicalize.canonicalize(env, sym, info.superClassBound()); + superClassBound = Canonicalize.canonicalize(source, env, sym, info.superClassBound()); } - ImmutableList<Type> interfaceBounds = canonicalizeList(env, sym, info.interfaceBounds()); + ImmutableList<Type> interfaceBounds = + canonicalizeList(source, env, sym, info.interfaceBounds()); result.put(e.getKey(), new TyVarInfo(superClassBound, interfaceBounds, info.annotations())); } return result.build(); } private static ImmutableList<Type> canonicalizeList( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, ImmutableList<Type> types) { + SourceFile source, + Env<ClassSymbol, TypeBoundClass> env, + ClassSymbol sym, + ImmutableList<Type> types) { ImmutableList.Builder<Type> result = ImmutableList.builder(); for (Type type : types) { - result.add(Canonicalize.canonicalize(env, sym, type)); + result.add(Canonicalize.canonicalize(source, env, sym, type)); } return result.build(); } diff --git a/java/com/google/turbine/binder/ModuleBinder.java b/java/com/google/turbine/binder/ModuleBinder.java index 23c9624..2cf3d53 100644 --- a/java/com/google/turbine/binder/ModuleBinder.java +++ b/java/com/google/turbine/binder/ModuleBinder.java @@ -29,6 +29,7 @@ import com.google.turbine.binder.bound.ModuleInfo.ProvideInfo; import com.google.turbine.binder.bound.ModuleInfo.RequireInfo; import com.google.turbine.binder.bound.ModuleInfo.UseInfo; import com.google.turbine.binder.bound.PackageSourceBoundModule; +import com.google.turbine.binder.bound.SourceModuleInfo; import com.google.turbine.binder.bound.TypeBoundClass; import com.google.turbine.binder.env.CompoundEnv; import com.google.turbine.binder.env.Env; @@ -54,7 +55,7 @@ import com.google.turbine.type.AnnoInfo; /** Binding pass for modules. */ public class ModuleBinder { - public static ModuleInfo bind( + public static SourceModuleInfo bind( PackageSourceBoundModule module, CompoundEnv<ClassSymbol, TypeBoundClass> env, Env<ModuleSymbol, ModuleInfo> moduleEnv, @@ -80,7 +81,7 @@ public class ModuleBinder { this.scope = module.scope().toScope(Resolve.resolveFunction(env, /* origin= */ null)); } - private ModuleInfo bind() { + private SourceModuleInfo bind() { // bind annotations; constant fields are already bound ConstEvaluator constEvaluator = new ConstEvaluator( @@ -146,7 +147,7 @@ public class ModuleBinder { .addAll(requires.build()); } - return new ModuleInfo( + return new SourceModuleInfo( module.module().moduleName(), moduleVersion.orNull(), flags, @@ -155,7 +156,8 @@ public class ModuleBinder { exports.build(), opens.build(), uses.build(), - provides.build()); + provides.build(), + module.source()); } private RequireInfo bindRequires(ModRequires directive) { diff --git a/java/com/google/turbine/binder/bound/SourceModuleInfo.java b/java/com/google/turbine/binder/bound/SourceModuleInfo.java new file mode 100644 index 0000000..9ecca8b --- /dev/null +++ b/java/com/google/turbine/binder/bound/SourceModuleInfo.java @@ -0,0 +1,52 @@ +/* + * Copyright 2018 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.bound; + +import android.support.annotation.Nullable; +import com.google.common.collect.ImmutableList; +import com.google.turbine.binder.bound.ModuleInfo.ExportInfo; +import com.google.turbine.binder.bound.ModuleInfo.OpenInfo; +import com.google.turbine.binder.bound.ModuleInfo.ProvideInfo; +import com.google.turbine.binder.bound.ModuleInfo.RequireInfo; +import com.google.turbine.binder.bound.ModuleInfo.UseInfo; +import com.google.turbine.diag.SourceFile; +import com.google.turbine.type.AnnoInfo; + +/** A {@link ModuleInfo} that corresponds to a source file being compiled. */ +public class SourceModuleInfo extends ModuleInfo { + + private final SourceFile source; + + public SourceModuleInfo( + String name, + @Nullable String version, + int flags, + ImmutableList<AnnoInfo> annos, + ImmutableList<RequireInfo> requires, + ImmutableList<ExportInfo> exports, + ImmutableList<OpenInfo> opens, + ImmutableList<UseInfo> uses, + ImmutableList<ProvideInfo> provides, + SourceFile source) { + super(name, version, flags, annos, requires, exports, opens, uses, provides); + this.source = source; + } + + public SourceFile source() { + return source; + } +} diff --git a/java/com/google/turbine/diag/TurbineError.java b/java/com/google/turbine/diag/TurbineError.java index b8d6b65..8c75345 100644 --- a/java/com/google/turbine/diag/TurbineError.java +++ b/java/com/google/turbine/diag/TurbineError.java @@ -33,6 +33,7 @@ public class TurbineError extends Error { INVALID_LITERAL("invalid literal: %s"), UNEXPECTED_TYPE_PARAMETER("unexpected type parameter %s"), SYMBOL_NOT_FOUND("symbol not found %s"), + CLASS_FILE_NOT_FOUND("could not locate class file for %s"), TYPE_PARAMETER_QUALIFIER("type parameter used as type qualifier"), UNEXPECTED_TOKEN("unexpected token: %s"), INVALID_ANNOTATION_ARGUMENT("invalid annotation argument"), @@ -58,7 +59,20 @@ public class TurbineError extends Error { /** * Formats a diagnostic. * - * @param source the source file + * @param source the current source file + * @param kind the error kind + * @param args format args + */ + public static TurbineError format(SourceFile source, ErrorKind kind, Object... args) { + String path = firstNonNull(source.path(), "<>"); + String message = kind.format(args); + String diagnostic = path + ": error: " + message.trim() + System.lineSeparator(); + return new TurbineError(kind, diagnostic); + } + + /** + * Formats a diagnostic. + * * @param position the diagnostic position * @param kind the error kind * @param args format args diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java index d8b464b..04f352e 100644 --- a/java/com/google/turbine/lower/Lower.java +++ b/java/com/google/turbine/lower/Lower.java @@ -27,12 +27,12 @@ import com.google.common.collect.ImmutableSet; import com.google.turbine.binder.bound.AnnotationValue; import com.google.turbine.binder.bound.ClassValue; import com.google.turbine.binder.bound.EnumConstantValue; -import com.google.turbine.binder.bound.ModuleInfo; import com.google.turbine.binder.bound.ModuleInfo.ExportInfo; import com.google.turbine.binder.bound.ModuleInfo.OpenInfo; import com.google.turbine.binder.bound.ModuleInfo.ProvideInfo; import com.google.turbine.binder.bound.ModuleInfo.RequireInfo; import com.google.turbine.binder.bound.ModuleInfo.UseInfo; +import com.google.turbine.binder.bound.SourceModuleInfo; import com.google.turbine.binder.bound.SourceTypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; @@ -60,6 +60,9 @@ import com.google.turbine.bytecode.sig.Sig; import com.google.turbine.bytecode.sig.Sig.MethodSig; import com.google.turbine.bytecode.sig.Sig.TySig; import com.google.turbine.bytecode.sig.SigWriter; +import com.google.turbine.diag.SourceFile; +import com.google.turbine.diag.TurbineError; +import com.google.turbine.diag.TurbineError.ErrorKind; import com.google.turbine.model.Const; import com.google.turbine.model.TurbineFlag; import com.google.turbine.model.TurbineVisibility; @@ -109,7 +112,7 @@ public class Lower { /** Lowers all given classes to bytecode. */ public static Lowered lowerAll( ImmutableMap<ClassSymbol, SourceTypeBoundClass> units, - ImmutableList<ModuleInfo> modules, + ImmutableList<SourceModuleInfo> modules, Env<ClassSymbol, BytecodeBoundClass> classpath) { CompoundEnv<ClassSymbol, TypeBoundClass> env = CompoundEnv.<ClassSymbol, TypeBoundClass>of(classpath).append(new SimpleEnv<>(units)); @@ -124,7 +127,7 @@ public class Lower { } else { // multi-module mode: the output module-info.class are in a directory corresponding to their // package - for (ModuleInfo module : modules) { + for (SourceModuleInfo module : modules) { result.put(module.name().replace('.', '/') + "/module-info", lower(module, env, symbols)); } } @@ -141,7 +144,9 @@ public class Lower { } private static byte[] lower( - ModuleInfo module, CompoundEnv<ClassSymbol, TypeBoundClass> env, Set<ClassSymbol> symbols) { + SourceModuleInfo module, + CompoundEnv<ClassSymbol, TypeBoundClass> env, + Set<ClassSymbol> symbols) { return new Lower(env).lower(module, symbols); } @@ -152,7 +157,7 @@ public class Lower { this.env = env; } - private byte[] lower(ModuleInfo module, Set<ClassSymbol> symbols) { + private byte[] lower(SourceModuleInfo module, Set<ClassSymbol> symbols) { String name = "module-info"; ImmutableList<AnnotationInfo> annotations = lowerAnnotations(module.annos()); ClassFile.ModuleInfo moduleInfo = lowerModule(module); @@ -161,7 +166,7 @@ public class Lower { { Set<ClassSymbol> all = new LinkedHashSet<>(); for (ClassSymbol sym : sig.classes) { - addEnclosing(env, all, sym); + addEnclosing(module.source(), env, all, sym); } for (ClassSymbol innerSym : all) { innerClasses.add(innerClass(env, innerSym)); @@ -185,7 +190,7 @@ public class Lower { return ClassWriter.writeClass(classfile); } - private ClassFile.ModuleInfo lowerModule(ModuleInfo module) { + private ClassFile.ModuleInfo lowerModule(SourceModuleInfo module) { ImmutableList.Builder<ClassFile.ModuleInfo.RequireInfo> requires = ImmutableList.builder(); for (RequireInfo require : module.requires()) { requires.add( @@ -258,7 +263,7 @@ public class Lower { ImmutableList<AnnotationInfo> annotations = lowerAnnotations(info.annotations()); - ImmutableList<ClassFile.InnerClass> inners = collectInnerClasses(sym, info); + ImmutableList<ClassFile.InnerClass> inners = collectInnerClasses(info.source(), sym, info); ImmutableList<TypeAnnotationInfo> typeAnnotations = classTypeAnnotations(info); @@ -382,14 +387,14 @@ public class Lower { /** Creates inner class attributes for all referenced inner classes. */ private ImmutableList<ClassFile.InnerClass> collectInnerClasses( - ClassSymbol origin, SourceTypeBoundClass info) { + SourceFile source, ClassSymbol origin, SourceTypeBoundClass info) { Set<ClassSymbol> all = new LinkedHashSet<>(); - addEnclosing(env, all, origin); + addEnclosing(source, env, all, origin); for (ClassSymbol sym : info.children().values()) { - addEnclosing(env, all, sym); + addEnclosing(source, env, all, sym); } for (ClassSymbol sym : sig.classes) { - addEnclosing(env, all, sym); + addEnclosing(source, env, all, sym); } ImmutableList.Builder<ClassFile.InnerClass> inners = ImmutableList.builder(); for (ClassSymbol innerSym : all) { @@ -406,14 +411,17 @@ public class Lower { * classes' entries. */ private void addEnclosing( - Env<ClassSymbol, TypeBoundClass> env, Set<ClassSymbol> all, ClassSymbol sym) { + SourceFile source, + Env<ClassSymbol, TypeBoundClass> env, + Set<ClassSymbol> all, + ClassSymbol sym) { TypeBoundClass info = env.get(sym); if (info == null) { - throw new AssertionError(sym); + throw TurbineError.format(source, ErrorKind.CLASS_FILE_NOT_FOUND, sym); } ClassSymbol owner = env.get(sym).owner(); if (owner != null) { - addEnclosing(env, all, owner); + addEnclosing(source, env, all, owner); all.add(sym); } } diff --git a/java/com/google/turbine/types/Canonicalize.java b/java/com/google/turbine/types/Canonicalize.java index fc5d907..98adf57 100644 --- a/java/com/google/turbine/types/Canonicalize.java +++ b/java/com/google/turbine/types/Canonicalize.java @@ -22,6 +22,9 @@ import com.google.turbine.binder.bound.TypeBoundClass; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.TyVarSymbol; +import com.google.turbine.diag.SourceFile; +import com.google.turbine.diag.TurbineError; +import com.google.turbine.diag.TurbineError.ErrorKind; import com.google.turbine.model.TurbineFlag; import com.google.turbine.type.Type; import com.google.turbine.type.Type.ArrayTy; @@ -63,35 +66,38 @@ public class Canonicalize { /** Canonicalizes the given type. */ public static Type canonicalize( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, Type type) { + SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, Type type) { switch (type.tyKind()) { case PRIM_TY: case VOID_TY: case TY_VAR: return type; case WILD_TY: - return canonicalizeWildTy(env, base, (WildTy) type); + return canonicalizeWildTy(source, env, base, (WildTy) type); case ARRAY_TY: { Type.ArrayTy arrayTy = (Type.ArrayTy) type; - return new Type.ArrayTy(canonicalize(env, base, arrayTy.elementType()), arrayTy.annos()); + return new Type.ArrayTy( + canonicalize(source, env, base, arrayTy.elementType()), arrayTy.annos()); } case CLASS_TY: - return canonicalizeClassTy(env, base, (ClassTy) type); + return canonicalizeClassTy(source, env, base, (ClassTy) type); default: throw new AssertionError(type.tyKind()); } } /** Canonicalize a qualified class type, excluding type arguments. */ - private static ClassTy canon(Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, ClassTy ty) { + private static ClassTy canon( + SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, ClassTy ty) { if (isRaw(env, ty)) { return Erasure.eraseClassTy(ty); } // if the first name is a simple name resolved inside a nested class, add explicit qualifiers // for the enclosing declarations Iterator<ClassTy.SimpleClassTy> it = ty.classes.iterator(); - Collection<ClassTy.SimpleClassTy> lexicalBase = lexicalBase(env, ty.classes.get(0).sym(), base); + Collection<ClassTy.SimpleClassTy> lexicalBase = + lexicalBase(source, env, ty.classes.get(0).sym(), base); ClassTy canon = !lexicalBase.isEmpty() ? new ClassTy(lexicalBase) @@ -99,7 +105,7 @@ public class Canonicalize { // canonicalize each additional simple name that appeared in source while (it.hasNext()) { - canon = canonOne(env, canon, it.next()); + canon = canonOne(source, env, canon, it.next()); } return canon; } @@ -122,7 +128,10 @@ public class Canonicalize { /** Given a base symbol to canonicalize, find any implicit enclosing instances. */ private static Collection<ClassTy.SimpleClassTy> lexicalBase( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol first, ClassSymbol owner) { + SourceFile source, + Env<ClassSymbol, TypeBoundClass> env, + ClassSymbol first, + ClassSymbol owner) { if ((env.get(first).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { return Collections.emptyList(); } @@ -137,7 +146,11 @@ public class Canonicalize { if ((env.get(owner).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { break; } - canonOwner = env.get(canonOwner).owner(); + TypeBoundClass info = env.get(canonOwner); + if (info == null) { + throw TurbineError.format(source, ErrorKind.CLASS_FILE_NOT_FOUND, canonOwner); + } + canonOwner = info.owner(); } return result; } @@ -167,7 +180,7 @@ public class Canonicalize { * result. */ private static ClassTy canonOne( - Env<ClassSymbol, TypeBoundClass> env, ClassTy base, ClassTy.SimpleClassTy ty) { + SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassTy base, SimpleClassTy ty) { // if the class is static, it has a trivial canonical qualifier with no type arguments if ((env.get(ty.sym()).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { return new ClassTy(Collections.singletonList(ty)); @@ -196,7 +209,7 @@ public class Canonicalize { break; } TypeBoundClass info = env.get(curr.sym()); - curr = canon(env, info.owner(), info.superClassType()); + curr = canon(source, env, info.owner(), info.superClassType()); } simples.add(ty); return new ClassTy(simples.build()); @@ -315,36 +328,41 @@ public class Canonicalize { } public static ClassTy canonicalizeClassTy( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, ClassTy ty) { + SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, ClassTy ty) { // canonicalize type arguments first ImmutableList.Builder<ClassTy.SimpleClassTy> args = ImmutableList.builder(); for (ClassTy.SimpleClassTy s : ty.classes) { - args.add(new ClassTy.SimpleClassTy(s.sym(), canonicalize(s.targs(), base, env), s.annos())); + args.add( + new ClassTy.SimpleClassTy( + s.sym(), canonicalize(source, s.targs(), base, env), s.annos())); } ty = new ClassTy(args.build()); - return canon(env, base, ty); + return canon(source, env, base, ty); } private static ImmutableList<Type> canonicalize( - ImmutableList<Type> targs, ClassSymbol base, Env<ClassSymbol, TypeBoundClass> env) { + SourceFile source, + ImmutableList<Type> targs, + ClassSymbol base, + Env<ClassSymbol, TypeBoundClass> env) { ImmutableList.Builder<Type> result = ImmutableList.builder(); for (Type a : targs) { - result.add(canonicalize(env, base, a)); + result.add(canonicalize(source, env, base, a)); } return result.build(); } private static Type canonicalizeWildTy( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, WildTy type) { + SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, WildTy type) { switch (type.boundKind()) { case NONE: return type; case LOWER: return new Type.WildLowerBoundedTy( - canonicalize(env, base, type.bound()), type.annotations()); + canonicalize(source, env, base, type.bound()), type.annotations()); case UPPER: return new Type.WildUpperBoundedTy( - canonicalize(env, base, type.bound()), type.annotations()); + canonicalize(source, env, base, type.bound()), type.annotations()); default: throw new AssertionError(type.boundKind()); } diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java index 6409d4d..86d4ce4 100644 --- a/javatests/com/google/turbine/lower/LowerTest.java +++ b/javatests/com/google/turbine/lower/LowerTest.java @@ -19,6 +19,7 @@ package com.google.turbine.lower; import static com.google.common.truth.Truth.assertThat; import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.fail; import com.google.common.base.Joiner; import com.google.common.base.Optional; @@ -36,6 +37,7 @@ import com.google.turbine.binder.sym.MethodSymbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.bytecode.ByteReader; import com.google.turbine.bytecode.ConstantPoolReader; +import com.google.turbine.diag.TurbineError; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.model.TurbineFlag; import com.google.turbine.model.TurbineTyKind; @@ -480,6 +482,122 @@ public class LowerTest { .isEqualTo(IntegrationTestSupport.dump(IntegrationTestSupport.canonicalize(expected))); } + @Test + public void missingOuter() throws Exception { + + Map<String, byte[]> lib = + IntegrationTestSupport.runJavac( + ImmutableMap.of( + "A.java", + lines( + "interface A {", // + " interface M {", + " interface I {}", + " } ", + "}"), + "B.java", + lines( + "interface B extends A {", + " interface BM extends M {", + " interface BI extends I {}", + " }", + "}")), + ImmutableList.of()); + + Path libJar = temporaryFolder.newFile("lib.jar").toPath(); + try (OutputStream os = Files.newOutputStream(libJar); + JarOutputStream jos = new JarOutputStream(os)) { + jos.putNextEntry(new JarEntry("A$M.class")); + jos.write(lib.get("A$M")); + jos.putNextEntry(new JarEntry("A$M$I.class")); + jos.write(lib.get("A$M$I")); + jos.putNextEntry(new JarEntry("B.class")); + jos.write(lib.get("B")); + jos.putNextEntry(new JarEntry("B$BM.class")); + jos.write(lib.get("B$BM")); + jos.putNextEntry(new JarEntry("B$BM$BI.class")); + jos.write(lib.get("B$BM$BI")); + } + + ImmutableMap<String, String> sources = + ImmutableMap.<String, String>builder() + .put( + "Test.java", + lines( + "public class Test extends B.BM {", // + " I i;", + "}")) + .build(); + + try { + IntegrationTestSupport.runTurbine(sources, ImmutableList.of(libJar)); + fail(); + } catch (TurbineError error) { + assertThat(error) + .hasMessageThat() + .contains("Test.java: error: could not locate class file for A"); + } + } + + @Test + public void missingOuter2() throws Exception { + + Map<String, byte[]> lib = + IntegrationTestSupport.runJavac( + ImmutableMap.of( + "A.java", + lines( + "class A {", // + " class M { ", + " class I {} ", + " } ", + "}"), + "B.java", + lines( + "class B extends A { ", + " class BM extends M { ", + " class BI extends I {} ", + " } ", + "}")), + ImmutableList.of()); + + Path libJar = temporaryFolder.newFile("lib.jar").toPath(); + try (OutputStream os = Files.newOutputStream(libJar); + JarOutputStream jos = new JarOutputStream(os)) { + jos.putNextEntry(new JarEntry("A$M.class")); + jos.write(lib.get("A$M")); + jos.putNextEntry(new JarEntry("A$M$I.class")); + jos.write(lib.get("A$M$I")); + jos.putNextEntry(new JarEntry("B.class")); + jos.write(lib.get("B")); + jos.putNextEntry(new JarEntry("B$BM.class")); + jos.write(lib.get("B$BM")); + jos.putNextEntry(new JarEntry("B$BM$BI.class")); + jos.write(lib.get("B$BM$BI")); + } + + ImmutableMap<String, String> sources = + ImmutableMap.<String, String>builder() + .put( + "Test.java", + lines( + "public class Test extends B {", // + " class M extends BM {", + " I i;", + " }", + "}")) + .build(); + + try { + IntegrationTestSupport.runTurbine(sources, ImmutableList.of(libJar)); + fail(); + } catch (TurbineError error) { + assertThat(error) + .hasMessageThat() + .contains("Test.java: error: could not locate class file for A"); + } + } + static String lines(String... lines) { return Joiner.on("\n").join(lines); } |