• 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.base.Strings;
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.collect.ImmutableMap;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.Objects;
27 import java.util.Optional;
28 import java.util.stream.Stream;
29 import javax.annotation.processing.ProcessingEnvironment;
30 import javax.lang.model.SourceVersion;
31 import javax.lang.model.element.AnnotationMirror;
32 import javax.lang.model.element.AnnotationValue;
33 import javax.lang.model.element.Element;
34 import javax.lang.model.element.ExecutableElement;
35 import javax.lang.model.type.ArrayType;
36 import javax.lang.model.type.DeclaredType;
37 import javax.lang.model.type.IntersectionType;
38 import javax.lang.model.type.TypeMirror;
39 import javax.lang.model.type.TypeVariable;
40 import javax.lang.model.type.WildcardType;
41 import javax.lang.model.util.SimpleTypeVisitor8;
42 
43 class Nullables {
44   /**
45    * If set to a non-empty string, defines which {@code @Nullable} type annotation should be used by
46    * default. If set to an empty string, does not insert {@code @Nullable} unless it is referenced
47    * in the {@code @AutoValue} methods. If unset, defaults to {@value #DEFAULT_NULLABLE}.
48    */
49   static final String NULLABLE_OPTION = "com.google.auto.value.NullableTypeAnnotation";
50 
51   // We write this using .concat in order to hide it from rewriting rules.
52   private static final String DEFAULT_NULLABLE = "org".concat(".jspecify.nullness.Nullable");
53 
54   private final Optional<AnnotationMirror> nullableTypeAnnotation;
55 
Nullables(Optional<AnnotationMirror> nullableTypeAnnotation)56   private Nullables(Optional<AnnotationMirror> nullableTypeAnnotation) {
57     this.nullableTypeAnnotation = nullableTypeAnnotation;
58   }
59 
60   /**
61    * Make an instance where the default {@code @Nullable} type annotation is discovered by looking
62    * for methods whose parameter or return types have such an annotation. If there are none, use a
63    * default {@code @Nullable} type annotation if it is available.
64    *
65    * @param methods the methods to examine
66    * @param processingEnv the {@link ProcessingEnvironment}, or null if one is unavailable
67    *     (typically in tests)
68    */
fromMethods( ProcessingEnvironment processingEnv, Collection<ExecutableElement> methods)69   static Nullables fromMethods(
70       /* @Nullable */ ProcessingEnvironment processingEnv, Collection<ExecutableElement> methods) {
71     Optional<AnnotationMirror> nullableTypeAnnotation =
72         methods.stream()
73             .flatMap(
74                 method ->
75                     Stream.concat(
76                         Stream.of(method.getReturnType()),
77                         method.getParameters().stream().map(Element::asType)))
78             .map(Nullables::nullableIn)
79             .filter(Optional::isPresent)
80             .findFirst()
81             .orElseGet(() -> defaultNullableTypeAnnotation(processingEnv));
82     return new Nullables(nullableTypeAnnotation);
83   }
84 
85   /**
86    * Returns a list that is either empty or contains a single element that is an appropriate
87    * {@code @Nullable} type-annotation.
88    */
nullableTypeAnnotations()89   ImmutableList<AnnotationMirror> nullableTypeAnnotations() {
90     return nullableTypeAnnotation.map(ImmutableList::of).orElse(ImmutableList.of());
91   }
92 
defaultNullableTypeAnnotation( ProcessingEnvironment processingEnv)93   private static Optional<AnnotationMirror> defaultNullableTypeAnnotation(
94       /* @Nullable */ ProcessingEnvironment processingEnv) {
95     if (processingEnv == null) {
96       return Optional.empty();
97     }
98     // -Afoo without `=` sets "foo" to null in the getOptions() map.
99     String nullableOption =
100         Strings.nullToEmpty(
101             processingEnv.getOptions().getOrDefault(NULLABLE_OPTION, DEFAULT_NULLABLE));
102     return (!nullableOption.isEmpty()
103             && processingEnv.getSourceVersion().ordinal() >= SourceVersion.RELEASE_8.ordinal())
104         ? Optional.ofNullable(processingEnv.getElementUtils().getTypeElement(nullableOption))
105             .map(t -> annotationMirrorOf(MoreTypes.asDeclared(t.asType())))
106         : Optional.empty();
107   }
108 
annotationMirrorOf(DeclaredType annotationType)109   private static AnnotationMirror annotationMirrorOf(DeclaredType annotationType) {
110     return new AnnotationMirror() {
111       @Override
112       public DeclaredType getAnnotationType() {
113         return annotationType;
114       }
115 
116       @Override
117       public ImmutableMap<? extends ExecutableElement, ? extends AnnotationValue>
118           getElementValues() {
119         return ImmutableMap.of();
120       }
121     };
122   }
123 
124   private static Optional<AnnotationMirror> nullableIn(TypeMirror type) {
125     return new NullableFinder().visit(type);
126   }
127 
128   private static Optional<AnnotationMirror> nullableIn(
129       List<? extends AnnotationMirror> annotations) {
130     return annotations.stream()
131         .filter(a -> a.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable"))
132         .map(a -> (AnnotationMirror) a) // get rid of the pesky wildcard
133         .findFirst();
134   }
135 
136   private static class NullableFinder extends SimpleTypeVisitor8<Optional<AnnotationMirror>, Void> {
137     private final TypeMirrorSet visiting = new TypeMirrorSet();
138 
139     NullableFinder() {
140       super(Optional.empty());
141     }
142 
143     // Primitives can't be @Nullable so we don't check that.
144 
145     @Override
146     public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void unused) {
147       if (!visiting.add(t)) {
148         return Optional.empty();
149       }
150       return nullableIn(t.getAnnotationMirrors())
151           .map(Optional::of)
152           .orElseGet(() -> visitAll(t.getTypeArguments()));
153     }
154 
155     @Override
156     public Optional<AnnotationMirror> visitTypeVariable(TypeVariable t, Void unused) {
157       if (!visiting.add(t)) {
158         return Optional.empty();
159       }
160       return nullableIn(t.getAnnotationMirrors())
161           .map(Optional::of)
162           .orElseGet(() -> visitAll(ImmutableList.of(t.getUpperBound(), t.getLowerBound())));
163     }
164 
165     @Override
166     public Optional<AnnotationMirror> visitArray(ArrayType t, Void unused) {
167       return nullableIn(t.getAnnotationMirrors())
168           .map(Optional::of)
169           .orElseGet(() -> visit(t.getComponentType()));
170     }
171 
172     @Override
173     public Optional<AnnotationMirror> visitWildcard(WildcardType t, Void unused) {
174       return nullableIn(t.getAnnotationMirrors())
175           .map(Optional::of)
176           .orElseGet(
177               () ->
178                   visitAll(
179                       Stream.of(t.getExtendsBound(), t.getSuperBound())
180                           .filter(Objects::nonNull)
181                           .collect(toList())));
182     }
183 
184     @Override
185     public Optional<AnnotationMirror> visitIntersection(IntersectionType t, Void unused) {
186       return nullableIn(t.getAnnotationMirrors())
187           .map(Optional::of)
188           .orElseGet(() -> visitAll(t.getBounds()));
189     }
190 
191     private Optional<AnnotationMirror> visitAll(List<? extends TypeMirror> types) {
192       return types.stream()
193           .map(this::visit)
194           .filter(Optional::isPresent)
195           .findFirst()
196           .orElse(Optional.empty());
197     }
198   }
199 }
200