Search in sources :

Example 56 with TypeParameterElement

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();
}
Also used : TypeParameterElement(javax.lang.model.element.TypeParameterElement) TypeMirror(javax.lang.model.type.TypeMirror) Parameterizable(javax.lang.model.element.Parameterizable) DeclaringType(org.immutables.value.processor.meta.Proto.DeclaringType)

Example 57 with TypeParameterElement

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;
}
Also used : TypeParameterElement(javax.lang.model.element.TypeParameterElement)

Example 58 with TypeParameterElement

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> {");
    }
}
Also used : RoundEnvironment(javax.annotation.processing.RoundEnvironment) JavaFileObject(javax.tools.JavaFileObject) ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) Compilation(com.google.testing.compile.Compilation) SupportedAnnotationTypes(javax.annotation.processing.SupportedAnnotationTypes) TypeElement(javax.lang.model.element.TypeElement) AbstractProcessor(javax.annotation.processing.AbstractProcessor) TypeParameterElement(javax.lang.model.element.TypeParameterElement) Test(org.junit.Test)

Example 59 with TypeParameterElement

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)));
}
Also used : VerifyException(com.google.common.base.VerifyException) MoreElements(com.google.auto.common.MoreElements) ImmutableSet(com.google.common.collect.ImmutableSet) MoreTypes(com.google.auto.common.MoreTypes) Equivalence(com.google.common.base.Equivalence) ImmutableMap(com.google.common.collect.ImmutableMap) ExecutableElement(javax.lang.model.element.ExecutableElement) VariableElement(javax.lang.model.element.VariableElement) MoreStreams.toImmutableMap(com.google.auto.common.MoreStreams.toImmutableMap) Element(javax.lang.model.element.Element) TypeElement(javax.lang.model.element.TypeElement) Types(javax.lang.model.util.Types) Function(java.util.function.Function) TypeParameterElement(javax.lang.model.element.TypeParameterElement) ImmutableBiMap(com.google.common.collect.ImmutableBiMap) LinkedHashMap(java.util.LinkedHashMap) MoreStreams.toImmutableBiMap(com.google.auto.common.MoreStreams.toImmutableBiMap) List(java.util.List) TypeMirror(javax.lang.model.type.TypeMirror) ImmutableList(com.google.common.collect.ImmutableList) Map(java.util.Map) ProcessingEnvironment(javax.annotation.processing.ProcessingEnvironment) Optional(java.util.Optional) TypeVariable(javax.lang.model.type.TypeVariable) TypeMirror(javax.lang.model.type.TypeMirror) TypeVariable(javax.lang.model.type.TypeVariable) TypeParameterElement(javax.lang.model.element.TypeParameterElement) LinkedHashMap(java.util.LinkedHashMap)

Example 60 with TypeParameterElement

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>
 * &#64;AutoValue class {@code Foo<T>} {
 *   abstract T getFoo();
 *
 *   &#64;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)));
}
Also used : ArrayType(javax.lang.model.type.ArrayType) MoreElements(com.google.auto.common.MoreElements) SimpleTypeVisitor8(javax.lang.model.util.SimpleTypeVisitor8) MoreTypes(com.google.auto.common.MoreTypes) Equivalence(com.google.common.base.Equivalence) ImmutableMap(com.google.common.collect.ImmutableMap) Modifier(javax.lang.model.element.Modifier) Collection(java.util.Collection) ExecutableElement(javax.lang.model.element.ExecutableElement) MoreStreams.toImmutableMap(com.google.auto.common.MoreStreams.toImmutableMap) TypeElement(javax.lang.model.element.TypeElement) Types(javax.lang.model.util.Types) Function(java.util.function.Function) Elements(javax.lang.model.util.Elements) TypeParameterElement(javax.lang.model.element.TypeParameterElement) TypeKind(javax.lang.model.type.TypeKind) LinkedHashMap(java.util.LinkedHashMap) List(java.util.List) TypeMirror(javax.lang.model.type.TypeMirror) Map(java.util.Map) DeclaredType(javax.lang.model.type.DeclaredType) TypeVariable(javax.lang.model.type.TypeVariable) Preconditions(com.google.common.base.Preconditions) WildcardType(javax.lang.model.type.WildcardType) TypeMirror(javax.lang.model.type.TypeMirror) DeclaredType(javax.lang.model.type.DeclaredType)

Aggregations

TypeParameterElement (javax.lang.model.element.TypeParameterElement)66 TypeElement (javax.lang.model.element.TypeElement)30 TypeMirror (javax.lang.model.type.TypeMirror)26 Test (org.junit.Test)18 ExecutableElement (javax.lang.model.element.ExecutableElement)13 TypeVariable (javax.lang.model.type.TypeVariable)13 Element (javax.lang.model.element.Element)10 VariableElement (javax.lang.model.element.VariableElement)9 ArrayList (java.util.ArrayList)7 LinkedHashMap (java.util.LinkedHashMap)7 List (java.util.List)7 DeclaredType (javax.lang.model.type.DeclaredType)7 MethodSpec (com.squareup.javapoet.MethodSpec)6 Map (java.util.Map)6 FieldSpec (com.squareup.javapoet.FieldSpec)5 HashSet (java.util.HashSet)4 Modifier (javax.lang.model.element.Modifier)4 ArrayType (javax.lang.model.type.ArrayType)4 Types (javax.lang.model.util.Types)4 MoreElements (com.google.auto.common.MoreElements)3