• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.auto.value.processor;
17 
18 import static java.util.stream.Collectors.toList;
19 
20 import com.google.auto.common.MoreTypes;
21 import com.google.common.annotations.VisibleForTesting;
22 import com.google.common.base.Strings;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableMap;
25 import java.util.Collection;
26 import java.util.List;
27 import java.util.Objects;
28 import java.util.Optional;
29 import java.util.stream.Stream;
30 import javax.annotation.processing.ProcessingEnvironment;
31 import javax.lang.model.SourceVersion;
32 import javax.lang.model.element.AnnotationMirror;
33 import javax.lang.model.element.AnnotationValue;
34 import javax.lang.model.element.Element;
35 import javax.lang.model.element.ExecutableElement;
36 import javax.lang.model.type.ArrayType;
37 import javax.lang.model.type.DeclaredType;
38 import javax.lang.model.type.IntersectionType;
39 import javax.lang.model.type.TypeMirror;
40 import javax.lang.model.type.TypeVariable;
41 import javax.lang.model.type.WildcardType;
42 import javax.lang.model.util.SimpleTypeVisitor8;
43 
44 class Nullables {
45   /**
46    * If set to a non-empty string, defines which {@code @Nullable} type annotation should be used by
47    * default. If set to an empty string, does not insert {@code @Nullable} unless it is referenced
48    * in the {@code @AutoValue} methods. If unset, defaults to {@value #DEFAULT_NULLABLE}.
49    */
50   static final String NULLABLE_OPTION = "com.google.auto.value.NullableTypeAnnotation";
51 
52   // We write this using .concat in order to hide it from rewriting rules.
53   private static final String DEFAULT_NULLABLE = "org".concat(".jspecify.nullness.Nullable");
54 
55   private final Optional<AnnotationMirror> defaultNullable;
56 
Nullables(ProcessingEnvironment processingEnv)57   Nullables(ProcessingEnvironment processingEnv) {
58     // -Afoo without `=` sets "foo" to null in the getOptions() map.
59     String nullableOption =
60         Strings.nullToEmpty(
61             processingEnv.getOptions().getOrDefault(NULLABLE_OPTION, DEFAULT_NULLABLE));
62     this.defaultNullable =
63         (!nullableOption.isEmpty()
64                 && processingEnv.getSourceVersion().ordinal() >= SourceVersion.RELEASE_8.ordinal())
65             ? Optional.ofNullable(processingEnv.getElementUtils().getTypeElement(nullableOption))
66                 .map(t -> annotationMirrorOf(MoreTypes.asDeclared(t.asType())))
67             : Optional.empty();
68   }
69 
annotationMirrorOf(DeclaredType annotationType)70   private static AnnotationMirror annotationMirrorOf(DeclaredType annotationType) {
71     return new AnnotationMirror() {
72       @Override
73       public DeclaredType getAnnotationType() {
74         return annotationType;
75       }
76 
77       @Override
78       public ImmutableMap<? extends ExecutableElement, ? extends AnnotationValue>
79           getElementValues() {
80         return ImmutableMap.of();
81       }
82     };
83   }
84 
85   /**
86    * Returns the type of a {@code @Nullable} type-annotation, if one is found anywhere in the
87    * signatures of the given methods.
88    */
89   @VisibleForTesting
90   static Optional<AnnotationMirror> nullableMentionedInMethods(
91       Collection<ExecutableElement> methods) {
92     return methods.stream()
93         .flatMap(
94             method ->
95                 Stream.concat(
96                     Stream.of(method.getReturnType()),
97                     method.getParameters().stream().map(Element::asType)))
98         .map(Nullables::nullableIn)
99         .filter(Optional::isPresent)
100         .findFirst()
101         .orElse(Optional.empty());
102   }
103 
104   /**
105    * Returns the type of an appropriate {@code @Nullable} type-annotation, given a set of methods
106    * that are known to be in the same compilation as the code being generated. If one of those
107    * methods contains an appropriate {@code @Nullable} annotation on a parameter or return type,
108    * this method will return that. Otherwise, if the <a href="http://jspecify.org">JSpecify</a>
109    * {@code @Nullable} is available, this method will return it. Otherwise, this method will return
110    * an empty {@code Optional}.
111    */
112   Optional<AnnotationMirror> appropriateNullableGivenMethods(
113       Collection<ExecutableElement> methods) {
114     return nullableMentionedInMethods(methods).map(Optional::of).orElse(defaultNullable);
115   }
116 
117   private static Optional<AnnotationMirror> nullableIn(TypeMirror type) {
118     return new NullableFinder().visit(type);
119   }
120 
121   private static Optional<AnnotationMirror> nullableIn(
122       List<? extends AnnotationMirror> annotations) {
123     return annotations.stream()
124         .filter(a -> a.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable"))
125         .map(a -> (AnnotationMirror) a) // get rid of the pesky wildcard
126         .findFirst();
127   }
128 
129   private static class NullableFinder extends SimpleTypeVisitor8<Optional<AnnotationMirror>, Void> {
130     private final TypeMirrorSet visiting = new TypeMirrorSet();
131 
132     NullableFinder() {
133       super(Optional.empty());
134     }
135 
136     // Primitives can't be @Nullable so we don't check that.
137 
138     @Override
139     public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void unused) {
140       if (!visiting.add(t)) {
141         return Optional.empty();
142       }
143       return nullableIn(t.getAnnotationMirrors())
144           .map(Optional::of)
145           .orElseGet(() -> visitAll(t.getTypeArguments()));
146     }
147 
148     @Override
149     public Optional<AnnotationMirror> visitTypeVariable(TypeVariable t, Void unused) {
150       if (!visiting.add(t)) {
151         return Optional.empty();
152       }
153       return nullableIn(t.getAnnotationMirrors())
154           .map(Optional::of)
155           .orElseGet(() -> visitAll(ImmutableList.of(t.getUpperBound(), t.getLowerBound())));
156     }
157 
158     @Override
159     public Optional<AnnotationMirror> visitArray(ArrayType t, Void unused) {
160       return nullableIn(t.getAnnotationMirrors())
161           .map(Optional::of)
162           .orElseGet(() -> visit(t.getComponentType()));
163     }
164 
165     @Override
166     public Optional<AnnotationMirror> visitWildcard(WildcardType t, Void unused) {
167       return nullableIn(t.getAnnotationMirrors())
168           .map(Optional::of)
169           .orElseGet(
170               () ->
171                   visitAll(
172                       Stream.of(t.getExtendsBound(), t.getSuperBound())
173                           .filter(Objects::nonNull)
174                           .collect(toList())));
175     }
176 
177     @Override
178     public Optional<AnnotationMirror> visitIntersection(IntersectionType t, Void unused) {
179       return nullableIn(t.getAnnotationMirrors())
180           .map(Optional::of)
181           .orElseGet(() -> visitAll(t.getBounds()));
182     }
183 
184     private Optional<AnnotationMirror> visitAll(List<? extends TypeMirror> types) {
185       return types.stream()
186           .map(this::visit)
187           .filter(Optional::isPresent)
188           .findFirst()
189           .orElse(Optional.empty());
190     }
191   }
192 }
193