Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@

import androidx.room3.compiler.processing.XExecutableElement;
import androidx.room3.compiler.processing.XMethodElement;
import androidx.room3.compiler.processing.XMethodType;
import androidx.room3.compiler.processing.XType;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.collect.ImmutableList;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import dagger.internal.codegen.xprocessing.XElements;
import java.util.Objects;

/** Represents the method signature needed to uniquely identify a method. */
@AutoValue
public abstract class MethodSignature {
MethodSignature() {}

Expand All @@ -40,22 +40,20 @@ public abstract class MethodSignature {

/** Creates a {@link MethodSignature} from a method name and parameter {@link TypeName}s */
public static MethodSignature of(String methodName, TypeName... typeNames) {
return new AutoValue_MethodSignature(methodName, ImmutableList.copyOf(typeNames));
return new AutoValue_MethodSignature_TypeNameMethodSignature(
methodName, ImmutableList.copyOf(typeNames));
}

/** Creates a {@link MethodSignature} from a {@link MethodSpec} */
public static MethodSignature of(MethodSpec method) {
return new AutoValue_MethodSignature(
return new AutoValue_MethodSignature_TypeNameMethodSignature(
method.name, method.parameters.stream().map(p -> p.type).collect(toImmutableList()));
}

/** Creates a {@link MethodSignature} from an {@link XExecutableElement} */
public static MethodSignature of(XExecutableElement executableElement) {
return new AutoValue_MethodSignature(
XElements.getSimpleName(executableElement),
executableElement.getParameters().stream()
.map(p -> p.getType().getTypeName())
.collect(toImmutableList()));
return new AutoValue_MethodSignature_ElementMethodSignature(
executableElement, executableElement.getEnclosingElement().getType());
}

/**
Expand All @@ -64,12 +62,27 @@ public static MethodSignature of(XExecutableElement executableElement) {
* <p>This version will resolve type parameters as declared by {@code enclosing}.
*/
static MethodSignature ofDeclaredType(XMethodElement method, XType enclosing) {
XMethodType executableType = method.asMemberOf(enclosing);
return new AutoValue_MethodSignature(
XElements.getSimpleName(method),
executableType.getParameterTypes().stream()
.map(XType::getTypeName)
.collect(toImmutableList()));
return new AutoValue_MethodSignature_ElementMethodSignature(method, enclosing);
}

@Override
public final boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof MethodSignature)) {
return false;
}
MethodSignature that = (MethodSignature) other;
return Objects.equals(this.name(), that.name())
&& Objects.equals(this.parameters(), that.parameters());
}

@Override
public final int hashCode() {
// Only hash the name to avoid expensive parameter resolution. This allows Set and Map lookups
// to be efficient, only calling equals() when the name matches.
return Objects.hashCode(name());
}

/** Returns a string in the format: METHOD_NAME(PARAM_TYPE1,PARAM_TYPE2,...) */
Expand All @@ -78,4 +91,33 @@ public final String toString() {
return String.format(
"%s(%s)", name(), parameters().stream().map(Object::toString).collect(joining(",")));
}

@AutoValue
abstract static class TypeNameMethodSignature extends MethodSignature {
@Override
public abstract String name();

@Override
public abstract ImmutableList<TypeName> parameters();
}

@AutoValue
abstract static class ElementMethodSignature extends MethodSignature {
abstract XExecutableElement executableElement();

abstract XType enclosingType();

@Override
public final String name() {
return XElements.getSimpleName(executableElement());
}

@Override
@Memoized
public ImmutableList<TypeName> parameters() {
return executableElement().asMemberOf(enclosingType()).getParameterTypes().stream()
.map(XType::getTypeName)
.collect(toImmutableList());
}
}
}
13 changes: 13 additions & 0 deletions javatests/dagger/hilt/processor/internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ java_test(
],
)

java_test(
name = "MethodSignatureTest",
size = "small",
srcs = ["MethodSignatureTest.java"],
deps = [
"//hilt-compiler/main/java/dagger/hilt/processor/internal:method_signature",
"//third_party/java/guava/collect",
"//third_party/java/javapoet",
"//third_party/java/junit",
"//third_party/java/truth",
],
)

java_library(
name = "generated_import",
srcs = ["GeneratedImport.java"],
Expand Down
107 changes: 107 additions & 0 deletions javatests/dagger/hilt/processor/internal/MethodSignatureTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright (C) 2026 The Dagger Authors.
*
* 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 dagger.hilt.processor.internal;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import com.google.common.collect.ImmutableList;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public final class MethodSignatureTest {

@Test
public void testBasicGetters() {
MethodSignature signature = MethodSignature.of("foo", TypeName.INT, TypeName.BOOLEAN);
assertThat(signature.name()).isEqualTo("foo");
assertThat(signature.parameters()).containsExactly(TypeName.INT, TypeName.BOOLEAN).inOrder();
}

@Test
public void testOfMethodSpec() {
MethodSpec method = MethodSpec.methodBuilder("bar")
.addParameter(ParameterSpec.builder(TypeName.INT, "x").build())
.addParameter(ParameterSpec.builder(TypeName.DOUBLE, "y").build())
.build();
MethodSignature signature = MethodSignature.of(method);
assertThat(signature.name()).isEqualTo("bar");
assertThat(signature.parameters()).containsExactly(TypeName.INT, TypeName.DOUBLE).inOrder();
}

@Test
public void testEqualsAndHashCode() {
MethodSignature sig1 = MethodSignature.of("foo", TypeName.INT);
MethodSignature sig2 = MethodSignature.of("foo", TypeName.INT);
MethodSignature sigDifferentParams = MethodSignature.of("foo", TypeName.BOOLEAN);
MethodSignature sigDifferentName = MethodSignature.of("bar", TypeName.INT);

// Equality
assertThat(sig1).isEqualTo(sig2);
assertThat(sig1).isNotEqualTo(sigDifferentParams);
assertThat(sig1).isNotEqualTo(sigDifferentName);

// HashCode contract (equal objects must have equal hashcodes)
assertThat(sig1.hashCode()).isEqualTo(sig2.hashCode());

// HashCode is name based to optimize initialization, so they can collide:
assertThat(sig1.hashCode()).isEqualTo(sigDifferentParams.hashCode());
assertThat(sig1.hashCode()).isNotEqualTo(sigDifferentName.hashCode());
}

@Test
public void testEqualsShortCircuitsName() {
MethodSignature sigThrowsOnParams = new ThrowingMethodSignature("foo");
MethodSignature sigDifferentName = MethodSignature.of("bar", TypeName.INT);

// Since names differ, equals() must short-circuit on the name and never invoke parameters()
assertThat(sigThrowsOnParams.equals(sigDifferentName)).isFalse();

// A standard equality check with same name should invoke parameters() and throw
MethodSignature other = MethodSignature.of("foo", TypeName.INT);
assertThrows(AssertionError.class, () -> sigThrowsOnParams.equals(other));
}

@Test
public void testToString() {
MethodSignature signature = MethodSignature.of("foo", TypeName.INT, TypeName.BOOLEAN);
assertThat(signature.toString()).isEqualTo("foo(int,boolean)");
}

private static final class ThrowingMethodSignature extends MethodSignature {
private final String name;

ThrowingMethodSignature(String name) {
this.name = name;
}

@Override
public String name() {
return name;
}

@Override
public ImmutableList<TypeName> parameters() {
throw new AssertionError("Parameters should not be resolved!");
}
}
}
Loading