diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java index 304005f4..cbdc025f 100644 --- a/java/com/google/turbine/binder/CanonicalTypeBinder.java +++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java @@ -25,6 +25,7 @@ import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; +import com.google.turbine.binder.bound.TypeBoundClass.RecordComponentInfo; import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.sym.ClassSymbol; @@ -60,8 +61,8 @@ static SourceTypeBoundClass bind( } ImmutableMap typParamTypes = typeParameters(base.source(), pos, env, sym, base.typeParameterTypes()); - ImmutableList components = - parameters(base.source(), env, sym, pos, base.components()); + ImmutableList components = + components(base.source(), env, sym, pos, base.components()); ImmutableList methods = methods(base.source(), pos, env, sym, base.methods()); ImmutableList fields = fields(base.source(), env, sym, base.fields()); return new SourceTypeBoundClass( @@ -166,6 +167,24 @@ private static ParamInfo param( base.access()); } + private static ImmutableList components( + SourceFile source, + Env env, + ClassSymbol sym, + int pos, + ImmutableList components) { + ImmutableList.Builder result = ImmutableList.builder(); + for (RecordComponentInfo component : components) { + result.add( + new RecordComponentInfo( + component.sym(), + Canonicalize.canonicalize(source, pos, env, sym, component.type()), + component.annotations(), + component.access())); + } + return result.build(); + } + private static ImmutableMap typeParameters( SourceFile source, int position, diff --git a/java/com/google/turbine/binder/ConstBinder.java b/java/com/google/turbine/binder/ConstBinder.java index b2c4e1c9..232370cc 100644 --- a/java/com/google/turbine/binder/ConstBinder.java +++ b/java/com/google/turbine/binder/ConstBinder.java @@ -29,6 +29,7 @@ import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; +import com.google.turbine.binder.bound.TypeBoundClass.RecordComponentInfo; import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.env.CompoundEnv; import com.google.turbine.binder.env.Env; @@ -104,7 +105,7 @@ public SourceTypeBoundClass bind() { env, log) .evaluateAnnotations(base.annotations()); - ImmutableList components = bindParameters(base.components()); + ImmutableList components = bindRecordComponents(base.components()); ImmutableList fields = fields(base.fields()); ImmutableList methods = bindMethods(base.methods()); return new SourceTypeBoundClass( @@ -169,6 +170,16 @@ private ParamInfo bindParameter(ParamInfo base) { return new ParamInfo(base.sym(), bindType(base.type()), annos, base.access()); } + private ImmutableList bindRecordComponents( + ImmutableList components) { + ImmutableList.Builder result = ImmutableList.builder(); + for (RecordComponentInfo base : components) { + ImmutableList annos = constEvaluator.evaluateAnnotations(base.annotations()); + result.add(new RecordComponentInfo(base.sym(), bindType(base.type()), annos, base.access())); + } + return result.build(); + } + static @Nullable AnnotationMetadata bindAnnotationMetadata( TurbineTyKind kind, Iterable annotations) { if (kind != TurbineTyKind.ANNOTATION) { diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java index 0acd0c53..4929962e 100644 --- a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java +++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java @@ -30,6 +30,7 @@ import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; +import com.google.turbine.binder.bound.TypeBoundClass.RecordComponentInfo; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.diag.TurbineError; @@ -73,7 +74,7 @@ public static SourceTypeBoundClass bind( base.superClassType(), base.typeParameterTypes(), base.access(), - bindParameters(env, base.components(), TurbineElementType.RECORD_COMPONENT), + bindComponents(env, base.components(), TurbineElementType.RECORD_COMPONENT), bindMethods(env, base.methods()), bindFields(env, base.fields()), base.owner(), @@ -144,6 +145,27 @@ private static ParamInfo bindParam( return new ParamInfo(base.sym(), type, declarationAnnotations.build(), base.access()); } + private static ImmutableList bindComponents( + Env env, + ImmutableList components, + TurbineElementType declarationTarget) { + ImmutableList.Builder result = ImmutableList.builder(); + for (RecordComponentInfo component : components) { + ImmutableList.Builder declarationAnnotations = ImmutableList.builder(); + Type type = + disambiguate( + env, + declarationTarget, + component.type(), + component.annotations(), + declarationAnnotations); + result.add( + new RecordComponentInfo( + component.sym(), type, declarationAnnotations.build(), component.access())); + } + return result.build(); + } + /** * Moves type annotations in {@code annotations} to {@code type}, and adds any declaration * annotations on {@code type} to {@code declarationAnnotations}. diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java index b28aa240..f4b48493 100644 --- a/java/com/google/turbine/binder/TypeBinder.java +++ b/java/com/google/turbine/binder/TypeBinder.java @@ -28,6 +28,7 @@ import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; +import com.google.turbine.binder.bound.TypeBoundClass.RecordComponentInfo; import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.lookup.CompoundScope; @@ -38,6 +39,7 @@ import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.binder.sym.MethodSymbol; import com.google.turbine.binder.sym.ParamSymbol; +import com.google.turbine.binder.sym.RecordComponentSymbol; import com.google.turbine.binder.sym.Symbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.diag.TurbineError.ErrorKind; @@ -235,8 +237,7 @@ private SourceTypeBoundClass bind() { SyntheticMethods syntheticMethods = new SyntheticMethods(); - ImmutableList components = - bindComponents(scope, syntheticMethods, base.decl().components()); + ImmutableList components = bindComponents(scope, base.decl().components()); ImmutableList.Builder methods = ImmutableList.builder() @@ -284,20 +285,17 @@ MethodSymbol create(ClassSymbol owner, String name) { } } - private ImmutableList bindComponents( - CompoundScope scope, - SyntheticMethods syntheticMethods, - ImmutableList components) { - ImmutableList.Builder result = ImmutableList.builder(); + private ImmutableList bindComponents( + CompoundScope scope, ImmutableList components) { + ImmutableList.Builder result = ImmutableList.builder(); for (Tree.VarDecl p : components) { int access = 0; for (TurbineModifier m : p.mods()) { access |= m.flag(); } - MethodSymbol msym = syntheticMethods.create(owner, ""); - ParamInfo param = - new ParamInfo( - new ParamSymbol(msym, p.name().value()), + RecordComponentInfo param = + new RecordComponentInfo( + new RecordComponentSymbol(owner, p.name().value()), bindTy(scope, p.ty()), bindAnnotations(scope, p.annos()), access); @@ -308,7 +306,7 @@ private ImmutableList bindComponents( /** Collect synthetic and implicit methods, including default constructors and enum methods. */ ImmutableList syntheticMethods( - SyntheticMethods syntheticMethods, ImmutableList components) { + SyntheticMethods syntheticMethods, ImmutableList components) { switch (base.kind()) { case CLASS: return maybeDefaultConstructor(syntheticMethods); @@ -322,13 +320,22 @@ ImmutableList syntheticMethods( } private ImmutableList maybeDefaultRecordConstructor( - SyntheticMethods syntheticMethods, ImmutableList components) { + SyntheticMethods syntheticMethods, ImmutableList components) { if (hasConstructor()) { return ImmutableList.of(); } MethodSymbol symbol = syntheticMethods.create(owner, ""); + ImmutableList.Builder params = ImmutableList.builder(); + for (RecordComponentInfo component : components) { + params.add( + new ParamInfo( + new ParamSymbol(symbol, component.name()), + component.type(), + component.annotations(), + component.access())); + } return ImmutableList.of( - syntheticConstructor(symbol, components, TurbineVisibility.fromAccess(base.access()))); + syntheticConstructor(symbol, params.build(), TurbineVisibility.fromAccess(base.access()))); } private ImmutableList maybeDefaultConstructor(SyntheticMethods syntheticMethods) { @@ -450,7 +457,7 @@ private ImmutableList syntheticEnumMethods(SyntheticMethods syntheti } private ImmutableList syntheticRecordMethods( - SyntheticMethods syntheticMethods, ImmutableList components) { + SyntheticMethods syntheticMethods, ImmutableList components) { ImmutableList.Builder methods = ImmutableList.builder(); MethodSymbol toStringMethod = syntheticMethods.create(owner, "toString"); methods.add( @@ -496,7 +503,7 @@ private ImmutableList syntheticRecordMethods( null, ImmutableList.of(), null)); - for (ParamInfo c : components) { + for (RecordComponentInfo c : components) { MethodSymbol componentMethod = syntheticMethods.create(owner, c.name()); methods.add( new MethodInfo( diff --git a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java index f1ad8397..fb3ee277 100644 --- a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java +++ b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java @@ -44,7 +44,7 @@ public class SourceTypeBoundClass implements TypeBoundClass { private final ImmutableMap typeParameterTypes; private final @Nullable Type superClassType; private final ImmutableList interfaceTypes; - private final ImmutableList components; + private final ImmutableList components; private final ImmutableList methods; private final ImmutableList fields; private final CompoundScope enclosingScope; @@ -60,7 +60,7 @@ public SourceTypeBoundClass( @Nullable Type superClassType, ImmutableMap typeParameterTypes, int access, - ImmutableList components, + ImmutableList components, ImmutableList methods, ImmutableList fields, @Nullable ClassSymbol owner, @@ -154,7 +154,7 @@ public ImmutableList interfaceTypes() { /** The record components. */ @Override - public ImmutableList components() { + public ImmutableList components() { return components; } diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java index 14f4f2d9..cc289019 100644 --- a/java/com/google/turbine/binder/bound/TypeBoundClass.java +++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java @@ -21,6 +21,7 @@ import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.binder.sym.MethodSymbol; import com.google.turbine.binder.sym.ParamSymbol; +import com.google.turbine.binder.sym.RecordComponentSymbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.model.Const; import com.google.turbine.model.TurbineFlag; @@ -51,7 +52,7 @@ public interface TypeBoundClass extends HeaderBoundClass { ImmutableList methods(); /** Record components. */ - ImmutableList components(); + ImmutableList components(); /** * Annotation metadata, e.g. from {@link java.lang.annotation.Target}, {@link @@ -322,4 +323,45 @@ public int access() { return access; } } + + /** A record component. */ + class RecordComponentInfo { + private final RecordComponentSymbol sym; + private final Type type; + private final int access; + private final ImmutableList annotations; + + public RecordComponentInfo( + RecordComponentSymbol sym, Type type, ImmutableList annotations, int access) { + this.sym = sym; + this.type = type; + this.access = access; + this.annotations = annotations; + } + + /** The record component's symbol. */ + public RecordComponentSymbol sym() { + return sym; + } + + /** The record component type. */ + public Type type() { + return type; + } + + /** Record component annotations. */ + public ImmutableList annotations() { + return annotations; + } + + /** The Record component's name. */ + public String name() { + return sym.name(); + } + + /** The Record component's modifiers. */ + public int access() { + return access; + } + } } diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java index 1f196f53..516a027a 100644 --- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java +++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java @@ -487,7 +487,7 @@ public ImmutableList methods() { } @Override - public ImmutableList components() { + public ImmutableList components() { return ImmutableList.of(); } diff --git a/java/com/google/turbine/binder/sym/RecordComponentSymbol.java b/java/com/google/turbine/binder/sym/RecordComponentSymbol.java new file mode 100644 index 00000000..c3f44f6e --- /dev/null +++ b/java/com/google/turbine/binder/sym/RecordComponentSymbol.java @@ -0,0 +1,67 @@ +/* + * Copyright 2021 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.sym; + +import com.google.errorprone.annotations.Immutable; +import java.util.Objects; +import org.jspecify.nullness.Nullable; + +/** A record component symbol. */ +@Immutable +public class RecordComponentSymbol implements Symbol { + private final ClassSymbol owner; + private final String name; + + public RecordComponentSymbol(ClassSymbol owner, String name) { + this.owner = owner; + this.name = name; + } + + /** The enclosing class. */ + public ClassSymbol owner() { + return owner; + } + + /** The parameter name. */ + public String name() { + return name; + } + + @Override + public Kind symKind() { + return Kind.RECORD_COMPONENT; + } + + @Override + public int hashCode() { + return Objects.hash(name, owner); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof RecordComponentSymbol)) { + return false; + } + RecordComponentSymbol other = (RecordComponentSymbol) obj; + return name().equals(other.name()) && owner().equals(other.owner()); + } + + @Override + public String toString() { + return name; + } +} diff --git a/java/com/google/turbine/binder/sym/Symbol.java b/java/com/google/turbine/binder/sym/Symbol.java index bc142cba..b1eb8e18 100644 --- a/java/com/google/turbine/binder/sym/Symbol.java +++ b/java/com/google/turbine/binder/sym/Symbol.java @@ -28,6 +28,7 @@ enum Kind { METHOD, FIELD, PARAMETER, + RECORD_COMPONENT, MODULE, PACKAGE } diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java index d4d02bdf..0f3eb245 100644 --- a/java/com/google/turbine/lower/Lower.java +++ b/java/com/google/turbine/lower/Lower.java @@ -39,6 +39,7 @@ import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; +import com.google.turbine.binder.bound.TypeBoundClass.RecordComponentInfo; import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.bytecode.BytecodeBoundClass; import com.google.turbine.binder.env.CompoundEnv; @@ -263,7 +264,7 @@ private byte[] lower( if (info.kind().equals(TurbineTyKind.RECORD)) { ImmutableList.Builder components = ImmutableList.builder(); - for (ParamInfo component : info.components()) { + for (RecordComponentInfo component : info.components()) { components.add(lowerComponent(info, component)); } record = new ClassFile.RecordInfo(components.build()); @@ -326,7 +327,7 @@ record = new ClassFile.RecordInfo(components.build()); } private ClassFile.RecordInfo.RecordComponentInfo lowerComponent( - SourceTypeBoundClass info, ParamInfo c) { + SourceTypeBoundClass info, RecordComponentInfo c) { Function tenv = new TyVarEnv(info.typeParameterTypes()); String desc = SigWriter.type(sig.signature(Erasure.erase(c.type(), tenv))); String signature = sig.fieldSignature(c.type()); diff --git a/java/com/google/turbine/processing/ModelFactory.java b/java/com/google/turbine/processing/ModelFactory.java index 9b782cd4..160d5aea 100644 --- a/java/com/google/turbine/processing/ModelFactory.java +++ b/java/com/google/turbine/processing/ModelFactory.java @@ -29,6 +29,7 @@ import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; +import com.google.turbine.binder.bound.TypeBoundClass.RecordComponentInfo; import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.env.CompoundEnv; import com.google.turbine.binder.env.Env; @@ -40,6 +41,7 @@ import com.google.turbine.binder.sym.MethodSymbol; import com.google.turbine.binder.sym.PackageSymbol; import com.google.turbine.binder.sym.ParamSymbol; +import com.google.turbine.binder.sym.RecordComponentSymbol; import com.google.turbine.binder.sym.Symbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.model.TurbineConstantTypeKind; @@ -48,6 +50,7 @@ import com.google.turbine.processing.TurbineElement.TurbineNoTypeElement; import com.google.turbine.processing.TurbineElement.TurbinePackageElement; import com.google.turbine.processing.TurbineElement.TurbineParameterElement; +import com.google.turbine.processing.TurbineElement.TurbineRecordComponentElement; import com.google.turbine.processing.TurbineElement.TurbineTypeElement; import com.google.turbine.processing.TurbineElement.TurbineTypeParameterElement; import com.google.turbine.processing.TurbineTypeMirror.TurbineArrayType; @@ -110,6 +113,8 @@ public void round(CompoundEnv env, TopLevelIndex tl private final Map methodCache = new HashMap<>(); private final Map classCache = new HashMap<>(); private final Map paramCache = new HashMap<>(); + private final Map recordComponentCache = + new HashMap<>(); private final Map tyParamCache = new HashMap<>(); private final Map packageCache = new HashMap<>(); @@ -230,6 +235,8 @@ Element element(Symbol symbol) { return fieldElement((FieldSymbol) symbol); case PARAMETER: return parameterElement((ParamSymbol) symbol); + case RECORD_COMPONENT: + return recordComponentElement((RecordComponentSymbol) symbol); case PACKAGE: return packageElement((PackageSymbol) symbol); case MODULE: @@ -263,6 +270,11 @@ VariableElement parameterElement(ParamSymbol sym) { return paramCache.computeIfAbsent(sym, k -> new TurbineParameterElement(this, sym)); } + VariableElement recordComponentElement(RecordComponentSymbol sym) { + return recordComponentCache.computeIfAbsent( + sym, k -> new TurbineRecordComponentElement(this, sym)); + } + TurbineTypeParameterElement typeParameterElement(TyVarSymbol sym) { return tyParamCache.computeIfAbsent(sym, k -> new TurbineTypeParameterElement(this, sym)); } @@ -330,6 +342,16 @@ ParamInfo getParamInfo(ParamSymbol sym) { return null; } + RecordComponentInfo getRecordComponentInfo(RecordComponentSymbol sym) { + TypeBoundClass info = getSymbol(sym.owner()); + for (RecordComponentInfo component : info.components()) { + if (component.sym().equals(sym)) { + return component; + } + } + return null; + } + FieldInfo getFieldInfo(FieldSymbol symbol) { TypeBoundClass info = getSymbol(symbol.owner()); requireNonNull(info, symbol.owner().toString()); @@ -370,6 +392,8 @@ static ClassSymbol enclosingClass(Symbol sym) { return ((FieldSymbol) sym).owner(); case PARAMETER: return ((ParamSymbol) sym).owner().owner(); + case RECORD_COMPONENT: + return ((RecordComponentSymbol) sym).owner(); case PACKAGE: case MODULE: throw new IllegalArgumentException(sym.toString()); diff --git a/java/com/google/turbine/processing/TurbineElement.java b/java/com/google/turbine/processing/TurbineElement.java index 8581fdac..7824d562 100644 --- a/java/com/google/turbine/processing/TurbineElement.java +++ b/java/com/google/turbine/processing/TurbineElement.java @@ -21,6 +21,7 @@ import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -32,6 +33,7 @@ import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; +import com.google.turbine.binder.bound.TypeBoundClass.RecordComponentInfo; import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.lookup.PackageScope; import com.google.turbine.binder.sym.ClassSymbol; @@ -39,6 +41,7 @@ import com.google.turbine.binder.sym.MethodSymbol; import com.google.turbine.binder.sym.PackageSymbol; import com.google.turbine.binder.sym.ParamSymbol; +import com.google.turbine.binder.sym.RecordComponentSymbol; import com.google.turbine.binder.sym.Symbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.diag.TurbineError; @@ -379,11 +382,20 @@ public ElementKind getKind() { case ANNOTATION: return ElementKind.ANNOTATION_TYPE; case RECORD: - return ElementKind.valueOf("RECORD"); + return RECORD.get(); } throw new AssertionError(info.kind()); } + private static final Supplier RECORD = + Suppliers.memoize( + new Supplier() { + @Override + public ElementKind get() { + return ElementKind.valueOf("RECORD"); + } + }); + @Override public Set getModifiers() { return asModifierSet(ModifierOwner.TYPE, infoNonNull().access() & ~TurbineFlag.ACC_SUPER); @@ -431,6 +443,9 @@ public Element getEnclosingElement() { public ImmutableList get() { TypeBoundClass info = infoNonNull(); ImmutableList.Builder result = ImmutableList.builder(); + for (RecordComponentInfo component : info.components()) { + result.add(factory.recordComponentElement(component.sym())); + } for (FieldInfo field : info.fields()) { result.add(factory.fieldElement(field.sym())); } @@ -1203,6 +1218,120 @@ protected ImmutableList annos() { } } + /** A {@link VariableElement} implementation for a record info. */ + static class TurbineRecordComponentElement extends TurbineElement implements VariableElement { + + @Override + public RecordComponentSymbol sym() { + return sym; + } + + @Override + public String javadoc() { + return null; + } + + @Override + public int hashCode() { + return sym.hashCode(); + } + + @Override + public boolean equals(@Nullable Object obj) { + return obj instanceof TurbineRecordComponentElement + && sym.equals(((TurbineRecordComponentElement) obj).sym); + } + + private final RecordComponentSymbol sym; + + private final Supplier info = + memoize( + new Supplier() { + @Override + public RecordComponentInfo get() { + return factory.getRecordComponentInfo(sym); + } + }); + + @Nullable + RecordComponentInfo info() { + return info.get(); + } + + public TurbineRecordComponentElement(ModelFactory factory, RecordComponentSymbol sym) { + super(factory); + this.sym = sym; + } + + @Override + public Object getConstantValue() { + return null; + } + + private final Supplier type = + memoize( + new Supplier() { + @Override + public TypeMirror get() { + return factory.asTypeMirror(info().type()); + } + }); + + @Override + public TypeMirror asType() { + return type.get(); + } + + @Override + public ElementKind getKind() { + return RECORD_COMPONENT.get(); + } + + private static final Supplier RECORD_COMPONENT = + Suppliers.memoize( + new Supplier() { + @Override + public ElementKind get() { + return ElementKind.valueOf("RECORD_COMPONENT"); + } + }); + + @Override + public Set getModifiers() { + return asModifierSet(ModifierOwner.PARAMETER, info().access()); + } + + @Override + public Name getSimpleName() { + return new TurbineName(sym.name()); + } + + @Override + public Element getEnclosingElement() { + return factory.typeElement(sym.owner()); + } + + @Override + public List getEnclosedElements() { + return ImmutableList.of(); + } + + @Override + public R accept(ElementVisitor v, P p) { + return v.visitVariable(this, p); + } + + @Override + public String toString() { + return String.valueOf(sym.name()); + } + + @Override + protected ImmutableList annos() { + return info().annotations(); + } + } + static class TurbineNoTypeElement implements TypeElement { private final ModelFactory factory; diff --git a/java/com/google/turbine/processing/TurbineMessager.java b/java/com/google/turbine/processing/TurbineMessager.java index b16ccea3..8e78b8bb 100644 --- a/java/com/google/turbine/processing/TurbineMessager.java +++ b/java/com/google/turbine/processing/TurbineMessager.java @@ -128,6 +128,10 @@ private int getPosition(Symbol sym) { return fieldPosition((FieldSymbol) sym); case PARAMETER: return paramPosition((ParamSymbol) sym); + case RECORD_COMPONENT: + // javac doesn't seem to provide diagnostic positions for record components, so we don't + // either + return -1; case MODULE: case PACKAGE: break; diff --git a/java/com/google/turbine/processing/TurbineTypes.java b/java/com/google/turbine/processing/TurbineTypes.java index 9eb89d0a..655b14bb 100644 --- a/java/com/google/turbine/processing/TurbineTypes.java +++ b/java/com/google/turbine/processing/TurbineTypes.java @@ -29,6 +29,7 @@ import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.binder.sym.MethodSymbol; import com.google.turbine.binder.sym.ParamSymbol; +import com.google.turbine.binder.sym.RecordComponentSymbol; import com.google.turbine.binder.sym.Symbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.model.TurbineConstantTypeKind; @@ -1131,6 +1132,8 @@ private static ClassSymbol enclosingClass(Symbol symbol) { return ((FieldSymbol) symbol).owner(); case PARAMETER: return ((ParamSymbol) symbol).owner().owner(); + case RECORD_COMPONENT: + return ((RecordComponentSymbol) symbol).owner(); case MODULE: case PACKAGE: throw new IllegalArgumentException(symbol.symKind().toString()); diff --git a/javatests/com/google/turbine/lower/IntegrationTestSupport.java b/javatests/com/google/turbine/lower/IntegrationTestSupport.java index a0f77406..c91270f5 100644 --- a/javatests/com/google/turbine/lower/IntegrationTestSupport.java +++ b/javatests/com/google/turbine/lower/IntegrationTestSupport.java @@ -16,6 +16,7 @@ package com.google.turbine.lower; +import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_VERSION; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.io.MoreFiles.getFileExtension; import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH; @@ -50,6 +51,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import java.lang.reflect.Method; import java.nio.file.FileSystem; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -690,5 +692,21 @@ public static TestInput parse(String text) { } } + public static int getMajor() { + try { + Method versionMethod = Runtime.class.getMethod("version"); + Object version = versionMethod.invoke(null); + return (int) version.getClass().getMethod("major").invoke(version); + } catch (ReflectiveOperationException e) { + // continue below + } + + int version = (int) Double.parseDouble(JAVA_CLASS_VERSION.value()); + if (49 <= version && version <= 52) { + return version - (49 - 5); + } + throw new IllegalStateException("Unknown Java version: " + JAVA_CLASS_VERSION.value()); + } + private IntegrationTestSupport() {} } diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java index 389f55ae..9e160750 100644 --- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java +++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java @@ -16,8 +16,6 @@ package com.google.turbine.lower; -import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_VERSION; -import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION; import static com.google.common.truth.Truth.assertThat; import static com.google.turbine.testing.TestResources.getResource; import static java.util.stream.Collectors.toList; @@ -28,7 +26,6 @@ import com.google.common.collect.Lists; import java.io.IOError; import java.io.IOException; -import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -376,7 +373,7 @@ public void test() throws Exception { } int version = SOURCE_VERSION.getOrDefault(test, 8); - assumeTrue(version <= getMajor()); + assumeTrue(version <= IntegrationTestSupport.getMajor()); ImmutableList javacopts = ImmutableList.of("-source", String.valueOf(version), "-target", String.valueOf(version)); @@ -389,20 +386,4 @@ public void test() throws Exception { assertThat(IntegrationTestSupport.dump(IntegrationTestSupport.sortMembers(actual))) .isEqualTo(IntegrationTestSupport.dump(IntegrationTestSupport.canonicalize(expected))); } - - private static int getMajor() { - try { - Method versionMethod = Runtime.class.getMethod("version"); - Object version = versionMethod.invoke(null); - return (int) version.getClass().getMethod("major").invoke(version); - } catch (ReflectiveOperationException e) { - // continue below - } - - int version = (int) Double.parseDouble(JAVA_CLASS_VERSION.value()); - if (49 <= version && version <= 52) { - return version - (49 - 5); - } - throw new IllegalStateException("Unknown Java version: " + JAVA_SPECIFICATION_VERSION.value()); - } } diff --git a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java index 96664d28..d5c281e4 100644 --- a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java +++ b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java @@ -24,6 +24,7 @@ import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; import com.google.common.base.Joiner; import com.google.common.base.Splitter; @@ -53,6 +54,7 @@ import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; @@ -614,6 +616,62 @@ public void badElementValue() throws IOException { .containsExactly("@Deprecated({})"); } + @SupportedAnnotationTypes("*") + public static class RecordProcessor extends AbstractProcessor { + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (Element e : roundEnv.getRootElements()) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getKind() + " " + e); + for (Element m : e.getEnclosedElements()) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, m.getKind() + " " + m); + } + } + return false; + } + } + + @Test + public void recordProcessing() throws IOException { + assumeTrue(IntegrationTestSupport.getMajor() >= 15); + ImmutableList units = + parseUnit( + "=== R.java ===", // + "record R(@Deprecated T x, int... y) {}"); + TurbineError e = + assertThrows( + TurbineError.class, + () -> + Binder.bind( + units, + ClassPathBinder.bindClasspath(ImmutableList.of()), + ProcessorInfo.create( + ImmutableList.of(new RecordProcessor()), + getClass().getClassLoader(), + ImmutableMap.of(), + SourceVersion.latestSupported()), + TestClassPaths.TURBINE_BOOTCLASSPATH, + Optional.empty())); + assertThat( + e.diagnostics().stream() + .filter(d -> d.severity().equals(Diagnostic.Kind.ERROR)) + .map(d -> d.message())) + .containsExactly( + "RECORD R", + "RECORD_COMPONENT x", + "RECORD_COMPONENT y", + "CONSTRUCTOR R(T,int[])", + "METHOD toString()", + "METHOD hashCode()", + "METHOD equals(java.lang.Object)", + "METHOD x()", + "METHOD y()"); + } + private static ImmutableList parseUnit(String... lines) { return IntegrationTestSupport.TestInput.parse(Joiner.on('\n').join(lines)) .sources