diff --git a/hilt-compiler/main/java/dagger/hilt/processor/internal/MethodSignature.java b/hilt-compiler/main/java/dagger/hilt/processor/internal/MethodSignature.java
index e397ec84308..b28e5fd6a58 100644
--- a/hilt-compiler/main/java/dagger/hilt/processor/internal/MethodSignature.java
+++ b/hilt-compiler/main/java/dagger/hilt/processor/internal/MethodSignature.java
@@ -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() {}
@@ -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());
}
/**
@@ -64,12 +62,27 @@ public static MethodSignature of(XExecutableElement executableElement) {
*
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,...) */
@@ -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 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 parameters() {
+ return executableElement().asMemberOf(enclosingType()).getParameterTypes().stream()
+ .map(XType::getTypeName)
+ .collect(toImmutableList());
+ }
+ }
}
diff --git a/javatests/dagger/hilt/processor/internal/BUILD b/javatests/dagger/hilt/processor/internal/BUILD
index ed8d9d9433d..9d59fb395cd 100644
--- a/javatests/dagger/hilt/processor/internal/BUILD
+++ b/javatests/dagger/hilt/processor/internal/BUILD
@@ -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"],
diff --git a/javatests/dagger/hilt/processor/internal/MethodSignatureTest.java b/javatests/dagger/hilt/processor/internal/MethodSignatureTest.java
new file mode 100644
index 00000000000..6a9f0ac581a
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/MethodSignatureTest.java
@@ -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 parameters() {
+ throw new AssertionError("Parameters should not be resolved!");
+ }
+ }
+}