use of javax.lang.model.element.TypeParameterElement in project immutables by immutables.
the class Generics method computeParameters.
private static Parameter[] computeParameters(final Protoclass protoclass, final Element element) {
if (!(element instanceof Parameterizable)) {
return NO_PARAMETERS;
}
final List<? extends TypeParameterElement> typeParameters = ((Parameterizable) element).getTypeParameters();
if (typeParameters.isEmpty()) {
return NO_PARAMETERS;
}
class Creator {
final String[] vars = collectVars(typeParameters);
final DeclaringType declaringType = protoclass.environment().round().inferDeclaringTypeFor(element);
Parameter[] create() {
final Parameter[] parameters = new Parameter[typeParameters.size()];
int i = 0;
for (TypeParameterElement e : typeParameters) {
parameters[i] = new Parameter(i, e.getSimpleName().toString(), boundsFrom(e));
i++;
}
return parameters;
}
String[] boundsFrom(TypeParameterElement e) {
List<? extends TypeMirror> boundMirrors = e.getBounds();
if (boundMirrors.isEmpty()) {
return NO_STRINGS;
}
String[] bounds = new String[boundMirrors.size()];
int c = 0;
for (TypeMirror m : boundMirrors) {
TypeStringProvider provider = newProvider(m);
provider.process();
bounds[c++] = provider.returnTypeName();
}
if (bounds.length == 1 && bounds[0].equals(Object.class.getName())) {
return NO_STRINGS;
}
return bounds;
}
TypeStringProvider newProvider(TypeMirror type) {
return new TypeStringProvider(protoclass.report(), element, type, new ImportsTypeStringResolver(declaringType, declaringType), vars, null);
}
}
return new Creator().create();
}
use of javax.lang.model.element.TypeParameterElement in project immutables by immutables.
the class Generics method collectVars.
private static String[] collectVars(List<? extends TypeParameterElement> typeParameters) {
String[] vars = new String[typeParameters.size()];
int c = 0;
for (TypeParameterElement p : typeParameters) {
vars[c++] = p.getSimpleName().toString();
}
return vars;
}
use of javax.lang.model.element.TypeParameterElement in project auto by google.
the class AutoValueCompilationTest method testTypeParametersWithAnnotationsOnBounds.
// Tests that type annotations are correctly copied from the bounds of type parameters in the
// @AutoValue class to the bounds of the corresponding parameters in the generated class. For
// example, if we have `@AutoValue abstract class Foo<T extends @NullableType Object>`, then the
// generated class should be `class AutoValue_Foo<T extends @NullableType Object> extends Foo<T>`.
// Some buggy versions of javac do not report type annotations correctly in this context.
// AutoValue can't copy them if it can't see them, so we make a special annotation processor to
// detect if we are in the presence of this bug and if so we don't fail.
@Test
public void testTypeParametersWithAnnotationsOnBounds() {
@SupportedAnnotationTypes("*")
class CompilerBugProcessor extends AbstractProcessor {
boolean checkedAnnotationsOnTypeBounds;
boolean reportsAnnotationsOnTypeBounds;
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
TypeElement test = processingEnv.getElementUtils().getTypeElement("com.example.Test");
TypeParameterElement t = test.getTypeParameters().get(0);
this.checkedAnnotationsOnTypeBounds = true;
this.reportsAnnotationsOnTypeBounds = !t.getBounds().get(0).getAnnotationMirrors().isEmpty();
}
return false;
}
}
CompilerBugProcessor compilerBugProcessor = new CompilerBugProcessor();
JavaFileObject nullableTypeFileObject = JavaFileObjects.forSourceLines("foo.bar.NullableType", "package foo.bar;", "", "import java.lang.annotation.ElementType;", "import java.lang.annotation.Target;", "", "@Target(ElementType.TYPE_USE)", "public @interface NullableType {}");
JavaFileObject autoValueFileObject = JavaFileObjects.forSourceLines("com.example.Test", "package com.example;", "", "import com.google.auto.value.AutoValue;", "import foo.bar.NullableType;", "", "@AutoValue", "abstract class Test<T extends @NullableType Object & @NullableType Cloneable> {}");
Compilation compilation = javac().withProcessors(new AutoValueProcessor(), compilerBugProcessor).withOptions("-Xlint:-processing", "-implicit:none").compile(nullableTypeFileObject, autoValueFileObject);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilerBugProcessor.checkedAnnotationsOnTypeBounds).isTrue();
if (compilerBugProcessor.reportsAnnotationsOnTypeBounds) {
assertThat(compilation).generatedSourceFile("com.example.AutoValue_Test").contentsAsUtf8String().contains("class AutoValue_Test<T extends @NullableType Object & @NullableType Cloneable>" + " extends Test<T> {");
}
}
use of javax.lang.model.element.TypeParameterElement in project auto by google.
the class BuilderMethodClassifierForAutoBuilder method rewriteParameterTypes.
// Rewrites the parameter types of the executable so they use the type variables of the builder
// where appropriate.
//
// Suppose we have something like this:
//
// static <E> Set<E> singletonSet(E elem) {...}
//
// @AutoBuilder(callMethod = "singletonSet")
// interface SingletonSetBuilder<E> {
// SingletonSetBuilder<E> setElem(E elem);
// Set<E> build();
// }
//
// We want to check that the type of the setter `setElem` matches the type of the
// parameter it is setting. But in fact it doesn't: the type of the setter is
// E-of-SingletonSetBuilder while the type of the parameter is E-of-singletonSet. So we
// need to rewrite any type variables mentioned in parameters so that they use the corresponding
// types from the builder. We want to return a map where "elem" is mapped to
// E-of-SingletonSetBuilder, even though the `elem` that we get from the parameters of
// singletonSet is going to be E-of-singletonSet. And we also want that to work if the parameter
// is something more complicated, like List<? extends E>.
//
// For the corresponding situation with AutoValue, we have a way of dodging the problem somewhat.
// For an @AutoValue class Foo<E> with a builder Builder<E>, we can craft a DeclaredType
// Foo<E> where the E comes from Builder<E>, and we can use Types.asMemberOf to determine the
// return types of methods (which are what we want to rewrite in that case). But that doesn't
// work here because singletonSet is static and Types.asMemberOf would have no effect on it.
//
// So instead we take the type of each parameter and feed it through a TypeVisitor that rewrites
// type variables, rewriting from E-of-singletonSet to E-of-SingletonSetBuilder. Then we can use
// Types.isSameType or Types.isAssignable and it will work as we expect.
//
// In principle a similar situation arises with the return type Set<E> of singletonSet versus
// the return type Set<E> of SingletonSetBuilder.build(). But in fact we only use
// MoreTypes.equivalence to compare those, and that returns true for distinct type variables if
// they have the same name and bounds.
private static ImmutableMap<String, TypeMirror> rewriteParameterTypes(ExecutableElement executable, TypeElement builderType, ErrorReporter errorReporter, Types typeUtils) {
ImmutableList<TypeParameterElement> executableTypeParams = executableTypeParams(executable);
List<? extends TypeParameterElement> builderTypeParams = builderType.getTypeParameters();
if (!BuilderSpec.sameTypeParameters(executableTypeParams, builderTypeParams)) {
errorReporter.abortWithError(builderType, "[AutoBuilderTypeParams] Builder type parameters %s must match type parameters %s of %s", TypeEncoder.typeParametersString(builderTypeParams), TypeEncoder.typeParametersString(executableTypeParams), AutoBuilderProcessor.executableString(executable));
}
if (executableTypeParams.isEmpty()) {
// variables to substitute.
return executable.getParameters().stream().collect(toImmutableMap(v -> v.getSimpleName().toString(), Element::asType));
}
Map<Equivalence.Wrapper<TypeVariable>, TypeMirror> typeVariables = new LinkedHashMap<>();
for (int i = 0; i < executableTypeParams.size(); i++) {
TypeVariable from = MoreTypes.asTypeVariable(executableTypeParams.get(i).asType());
TypeVariable to = MoreTypes.asTypeVariable(builderTypeParams.get(i).asType());
typeVariables.put(MoreTypes.equivalence().wrap(from), to);
}
Function<TypeVariable, TypeMirror> substitute = v -> typeVariables.get(MoreTypes.equivalence().wrap(v));
return executable.getParameters().stream().collect(toImmutableMap(v -> v.getSimpleName().toString(), v -> TypeVariables.substituteTypeVariables(v.asType(), substitute, typeUtils)));
}
use of javax.lang.model.element.TypeParameterElement in project auto by google.
the class TypeVariables method rewriteReturnTypes.
/**
* Returns a map from methods to return types, where the return types are not necessarily the
* original return types of the methods. Consider this example:
*
* <pre>
* @AutoValue class {@code Foo<T>} {
* abstract T getFoo();
*
* @AutoValue.Builder
* abstract class {@code Builder<T>} {
* abstract Builder setFoo(T t);
* abstract {@code Foo<T>} build();
* }
* }
* </pre>
*
* We want to be able to check that the parameter type of {@code setFoo} is the same as the return
* type of {@code getFoo}. But in fact it isn't, because the {@code T} of {@code Foo<T>} is not
* the same as the {@code T} of {@code Foo.Builder<T>}. So we create a parallel {@code Foo<T>}
* where the {@code T} <i>is</i> the one from {@code Foo.Builder<T>}. That way the types do
* correspond. This method then returns the return types of the given methods as they appear in
* that parallel class, meaning the type given for {@code getFoo()} is the {@code T} of {@code
* Foo.Builder<T>}.
*
* <p>We do the rewrite this way around (applying the type parameter from {@code Foo.Builder} to
* {@code Foo}) because if we hit one of the historical Eclipse bugs with {@link Types#asMemberOf}
* then {@link EclipseHack#methodReturnType} can use fallback logic, which only works for methods
* with no arguments.
*
* @param methods the methods whose return types are to be rewritten.
* @param sourceType the class containing those methods ({@code Foo} in the example).
* @param targetType the class to translate the methods into ({@code Foo.Builder<T>}) in the
* example.
*/
static ImmutableMap<ExecutableElement, TypeMirror> rewriteReturnTypes(Elements elementUtils, Types typeUtils, Collection<ExecutableElement> methods, TypeElement sourceType, TypeElement targetType) {
List<? extends TypeParameterElement> sourceTypeParameters = sourceType.getTypeParameters();
List<? extends TypeParameterElement> targetTypeParameters = targetType.getTypeParameters();
Preconditions.checkArgument(sourceTypeParameters.toString().equals(targetTypeParameters.toString()), "%s != %s", sourceTypeParameters, targetTypeParameters);
// What we're doing is only valid if the type parameters are "the same". The check here even
// requires the names to be the same. The logic would still work without that, but we impose
// that requirement elsewhere and it means we can check in this simple way.
EclipseHack eclipseHack = new EclipseHack(elementUtils, typeUtils);
TypeMirror[] targetTypeParameterMirrors = new TypeMirror[targetTypeParameters.size()];
for (int i = 0; i < targetTypeParameters.size(); i++) {
targetTypeParameterMirrors[i] = targetTypeParameters.get(i).asType();
}
DeclaredType parallelSource = typeUtils.getDeclaredType(sourceType, targetTypeParameterMirrors);
return methods.stream().collect(toImmutableMap(m -> m, m -> eclipseHack.methodReturnType(m, parallelSource)));
}
Aggregations