Skip to content

Commit

Permalink
Create com.uber.nullaway.generics package (#855)
Browse files Browse the repository at this point in the history
Fixes #817.  This is a refactoring with no semantic changes.
  • Loading branch information
msridhar authored Nov 15, 2023
1 parent 4af912d commit 60648a9
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 228 deletions.
2 changes: 1 addition & 1 deletion nullaway/src/main/java/com/uber/nullaway/ErrorBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public class ErrorBuilder {
* expression into a @NonNull target, and this parameter is the Symbol for that target.
* @return the error description
*/
Description createErrorDescription(
public Description createErrorDescription(
ErrorMessage errorMessage,
Description.Builder descriptionBuilder,
VisitorState state,
Expand Down
1 change: 1 addition & 0 deletions nullaway/src/main/java/com/uber/nullaway/NullAway.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
import com.uber.nullaway.ErrorMessage.MessageTypes;
import com.uber.nullaway.dataflow.AccessPathNullnessAnalysis;
import com.uber.nullaway.dataflow.EnclosingEnvironmentNullness;
import com.uber.nullaway.generics.GenericsChecks;
import com.uber.nullaway.handlers.Handler;
import com.uber.nullaway.handlers.Handlers;
import java.util.ArrayList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
import com.sun.tools.javac.code.TypeTag;
import com.uber.nullaway.CodeAnnotationInfo;
import com.uber.nullaway.Config;
import com.uber.nullaway.GenericsChecks;
import com.uber.nullaway.NullabilityUtil;
import com.uber.nullaway.Nullness;
import com.uber.nullaway.generics.GenericsChecks;
import com.uber.nullaway.handlers.Handler;
import com.uber.nullaway.handlers.Handler.NullnessHint;
import java.util.HashMap;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.uber.nullaway.generics;

import com.google.errorprone.VisitorState;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Types;
import java.util.List;

/**
* Visitor that checks equality of nullability annotations for all nested generic type arguments
* within a type. Compares the Type it is called upon, i.e. the LHS type and the Type passed as an
* argument, i.e. The RHS type.
*/
public class CompareNullabilityVisitor extends Types.DefaultTypeVisitor<Boolean, Type> {
private final VisitorState state;

CompareNullabilityVisitor(VisitorState state) {
this.state = state;
}

@Override
public Boolean visitClassType(Type.ClassType lhsType, Type rhsType) {
Types types = state.getTypes();
// The base type of rhsType may be a subtype of lhsType's base type. In such cases, we must
// compare lhsType against the supertype of rhsType with a matching base type.
rhsType = (Type.ClassType) types.asSuper(rhsType, lhsType.tsym);
// This is impossible, considering the fact that standard Java subtyping succeeds before
// running NullAway
if (rhsType == null) {
throw new RuntimeException("Did not find supertype of " + rhsType + " matching " + lhsType);
}
List<Type> lhsTypeArguments = lhsType.getTypeArguments();
List<Type> rhsTypeArguments = rhsType.getTypeArguments();
// This is impossible, considering the fact that standard Java subtyping succeeds before
// running NullAway
if (lhsTypeArguments.size() != rhsTypeArguments.size()) {
throw new RuntimeException(
"Number of types arguments in " + rhsType + " does not match " + lhsType);
}
for (int i = 0; i < lhsTypeArguments.size(); i++) {
Type lhsTypeArgument = lhsTypeArguments.get(i);
Type rhsTypeArgument = rhsTypeArguments.get(i);
boolean isLHSNullableAnnotated = false;
List<Attribute.TypeCompound> lhsAnnotations = lhsTypeArgument.getAnnotationMirrors();
// To ensure that we are checking only jspecify nullable annotations
for (Attribute.TypeCompound annotation : lhsAnnotations) {
if (annotation.getAnnotationType().toString().equals(GenericsChecks.NULLABLE_NAME)) {
isLHSNullableAnnotated = true;
break;
}
}
boolean isRHSNullableAnnotated = false;
List<Attribute.TypeCompound> rhsAnnotations = rhsTypeArgument.getAnnotationMirrors();
// To ensure that we are checking only jspecify nullable annotations
for (Attribute.TypeCompound annotation : rhsAnnotations) {
if (annotation.getAnnotationType().toString().equals(GenericsChecks.NULLABLE_NAME)) {
isRHSNullableAnnotated = true;
break;
}
}
if (isLHSNullableAnnotated != isRHSNullableAnnotated) {
return false;
}
// nested generics
if (!lhsTypeArgument.accept(this, rhsTypeArgument)) {
return false;
}
}
// If there is an enclosing type (for non-static inner classes), its type argument nullability
// should also match. When there is no enclosing type, getEnclosingType() returns a NoType
// object, which gets handled by the fallback visitType() method
return lhsType.getEnclosingType().accept(this, rhsType.getEnclosingType());
}

@Override
public Boolean visitArrayType(Type.ArrayType lhsType, Type rhsType) {
Type.ArrayType arrRhsType = (Type.ArrayType) rhsType;
return lhsType.getComponentType().accept(this, arrRhsType.getComponentType());
}

@Override
public Boolean visitType(Type t, Type type) {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.uber.nullaway.generics;

import static java.util.stream.Collectors.joining;

import com.google.errorprone.VisitorState;
import com.google.errorprone.util.ASTHelpers;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Types;

/**
* A visitor that pretty prints a generic type including its type-use nullability annotations, for
* use in error messages.
*
* <p>This code is a modified and extended version of code in {@link
* com.google.errorprone.util.Signatures}
*/
final class GenericTypePrettyPrintingVisitor extends Types.DefaultTypeVisitor<String, Void> {

private final VisitorState state;

GenericTypePrettyPrintingVisitor(VisitorState state) {
this.state = state;
}

@Override
public String visitWildcardType(Type.WildcardType t, Void unused) {
// NOTE: we have not tested this code yet as we do not yet support wildcard types
StringBuilder sb = new StringBuilder();
sb.append(t.kind);
if (t.kind != BoundKind.UNBOUND) {
sb.append(t.type.accept(this, null));
}
return sb.toString();
}

@Override
public String visitClassType(Type.ClassType t, Void s) {
StringBuilder sb = new StringBuilder();
Type enclosingType = t.getEnclosingType();
if (!ASTHelpers.isSameType(enclosingType, Type.noType, state)) {
sb.append(enclosingType.accept(this, null)).append('.');
}
for (Attribute.TypeCompound compound : t.getAnnotationMirrors()) {
sb.append('@');
sb.append(compound.type.accept(this, null));
sb.append(' ');
}
sb.append(t.tsym.getSimpleName());
if (t.getTypeArguments().nonEmpty()) {
sb.append('<');
sb.append(
t.getTypeArguments().stream().map(a -> a.accept(this, null)).collect(joining(", ")));
sb.append(">");
}
return sb.toString();
}

@Override
public String visitCapturedType(Type.CapturedType t, Void s) {
return t.wildcard.accept(this, null);
}

@Override
public String visitArrayType(Type.ArrayType t, Void unused) {
// TODO properly print cases like int @Nullable[]
return t.elemtype.accept(this, null) + "[]";
}

@Override
public String visitType(Type t, Void s) {
return t.toString();
}
}
Loading

0 comments on commit 60648a9

Please sign in to comment.