Skip to content

Commit

Permalink
Annotate auto-common for nullness.
Browse files Browse the repository at this point in the history
RELNOTES=`com.google.auto.common` is annotated for null-safety.
PiperOrigin-RevId: 382311770
  • Loading branch information
eamonnmcmanus authored and Google Java Core Libraries committed Jun 30, 2021
1 parent 2ee7f62 commit 9d79ce1
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,11 @@ public static Map.Entry<ExecutableElement, AnnotationValue> getAnnotationElement
*/
public static ImmutableSet<? extends AnnotationMirror> getAnnotatedAnnotations(
Element element, Class<? extends Annotation> annotationClass) {
return getAnnotatedAnnotations(element, annotationClass.getCanonicalName());
String name = annotationClass.getCanonicalName();
if (name == null) {
return ImmutableSet.of();
}
return getAnnotatedAnnotations(element, name);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Multimaps.filterKeys;
import static java.util.Objects.requireNonNull;
import static javax.lang.model.element.ElementKind.PACKAGE;
import static javax.tools.Diagnostic.Kind.ERROR;

Expand Down Expand Up @@ -55,6 +56,7 @@
import javax.lang.model.type.ErrorType;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleElementVisitor8;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* An abstract {@link Processor} implementation that defers processing of {@link Element}s to later
Expand Down Expand Up @@ -286,10 +288,7 @@ private ImmutableSetMultimap<TypeElement, Element> validElements(RoundEnvironmen

// Look at the elements we've found and the new elements from this round and validate them.
for (TypeElement annotationType : getSupportedAnnotationTypeElements()) {
Set<? extends Element> roundElements =
(annotationType == null)
? ImmutableSet.of()
: roundEnv.getElementsAnnotatedWith(annotationType);
Set<? extends Element> roundElements = roundEnv.getElementsAnnotatedWith(annotationType);
ImmutableSet<Element> prevRoundElements = deferredElementsByAnnotation.get(annotationType);
for (Element element : Sets.union(roundElements, prevRoundElements)) {
ElementName elementName = ElementName.forAnnotatedElement(element);
Expand Down Expand Up @@ -478,7 +477,8 @@ private static class ProcessingStepAsStep implements Step {
processingStep.annotations().stream()
.collect(
toImmutableMap(
Class::getCanonicalName, (Class<? extends Annotation> aClass) -> aClass));
c -> requireNonNull(c.getCanonicalName()),
(Class<? extends Annotation> aClass) -> aClass));
}

@Override
Expand All @@ -499,8 +499,12 @@ private ImmutableSetMultimap<Class<? extends Annotation>, Element> toClassKeyedM
elements
.asMap()
.forEach(
(annotation, annotatedElements) ->
builder.putAll(annotationsByName.get(annotation), annotatedElements));
(annotationName, annotatedElements) -> {
Class<? extends Annotation> annotation = annotationsByName.get(annotationName);
if (annotation != null) { // should not be null
builder.putAll(annotation, annotatedElements);
}
});
return builder.build();
}
}
Expand Down Expand Up @@ -558,7 +562,7 @@ Optional<? extends Element> getElement(Elements elements) {
}

@Override
public boolean equals(Object object) {
public boolean equals(@Nullable Object object) {
if (!(object instanceof ElementName)) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,11 @@ public static boolean isAnnotationPresent(Element element, String annotationName
*/
public static Optional<AnnotationMirror> getAnnotationMirror(
Element element, Class<? extends Annotation> annotationClass) {
return getAnnotationMirror(element, annotationClass.getCanonicalName());
String name = annotationClass.getCanonicalName();
if (name == null) {
return Optional.absent();
}
return getAnnotationMirror(element, name);
}

/**
Expand Down
31 changes: 20 additions & 11 deletions common/src/main/java/com/google/auto/common/MoreTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import static javax.lang.model.type.TypeKind.WILDCARD;

import com.google.common.base.Equivalence;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
Expand Down Expand Up @@ -55,6 +54,7 @@
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor8;
import javax.lang.model.util.Types;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Utilities related to {@link TypeMirror} instances.
Expand Down Expand Up @@ -140,7 +140,7 @@ private static class ComparedElements {
}

@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (o instanceof ComparedElements) {
ComparedElements that = (ComparedElements) o;
int nArguments = aArguments.size();
Expand Down Expand Up @@ -297,21 +297,30 @@ private Set<ComparedElements> visitingSetPlus(
}

@SuppressWarnings("TypeEquals")
private static boolean equal(TypeMirror a, TypeMirror b, Set<ComparedElements> visiting) {
private static boolean equal(
@Nullable TypeMirror a, @Nullable TypeMirror b, Set<ComparedElements> visiting) {
if (a == b) {
return true;
}
if (a == null || b == null) {
return false;
}
// TypeMirror.equals is not guaranteed to return true for types that are equal, but we can
// assume that if it does return true then the types are equal. This check also avoids getting
// stuck in infinite recursion when Eclipse decrees that the upper bound of the second K in
// <K extends Comparable<K>> is a distinct but equal K.
// The javac implementation of ExecutableType, at least in some versions, does not take thrown
// exceptions into account in its equals implementation, so avoid this optimization for
// ExecutableType.
if (Objects.equal(a, b) && !(a instanceof ExecutableType)) {
@SuppressWarnings("TypesEquals")
boolean equal = a.equals(b);
if (equal && !(a instanceof ExecutableType)) {
return true;
}
EqualVisitorParam p = new EqualVisitorParam();
p.type = b;
p.visiting = visiting;
return (a == b) || (a != null && b != null && a.accept(EqualVisitor.INSTANCE, p));
return a.accept(EqualVisitor.INSTANCE, p);
}

/**
Expand All @@ -321,7 +330,7 @@ private static boolean equal(TypeMirror a, TypeMirror b, Set<ComparedElements> v
* <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=508222">this bug</a> whereby
* the Eclipse compiler returns a value for static classes that is not NoType.
*/
private static TypeMirror enclosingType(DeclaredType t) {
private static @Nullable TypeMirror enclosingType(DeclaredType t) {
TypeMirror enclosing = t.getEnclosingType();
if (enclosing.getKind().equals(TypeKind.NONE)
|| t.asElement().getModifiers().contains(Modifier.STATIC)) {
Expand Down Expand Up @@ -461,17 +470,17 @@ public static ImmutableSet<TypeElement> referencedTypes(TypeMirror type) {
}

private static final class ReferencedTypes
extends SimpleTypeVisitor8<Void, ImmutableSet.Builder<TypeElement>> {
extends SimpleTypeVisitor8<@Nullable Void, ImmutableSet.Builder<TypeElement>> {
private static final ReferencedTypes INSTANCE = new ReferencedTypes();

@Override
public Void visitArray(ArrayType t, ImmutableSet.Builder<TypeElement> p) {
public @Nullable Void visitArray(ArrayType t, ImmutableSet.Builder<TypeElement> p) {
t.getComponentType().accept(this, p);
return null;
}

@Override
public Void visitDeclared(DeclaredType t, ImmutableSet.Builder<TypeElement> p) {
public @Nullable Void visitDeclared(DeclaredType t, ImmutableSet.Builder<TypeElement> p) {
p.add(MoreElements.asType(t.asElement()));
for (TypeMirror typeArgument : t.getTypeArguments()) {
typeArgument.accept(this, p);
Expand All @@ -480,14 +489,14 @@ public Void visitDeclared(DeclaredType t, ImmutableSet.Builder<TypeElement> p) {
}

@Override
public Void visitTypeVariable(TypeVariable t, ImmutableSet.Builder<TypeElement> p) {
public @Nullable Void visitTypeVariable(TypeVariable t, ImmutableSet.Builder<TypeElement> p) {
t.getLowerBound().accept(this, p);
t.getUpperBound().accept(this, p);
return null;
}

@Override
public Void visitWildcard(WildcardType t, ImmutableSet.Builder<TypeElement> p) {
public @Nullable Void visitWildcard(WildcardType t, ImmutableSet.Builder<TypeElement> p) {
TypeMirror extendsBound = t.getExtendsBound();
if (extendsBound != null) {
extendsBound.accept(this, p);
Expand Down
16 changes: 11 additions & 5 deletions common/src/main/java/com/google/auto/common/Overrides.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor8;
import javax.lang.model.util.Types;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Determines if one method overrides another. This class defines two ways of doing that:
Expand Down Expand Up @@ -142,7 +143,8 @@ public boolean overrides(
// the enclosing elements rather than the methods themselves for the reason described
// at the start of the method.
ExecutableElement inherited = methodFromSuperclasses(in, overridden);
return !overridden.getEnclosingElement().equals(inherited.getEnclosingElement());
return inherited != null
&& !overridden.getEnclosingElement().equals(inherited.getEnclosingElement());
} else if (overriddenType.getKind().isInterface()) {
// ...overrides from C another method mI declared in interface I. We've already checked
// the conditions (assuming that the only alternative to mI being abstract or default is
Expand All @@ -158,7 +160,8 @@ public boolean overrides(
// to methodFromSuperclasses above.
if (overrider.getModifiers().contains(Modifier.ABSTRACT)) {
ExecutableElement inherited = methodFromSuperinterfaces(in, overridden);
return !overridden.getEnclosingElement().equals(inherited.getEnclosingElement());
return inherited != null
&& !overridden.getEnclosingElement().equals(inherited.getEnclosingElement());
} else {
return true;
}
Expand Down Expand Up @@ -216,6 +219,7 @@ private boolean isSubsignature(
* implements List<E>}. The parameter types are erased since the purpose of this method is to
* determine whether two methods are candidates for one to override the other.
*/
@Nullable
ImmutableList<TypeMirror> erasedParameterTypes(ExecutableElement method, TypeElement in) {
if (method.getParameters().isEmpty()) {
return ImmutableList.of();
Expand All @@ -242,6 +246,7 @@ private class TypeSubstVisitor extends SimpleTypeVisitor8<TypeMirror, Void> {
*/
private final Map<TypeParameterElement, TypeMirror> typeBindings = Maps.newLinkedHashMap();

@Nullable
ImmutableList<TypeMirror> erasedParameterTypes(ExecutableElement method, TypeElement in) {
if (method.getEnclosingElement().equals(in)) {
ImmutableList.Builder<TypeMirror> params = ImmutableList.builder();
Expand Down Expand Up @@ -320,7 +325,7 @@ public TypeMirror visitArray(ArrayType t, Void p) {
* or the nearest override in a superclass of the given type, or null if the method is not
* found in the given type or any of its superclasses.
*/
ExecutableElement methodFromSuperclasses(TypeElement in, ExecutableElement method) {
@Nullable ExecutableElement methodFromSuperclasses(TypeElement in, ExecutableElement method) {
for (TypeElement t = in; t != null; t = superclass(t)) {
ExecutableElement tMethod = methodInType(t, method);
if (tMethod != null) {
Expand All @@ -335,6 +340,7 @@ ExecutableElement methodFromSuperclasses(TypeElement in, ExecutableElement metho
* itself, or the nearest override in a superinterface of the given type, or null if the method
* is not found in the given type or any of its transitive superinterfaces.
*/
@Nullable
ExecutableElement methodFromSuperinterfaces(TypeElement in, ExecutableElement method) {
TypeElement methodContainer = MoreElements.asType(method.getEnclosingElement());
Preconditions.checkArgument(methodContainer.getKind().isInterface());
Expand Down Expand Up @@ -371,7 +377,7 @@ ExecutableElement methodFromSuperinterfaces(TypeElement in, ExecutableElement me
* Returns the method from within the given type that has the same erased signature as the given
* method, or null if there is no such method.
*/
private ExecutableElement methodInType(TypeElement type, ExecutableElement method) {
private @Nullable ExecutableElement methodInType(TypeElement type, ExecutableElement method) {
int nParams = method.getParameters().size();
List<TypeMirror> params = erasedParameterTypes(method, type);
if (params == null) {
Expand All @@ -393,7 +399,7 @@ private ExecutableElement methodInType(TypeElement type, ExecutableElement metho
return null;
}

private TypeElement superclass(TypeElement type) {
private @Nullable TypeElement superclass(TypeElement type) {
TypeMirror sup = type.getSuperclass();
if (sup.getKind() == TypeKind.DECLARED) {
return MoreElements.asType(typeUtils.asElement(sup));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* A simple implementation of the {@link AnnotationMirror} interface.
Expand Down Expand Up @@ -122,7 +123,7 @@ public String toString() {
}

@Override
public boolean equals(Object other) {
public boolean equals(@Nullable Object other) {
return other instanceof AnnotationMirror
&& AnnotationMirrors.equivalence().equivalent(this, (AnnotationMirror) other);
}
Expand Down
3 changes: 2 additions & 1 deletion common/src/main/java/com/google/auto/common/Visibility.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Represents the visibility of a given {@link Element}: {@code public}, {@code protected},
Expand All @@ -41,7 +42,7 @@ public enum Visibility {

// TODO(ronshapiro): remove this and reference ElementKind.MODULE directly once we start building
// with -source 9
private static final ElementKind MODULE =
private static final @Nullable ElementKind MODULE =
Enums.getIfPresent(ElementKind.class, "MODULE").orNull();

/**
Expand Down
17 changes: 17 additions & 0 deletions common/src/main/java/com/google/auto/common/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2021 Google LLC
*
* 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.auto.common;

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static org.junit.Assume.assumeTrue;

import com.google.common.collect.ImmutableList;
Expand All @@ -31,6 +32,7 @@
import java.lang.reflect.Method;
import java.net.URI;
import java.nio.file.Files;
import java.util.Objects;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
Expand All @@ -44,6 +46,7 @@
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
Expand Down Expand Up @@ -99,12 +102,12 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
* Run {@link TestProcessor} in a compilation with the given {@code options}, and prevent the
* compilation from accessing classes with the qualified names in {@code maskFromClasspath}.
*/
private String runProcessor(ImmutableList<String> options, String packageToMask)
private String runProcessor(ImmutableList<String> options, @Nullable String packageToMask)
throws IOException {
File tempDir = temporaryFolder.newFolder();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardFileManager =
compiler.getStandardFileManager(/* diagnostics= */ null, /* locale= */ null, UTF_8);
compiler.getStandardFileManager(/* diagnosticListener= */ null, /* locale= */ null, UTF_8);
standardFileManager.setLocation(StandardLocation.CLASS_OUTPUT, ImmutableList.of(tempDir));
StandardJavaFileManager proxyFileManager =
Reflection.newProxy(
Expand Down Expand Up @@ -142,18 +145,20 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors)
*/
private static class FileManagerInvocationHandler implements InvocationHandler {
private final StandardJavaFileManager fileManager;
private final String packageToMask;
private final @Nullable String packageToMask;

FileManagerInvocationHandler(StandardJavaFileManager fileManager, String packageToMask) {
FileManagerInvocationHandler(
StandardJavaFileManager fileManager, @Nullable String packageToMask) {
this.fileManager = fileManager;
this.packageToMask = packageToMask;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
public Object invoke(Object proxy, Method method, @Nullable Object @Nullable [] args)
throws Throwable {
if (method.getName().equals("list")) {
String packageName = (String) args[1];
if (packageName.equals(packageToMask)) {
String packageName = (String) requireNonNull(args)[1];
if (Objects.equals(packageName, packageToMask)) {
return ImmutableList.of();
}
}
Expand Down
Loading

0 comments on commit 9d79ce1

Please sign in to comment.