Skip to content

Commit

Permalink
[J2KT] Report INFO in InsertCastsOnNullabilityMismatch pass when …
Browse files Browse the repository at this point in the history
…a cast is inserted due to nullability mismatch, with source and target type descriptors. The description contains information about resolved nullability and captures.

PiperOrigin-RevId: 703274571
  • Loading branch information
Googler authored and copybara-github committed Dec 5, 2024
1 parent 0644357 commit 47e2117
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 0 deletions.
5 changes: 5 additions & 0 deletions transpiler/java/com/google/j2cl/common/Problems.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ private void problem(Severity severity, String message) {
problemsBySeverity.put(severity, message);
}

@FormatMethod
public void info(SourcePosition sourcePosition, String detailMessage, Object... args) {
problem(Severity.INFO, sourcePosition, detailMessage, args);
}

@FormatMethod
public void info(String detailMessage, Object... args) {
problem(Severity.INFO, String.format(detailMessage, args));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.google.j2cl.transpiler.passes;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.stream.Collectors.joining;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
Expand All @@ -26,12 +27,15 @@
import com.google.j2cl.transpiler.ast.DeclaredTypeDescriptor;
import com.google.j2cl.transpiler.ast.Expression;
import com.google.j2cl.transpiler.ast.IntersectionTypeDescriptor;
import com.google.j2cl.transpiler.ast.NullabilityAnnotation;
import com.google.j2cl.transpiler.ast.PrimitiveTypeDescriptor;
import com.google.j2cl.transpiler.ast.TypeDescriptor;
import com.google.j2cl.transpiler.ast.TypeDescriptors;
import com.google.j2cl.transpiler.ast.TypeVariable;
import com.google.j2cl.transpiler.ast.UnionTypeDescriptor;
import com.google.j2cl.transpiler.passes.ConversionContextVisitor.ContextRewriter;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;

/** Inserts casts in places where necessary due to nullability differences in type arguments. */
Expand Down Expand Up @@ -68,6 +72,14 @@ public Expression rewriteTypeConversionContext(
return expression;
}

Describer describer = new Describer();
getProblems()
.info(
getSourcePosition(),
"Inserted nullability mismatch cast from '%s' to '%s'",
describer.getDescription(expression.getTypeDescriptor()),
describer.getDescription(inferredTypeDescriptor));

return CastExpression.newBuilder()
.setExpression(expression)
.setCastTypeDescriptor(castTypeDescriptor)
Expand Down Expand Up @@ -276,4 +288,144 @@ private enum Variance {
OUT,
IN_OUT
}

/**
* Produces readable description string for type descriptors, containing information about
* resolved nullability and captures with unique IDs.
*/
// TODO(b/382500942): Remove when no longer needed for debugging / development.
private static final class Describer {
private final List<TypeVariable> seenCaptures = new ArrayList<>();

@Nullable
private static String getDescription(NullabilityAnnotation nullabilityAnnotation) {
switch (nullabilityAnnotation) {
case NULLABLE:
return "@Nullable";
case NONE:
return null;
case NOT_NULLABLE:
return "@NonNull";
}
throw new AssertionError();
}

private String getDescription(TypeDescriptor typeDescriptor) {
return getDescription(typeDescriptor, ImmutableList.of());
}

private String getDescription(
TypeDescriptor typeDescriptor, ImmutableList<TypeVariable> enclosingWildcardOrCaptures) {
if (typeDescriptor instanceof PrimitiveTypeDescriptor) {
PrimitiveTypeDescriptor primitiveTypeDescriptor = (PrimitiveTypeDescriptor) typeDescriptor;
return primitiveTypeDescriptor.getSimpleSourceName();
} else if (typeDescriptor instanceof ArrayTypeDescriptor) {
ArrayTypeDescriptor arrayTypeDescriptor = (ArrayTypeDescriptor) typeDescriptor;
return getDescription(
arrayTypeDescriptor.getComponentTypeDescriptor(), enclosingWildcardOrCaptures)
+ getDescriptionInfix(getNullabilityAnnotation(typeDescriptor))
+ "[]";
} else if (typeDescriptor instanceof DeclaredTypeDescriptor) {
DeclaredTypeDescriptor declaredTypeDescriptor = (DeclaredTypeDescriptor) typeDescriptor;
return getDescriptionPrefix(getNullabilityAnnotation(typeDescriptor))
+ declaredTypeDescriptor.getTypeDeclaration().getReadableDescription()
+ getTypeArgumentsDescription(declaredTypeDescriptor, enclosingWildcardOrCaptures);
} else if (typeDescriptor instanceof TypeVariable) {
TypeVariable typeVariable = (TypeVariable) typeDescriptor;
return getDescriptionPrefix(getNullabilityAnnotation(typeDescriptor))
+ getDescriptionWithoutNullabilityAnnotation(typeVariable, enclosingWildcardOrCaptures);
} else if (typeDescriptor instanceof IntersectionTypeDescriptor) {
IntersectionTypeDescriptor intersectionTypeDescriptor =
(IntersectionTypeDescriptor) typeDescriptor;
return intersectionTypeDescriptor.getIntersectionTypeDescriptors().stream()
.map(it -> getDescription(it, enclosingWildcardOrCaptures))
.collect(joining(" & ", "(", ")"));
} else if (typeDescriptor instanceof UnionTypeDescriptor) {
UnionTypeDescriptor unionTypeDescriptor = (UnionTypeDescriptor) typeDescriptor;
return unionTypeDescriptor.getUnionTypeDescriptors().stream()
.map(it -> getDescription(it, enclosingWildcardOrCaptures))
.collect(joining(" | ", "(", ")"));
} else {
throw new AssertionError();
}
}

private String getTypeArgumentsDescription(
DeclaredTypeDescriptor declaredTypeDescriptor,
ImmutableList<TypeVariable> enclosingWildcardOrCaptures) {
ImmutableList<TypeDescriptor> arguments = declaredTypeDescriptor.getTypeArgumentDescriptors();
if (arguments.isEmpty()) {
return "";
} else {
return arguments.stream()
.map(it -> getDescription(it, enclosingWildcardOrCaptures))
.collect(joining(", ", "<", ">"));
}
}

private String getDescriptionWithoutNullabilityAnnotation(
TypeVariable typeVariable, ImmutableList<TypeVariable> enclosingWildcardOrCapture) {
if (!typeVariable.isWildcardOrCapture()) {
return typeVariable.getName();
} else {
return getCaptureDescription(typeVariable)
+ getBoundDescription(typeVariable, enclosingWildcardOrCapture);
}
}

private String getCaptureDescription(TypeVariable typeVariable) {
if (!typeVariable.isCapture()) {
return "";
}

int index = seenCaptures.indexOf(typeVariable);
if (index == -1) {
index = seenCaptures.size();
seenCaptures.add(typeVariable);
}
return "capture#" + (index + 1) + "-of ";
}

private String getBoundDescription(
TypeVariable typeVariable, ImmutableList<TypeVariable> enclosingWildcardOrCaptures) {
int index = enclosingWildcardOrCaptures.indexOf(typeVariable);
if (index != -1) {
return "rec#" + (enclosingWildcardOrCaptures.size() - index);
}
enclosingWildcardOrCaptures =
ImmutableList.<TypeVariable>builder()
.addAll(enclosingWildcardOrCaptures)
.add(typeVariable)
.build();
TypeDescriptor lowerBound = typeVariable.getLowerBoundTypeDescriptor();
if (lowerBound != null) {
return "? super " + getDescription(lowerBound, enclosingWildcardOrCaptures);
} else {
return "? extends "
+ getDescription(
typeVariable.getUpperBoundTypeDescriptor(), enclosingWildcardOrCaptures);
}
}

private static NullabilityAnnotation getNullabilityAnnotation(TypeDescriptor typeDescriptor) {
if (typeDescriptor instanceof TypeVariable) {
TypeVariable typeVariable = (TypeVariable) typeDescriptor;
return typeVariable.getNullabilityAnnotation();
} else {
return typeDescriptor.isNullable()
? NullabilityAnnotation.NULLABLE
: NullabilityAnnotation.NOT_NULLABLE;
}
}

private static String getDescriptionPrefix(NullabilityAnnotation nullabilityAnnotation) {
String description = getDescription(nullabilityAnnotation);
return description == null ? "" : description + " ";
}

private static String getDescriptionInfix(NullabilityAnnotation nullabilityAnnotation) {
String description = getDescription(nullabilityAnnotation);
return description == null ? "" : " " + description + " ";
}
}
}

0 comments on commit 47e2117

Please sign in to comment.