Skip to content

Commit

Permalink
Make Hilt copy type-use nullness annotations, too.
Browse files Browse the repository at this point in the history
RELNOTES=n/a
PiperOrigin-RevId: 703169716
  • Loading branch information
cpovirk authored and Dagger Team committed Dec 7, 2024
1 parent 84d3aa5 commit e2989f5
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XConstructorElement;
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
import androidx.room.compiler.processing.XVariableElement;
import com.google.common.base.Preconditions;
Expand Down Expand Up @@ -130,14 +131,30 @@ private static Optional<AnnotationSpec> getNullableAnnotationSpec(XElement eleme
return Optional.empty();
}

/** Returns a TypeName for the given type, including any @Nullable annotations on it. */
private static TypeName withAnyNullnessAnnotation(XType type) {
for (XAnnotation annotation : type.getAllAnnotations()) {
if (annotation.getClassName().simpleName().contentEquals("Nullable")) {
return type.getTypeName().annotated(toAnnotationSpec(annotation));
}
}
return type.getTypeName();
}

/**
* Returns a ParameterSpec of the input parameter, @Nullable annotated if existing in original
* (this does not handle Nullable type annotations).
* Returns a ParameterSpec of the input parameter, @Nullable annotated if existing in original.
*/
private static ParameterSpec getParameterSpecWithNullable(XVariableElement parameter) {
ParameterSpec.Builder builder =
ParameterSpec.builder(parameter.getType().getTypeName(), getSimpleName(parameter));
getNullableAnnotationSpec(parameter).ifPresent(builder::addAnnotation);
TypeName type = withAnyNullnessAnnotation(parameter.getType());
ParameterSpec.Builder builder = ParameterSpec.builder(type, getSimpleName(parameter));
/*
* If we already have a type-use Nullable, don't consider also adding a declaration Nullable,
* which could be a duplicate in the case of "hybrid" annotations that support both type-use and
* declaration targets.
*/
if (!type.isAnnotated()) {
getNullableAnnotationSpec(parameter).ifPresent(builder::addAnnotation);
}
return builder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,105 @@ public void copyConstructorParametersConvertsAndroidInternalNullableToExternal()
});
}

// This is a regression test for b/382104423
@Test
public void typeUseNullableCopiedFromSuperConstructor() {
Source baseView =
HiltCompilerTests.javaSource(
"test.BaseView",
"package test;",
"",
"import android.content.Context;",
"import android.util.AttributeSet;",
"import android.view.View;",
"import org.jspecify.annotations.Nullable;",
"",
"public class BaseView extends View {",
" public BaseView(Context context, @Nullable AttributeSet attrs) {",
" super(context, attrs);",
" }",
"}");
Source myView =
HiltCompilerTests.javaSource(
"test.MyView",
"package test;",
"",
"import android.content.Context;",
"import android.util.AttributeSet;",
"import android.view.View;",
"import dagger.hilt.android.AndroidEntryPoint;",
"import org.jspecify.annotations.Nullable;",
"",
"@AndroidEntryPoint(BaseView.class)",
"public class MyView extends Hilt_MyView {",
" public MyView(Context context, @Nullable AttributeSet attrs) {",
" super(context, attrs);",
" }",
"}");
HiltCompilerTests.hiltCompiler(baseView, myView)
.compile(
subject -> {
subject.hasErrorCount(0);
StringSubject stringSubject =
subject.generatedSourceFileWithPath("test/Hilt_MyView.java");
stringSubject.contains("org.jspecify.annotations.Nullable");
});
}

@Test
public void hybridTypeUseAndDeclarationNullableNotDuplicated() {
Source hybridNullable =
HiltCompilerTests.javaSource(
"test.Nullable",
"package test;",
"",
"import static java.lang.annotation.ElementType.PARAMETER;",
"import static java.lang.annotation.ElementType.TYPE_USE;",
"",
"import java.lang.annotation.Target;",
"",
"@Target({TYPE_USE, PARAMETER})",
"public @interface Nullable {}");
Source baseView =
HiltCompilerTests.javaSource(
"test.BaseView",
"package test;",
"",
"import android.content.Context;",
"import android.util.AttributeSet;",
"import android.view.View;",
"",
"public class BaseView extends View {",
" public BaseView(Context context, @Nullable AttributeSet attrs) {",
" super(context, attrs);",
" }",
"}");
Source myView =
HiltCompilerTests.javaSource(
"test.MyView",
"package test;",
"",
"import android.content.Context;",
"import android.util.AttributeSet;",
"import android.view.View;",
"import dagger.hilt.android.AndroidEntryPoint;",
"",
"@AndroidEntryPoint(BaseView.class)",
"public class MyView extends Hilt_MyView {",
" public MyView(Context context, @Nullable AttributeSet attrs) {",
" super(context, attrs);",
" }",
"}");
HiltCompilerTests.hiltCompiler(hybridNullable, baseView, myView)
.compile(
subject -> {
subject.hasErrorCount(0);
StringSubject stringSubject =
subject.generatedSourceFileWithPath("test/Hilt_MyView.java");
stringSubject.contains("@Nullable");
});
}

// This is a regression test for https://github.com/google/dagger/issues/3296
@Test
public void isRestrictedApiConstructorWithPrimitiveParameterTest() {
Expand Down

0 comments on commit e2989f5

Please sign in to comment.