Skip to content

Fix problem with type var in a method reference#7729

Open
smillst wants to merge 25 commits into
typetools:masterfrom
smillst:issue7680
Open

Fix problem with type var in a method reference#7729
smillst wants to merge 25 commits into
typetools:masterfrom
smillst:issue7680

Conversation

@smillst
Copy link
Copy Markdown
Member

@smillst smillst commented May 18, 2026

Fixes #7680.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b2f24365-3f88-4c58-a725-50eb2deb6cc9

📥 Commits

Reviewing files that changed from the base of the PR and between 101e06c and 19730a2.

📒 Files selected for processing (1)
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java

📝 Walkthrough

Walkthrough

This PR replaces the old InvocationType with a new AbstractExecutableType abstraction and adds AbstractInvocationType and CompileTimeDeclarationType. It updates InferenceFactory to produce these types, threads them through InvocationTypeInference and constraint-reduction (createB2/createB3/createC), renames several parameters for consistency, and adds a regression test Issue7680. Javadoc and minor doc fixes are included.

Possibly related PRs

Suggested reviewers

  • mernst
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR addresses issue #7680 by refactoring type inference to use AbstractExecutableType and CompileTimeDeclarationType abstractions for method references, which resolves the crash when handling type variables bounded by interfaces.
Out of Scope Changes check ✅ Passed All changes are focused on refactoring type-inference abstractions and supporting the method-reference fix. Minor changes like test fixture addition and Javadoc updates are directly related to the issue.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@smillst smillst assigned mernst and smillst and unassigned mernst May 19, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/MethodType.java`:
- Around line 43-62: The method getReturnType(Theta map) computes a
branch-specific Java returnType for diamond/new-class cases but then still
passes the stale returnTypeJava into the result constructors; update the
return-site to use the computed local returnType instead of returnTypeJava so
ProperType(...) and InferenceType.create(...) receive the correct underlying
Java type (modify the two return expressions in getReturnType to pass
returnType, keeping annotatedReturnType, map, and context unchanged).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b7686254-7dea-4e95-9208-6c6857a0006c

📥 Commits

Reviewing files that changed from the base of the PR and between aefd9c3 and e381cd1.

📒 Files selected for processing (2)
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/MethodReferenceType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/MethodType.java

@smillst smillst assigned mernst and unassigned smillst May 26, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceExecutableType.java (1)

98-103: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Thread qualifierVars into thrown-type construction.

getThrownTypes currently builds types with the overload that does not reuse this executable’s precomputed qualifierVars. That gives thrown types a different @Poly* variable mapping than the parameters/return type, which can make inference inconsistent for the same executable.

♻️ Proposed fix
-      thrown.add(InferenceType.create(t, iter.next(), map, context));
+      thrown.add(InferenceType.create(t, iter.next(), map, qualifierVars, context, false));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceExecutableType.java`
around lines 98 - 103, getThrownTypes builds thrown types using
InferenceType.create(t, iter.next(), map, context) which ignores this
executable's precomputed qualifierVars and yields inconsistent `@Poly`* mappings;
update getThrownTypes to pass the executable's qualifierVars into the
thrown-type construction (i.e., call the InferenceType.create overload that
accepts qualifierVars, supplying the stored qualifierVars from this class) so
thrown types share the same qualifier variable mapping as the parameters/return.
framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceMethodType.java (1)

71-74: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve executable-level qualifier vars on the return type.

This path bypasses the precomputed qualifierVars, so a polymorphic qualifier on the return can get a fresh variable instead of the one shared with the parameters. That changes inference behavior for @Poly* methods.

♻️ Proposed fix
-    if (map == null) {
-      return new ProperType(annotatedReturnType, returnType, context);
-    }
-    return InferenceType.create(annotatedReturnType, returnType, map, context);
+    return InferenceType.create(
+        annotatedReturnType, returnType, map, qualifierVars, context, false);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceMethodType.java`
around lines 71 - 74, When map == null the code returns a new ProperType which
bypasses this object's precomputed qualifierVars and causes polymorphic
qualifiers on the return to get fresh variables; instead, preserve the existing
qualifierVars by returning an InferenceType constructed with them. Replace the
early return in InferenceMethodType (the branch that currently does `return new
ProperType(annotatedReturnType, returnType, context)`) to call
InferenceType.create(annotatedReturnType, returnType, qualifierVars, context) so
the return type shares the same qualifierVars as the parameters.
framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java (2)

121-124: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve executable-level qualifier vars on the method-reference return type.

Like InferenceMethodType, this return-type path drops the shared qualifierVars. Any @Poly* qualifier on the referenced method’s return type can stop matching the qualifier variables already assigned to its parameters.

♻️ Proposed fix
-    if (map == null) {
-      return new ProperType(annotatedReturnType, returnType, context);
-    }
-    return InferenceType.create(annotatedReturnType, returnType, map, context);
+    return InferenceType.create(
+        annotatedReturnType, returnType, map, qualifierVars, context, false);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`
around lines 121 - 124, The return-type path in CompileTimeDeclarationType drops
executable-level qualifier variables: when map != null you call
InferenceType.create(annotatedReturnType, returnType, map, context) which loses
this object's qualifierVars and thus any `@Poly`* on the referenced method's
return type; preserve them by passing the CompileTimeDeclarationType's
qualifierVars into the created InferenceType (either by calling an overload of
InferenceType.create that accepts qualifierVars or by constructing the
InferenceType then assigning its qualifierVars) so that
annotatedReturnType/returnType inference keeps the same executable-level
qualifier variable set.

83-103: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Override the arity-less parameter lookup for unbound member references.

InferenceExecutableType.getParameterTypes(map) passes annotatedExecutableType.getParameterTypes().size(), but this subtype prepends the receiver for unbound references. For an unbound varargs reference, that makes size too small by one, so the vararg slot is removed and never added back.

♻️ Proposed fix
+  `@Override`
+  public List<AbstractType> getParameterTypes(Theta map) {
+    int size = annotatedExecutableType.getParameterTypes().size();
+    if (MemberReferenceKind.getMemberReferenceKind(methodRef).isUnbound()) {
+      size++;
+    }
+    return getParameterTypes(map, size);
+  }
+
   `@Override`
   public List<AbstractType> getParameterTypes(Theta map, int size) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`
around lines 83 - 103, The method getParameterTypes incorrectly uses the
incoming size without accounting for the prepended receiver when
MemberReferenceKind.getMemberReferenceKind(methodRef).isUnbound() is true,
causing varargs removal to drop the last slot; fix by incrementing the effective
arity used for varargs handling when unbound (e.g., adjust size = size + 1 right
after you prepend the receiver) so the subsequent TreeUtils.isVarargsCall branch
and its loops that rely on size/params.size()/paramsJava.size() operate with the
receiver accounted for; update references in getParameterTypes (methodRef,
receiver, size, params, paramsJava, TreeUtils.isVarargsCall) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`:
- Around line 121-124: The return-type path in CompileTimeDeclarationType drops
executable-level qualifier variables: when map != null you call
InferenceType.create(annotatedReturnType, returnType, map, context) which loses
this object's qualifierVars and thus any `@Poly`* on the referenced method's
return type; preserve them by passing the CompileTimeDeclarationType's
qualifierVars into the created InferenceType (either by calling an overload of
InferenceType.create that accepts qualifierVars or by constructing the
InferenceType then assigning its qualifierVars) so that
annotatedReturnType/returnType inference keeps the same executable-level
qualifier variable set.
- Around line 83-103: The method getParameterTypes incorrectly uses the incoming
size without accounting for the prepended receiver when
MemberReferenceKind.getMemberReferenceKind(methodRef).isUnbound() is true,
causing varargs removal to drop the last slot; fix by incrementing the effective
arity used for varargs handling when unbound (e.g., adjust size = size + 1 right
after you prepend the receiver) so the subsequent TreeUtils.isVarargsCall branch
and its loops that rely on size/params.size()/paramsJava.size() operate with the
receiver accounted for; update references in getParameterTypes (methodRef,
receiver, size, params, paramsJava, TreeUtils.isVarargsCall) accordingly.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceExecutableType.java`:
- Around line 98-103: getThrownTypes builds thrown types using
InferenceType.create(t, iter.next(), map, context) which ignores this
executable's precomputed qualifierVars and yields inconsistent `@Poly`* mappings;
update getThrownTypes to pass the executable's qualifierVars into the
thrown-type construction (i.e., call the InferenceType.create overload that
accepts qualifierVars, supplying the stored qualifierVars from this class) so
thrown types share the same qualifier variable mapping as the parameters/return.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceMethodType.java`:
- Around line 71-74: When map == null the code returns a new ProperType which
bypasses this object's precomputed qualifierVars and causes polymorphic
qualifiers on the return to get fresh variables; instead, preserve the existing
qualifierVars by returning an InferenceType constructed with them. Replace the
early return in InferenceMethodType (the branch that currently does `return new
ProperType(annotatedReturnType, returnType, context)`) to call
InferenceType.create(annotatedReturnType, returnType, qualifierVars, context) so
the return type shares the same qualifierVars as the parameters.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0d1c8640-5e5c-4da0-837c-4f7016bf5643

📥 Commits

Reviewing files that changed from the base of the PR and between 7cb2b23 and 8e8246d.

📒 Files selected for processing (6)
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/InvocationTypeInference.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/constraint/Expression.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceExecutableType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceFactory.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceMethodType.java

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`:
- Around line 74-76: Update the Javadoc in class CompileTimeDeclarationType for
the method that returns the method reference (the method whose Javadoc currently
reads "Returns the method reference for which this a compile-time declaration.")
by changing the phrase "for which this a" to "for which this is a" so the
sentence reads "Returns the method reference for which this is a compile-time
declaration."
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8be71a08-0b62-44f4-ac42-04830be751f3

📥 Commits

Reviewing files that changed from the base of the PR and between 8e8246d and ba795b2.

📒 Files selected for processing (1)
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java

@mernst mernst assigned smillst and unassigned mernst May 27, 2026
@mernst
Copy link
Copy Markdown
Member

mernst commented May 27, 2026

@smillst Please look at the CodeRabbit "outside the diff" suggestions, then re-assign me. Thanks!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java (1)

82-104: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix unbound-varargs sizing when getParameterTypes(map) is called without explicit size.

Line 94 and Line 98 expand varargs using size, but for unbound references the receiver is prepended at Line 88 while inherited default getParameterTypes(map) still uses the pre-receiver parameter count. In the exact method-reference path (Expression.reduceMethodRef), this can yield too few formals for unbound varargs references.

Suggested fix
+  `@Override`
+  public List<AbstractType> getParameterTypes(Theta map) {
+    int size = annotatedExecutableType.getParameterTypes().size();
+    if (MemberReferenceKind.getMemberReferenceKind(methodRef).isUnbound()) {
+      size++;
+    }
+    return getParameterTypes(map, size);
+  }
+
   `@Override`
   public List<AbstractType> getParameterTypes(Theta map, int size) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`
around lines 82 - 104, The varargs expansion uses the incoming size parameter
but the overload getParameterTypes(Theta map) calls the two-arg
getParameterTypes without accounting for the extra receiver inserted for unbound
method references; update the no-size overload to compute an effective size that
equals the annotatedExecutableType parameter count plus 1 if
MemberReferenceKind.getMemberReferenceKind(methodRef).isUnbound() before
delegating to getParameterTypes(Theta map, int size) (so the receiver-added
parameter is reflected when expanding varargs), leaving the existing
getParameterTypes(Theta map, int size), TreeUtils.isVarargsCall(methodRef),
receiver handling, and InferenceType.create(...) intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`:
- Around line 82-104: The varargs expansion uses the incoming size parameter but
the overload getParameterTypes(Theta map) calls the two-arg getParameterTypes
without accounting for the extra receiver inserted for unbound method
references; update the no-size overload to compute an effective size that equals
the annotatedExecutableType parameter count plus 1 if
MemberReferenceKind.getMemberReferenceKind(methodRef).isUnbound() before
delegating to getParameterTypes(Theta map, int size) (so the receiver-added
parameter is reflected when expanding varargs), leaving the existing
getParameterTypes(Theta map, int size), TreeUtils.isVarargsCall(methodRef),
receiver handling, and InferenceType.create(...) intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d7ec1a77-6851-4407-8776-7c6fcdd18371

📥 Commits

Reviewing files that changed from the base of the PR and between ba795b2 and db9d8d3.

📒 Files selected for processing (1)
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@smillst smillst removed their assignment May 28, 2026
@mernst mernst assigned smillst and unassigned mernst May 29, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java (1)

44-51: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider making fields private.

The receiver and methodRef fields have package-private visibility. Since there's already a getter for methodRef, consider making these fields private for better encapsulation.

♻️ Suggested visibility change
-  AnnotatedTypeMirror receiver;
+  private final AnnotatedTypeMirror receiver;

   /** The method reference tree. */
-  MemberReferenceTree methodRef;
+  private final MemberReferenceTree methodRef;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`
around lines 44 - 51, Make the fields 'receiver' and 'methodRef' in class
CompileTimeDeclarationType private to improve encapsulation; keep the existing
getter for methodRef (and add one for receiver if external code needs access)
and update any internal references that currently access these package-private
fields directly to use the getters (or adjust within the same class), ensuring
no behavior change other than visibility.
♻️ Duplicate comments (1)
framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java (1)

53-54: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Javadoc terminology mismatch.

The Javadoc says "Creates an invocation type for a method reference" but this class represents a compile-time declaration type, not an invocation type. Consider updating to "Creates a compile-time declaration type for a method reference" for consistency with the class name and purpose.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`
around lines 53 - 54, Update the Javadoc for CompileTimeDeclarationType to
reflect its actual purpose: change the sentence "Creates an invocation type for
a method reference" to something like "Creates a compile-time declaration type
for a method reference" so the terminology matches the class name
CompileTimeDeclarationType and its role; ensure any other nearby Javadoc
references to "invocation type" within this class are similarly adjusted to
"compile-time declaration type" to keep consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`:
- Around line 44-51: Make the fields 'receiver' and 'methodRef' in class
CompileTimeDeclarationType private to improve encapsulation; keep the existing
getter for methodRef (and add one for receiver if external code needs access)
and update any internal references that currently access these package-private
fields directly to use the getters (or adjust within the same class), ensuring
no behavior change other than visibility.

---

Duplicate comments:
In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`:
- Around line 53-54: Update the Javadoc for CompileTimeDeclarationType to
reflect its actual purpose: change the sentence "Creates an invocation type for
a method reference" to something like "Creates a compile-time declaration type
for a method reference" so the terminology matches the class name
CompileTimeDeclarationType and its role; ensure any other nearby Javadoc
references to "invocation type" within this class are similarly adjusted to
"compile-time declaration type" to keep consistency.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 70546306-de3f-469a-98d3-7894c80e7dd1

📥 Commits

Reviewing files that changed from the base of the PR and between 91c30a3 and 40c49fe.

📒 Files selected for processing (11)
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/DefaultTypeArgumentInference.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/InferenceResult.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/InvocationTypeInference.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/TypeArgumentInference.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/constraint/AdditionalArgument.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/constraint/Expression.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceExecutableType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceFactory.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceInvocationType.java

@smillst smillst assigned mernst and unassigned smillst May 29, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractInvocationType.java (1)

78-100: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider extracting the varargs check to avoid duplicate evaluation.

The method checks TreeUtils.isVarargsCall(invocation) twice (lines 83 and 91). Extract the result to a boolean variable to improve clarity and avoid redundant calls.

♻️ Proposed refactor
  `@Override`
  public List<AbstractType> getParameterTypes(Theta map, int size) {
    List<AnnotatedTypeMirror> params = new ArrayList<>(annotatedExecutableType.getParameterTypes());
    List<TypeMirror> paramsJava = new ArrayList<>(executableType.getParameterTypes());

+   boolean isVarargs = TreeUtils.isVarargsCall(invocation);
-   if (TreeUtils.isVarargsCall(invocation)) {
+   if (isVarargs) {
      AnnotatedTypeMirror eltType =
          ((AnnotatedArrayType) params.remove(params.size() - 1)).getComponentType();
      for (int i = params.size(); i < size; i++) {
        params.add(eltType);
      }
    }

-   if (TreeUtils.isVarargsCall(invocation)) {
+   if (isVarargs) {
      TypeMirror eltType =
          ((ArrayType) paramsJava.remove(paramsJava.size() - 1)).getComponentType();
      for (int i = paramsJava.size(); i < size; i++) {
        paramsJava.add(eltType);
      }
    }

    return InferenceType.create(params, paramsJava, map, qualifierVars, context);
  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractInvocationType.java`
around lines 78 - 100, The method getParameterTypes currently calls
TreeUtils.isVarargsCall(invocation) twice; store the result in a local boolean
(e.g., boolean isVarargs = TreeUtils.isVarargsCall(invocation)) and use
isVarargs in both varargs branches so you only compute it once, then proceed to
adjust params and paramsJava as before (using eltType for AnnotatedArrayType and
TypeMirror for ArrayType) and finally call InferenceType.create(params,
paramsJava, map, qualifierVars, context).
framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java (1)

83-108: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider extracting the varargs check to avoid duplicate evaluation.

The method checks TreeUtils.isVarargsCall(methodRef) twice (lines 96 and 101). Extract the result to a boolean variable to improve clarity and avoid redundant calls.

♻️ Proposed refactor
  `@Override`
  public List<AbstractType> getParameterTypes(Theta map, int size) {
    List<AnnotatedTypeMirror> params = new ArrayList<>();
    List<TypeMirror> paramsJava = new ArrayList<>();

    if (MemberReferenceKind.getMemberReferenceKind(methodRef).isUnbound()) {
      // For unbound method references, i.e. Type::instanceMethod, the receiver is treated as the
      // first parameter.
      params.add(receiver);
      paramsJava.add(receiver.getUnderlyingType());
    }
    params.addAll(annotatedExecutableType.getParameterTypes());
    paramsJava.addAll(executableType.getParameterTypes());

+   boolean isVarargs = TreeUtils.isVarargsCall(methodRef);
-   if (TreeUtils.isVarargsCall(methodRef)) {
+   if (isVarargs) {
      AnnotatedArrayType vararg = (AnnotatedArrayType) params.remove(params.size() - 1);
      for (int i = params.size(); i < size; i++) {
        params.add(vararg.getComponentType());
      }
+   }
+   if (isVarargs) {
      ArrayType varargTM = (ArrayType) paramsJava.remove(paramsJava.size() - 1);
      for (int i = paramsJava.size(); i < size; i++) {
        paramsJava.add(varargTM.getComponentType());
      }
    }

    return InferenceType.create(params, paramsJava, map, qualifierVars, context);
  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`
around lines 83 - 108, In getParameterTypes (CompileTimeDeclarationType) avoid
calling TreeUtils.isVarargsCall(methodRef) twice by storing its result in a
boolean (e.g., boolean isVarargs = TreeUtils.isVarargsCall(methodRef)) and use
isVarargs in the subsequent conditional and logic that handles varargs for both
params and paramsJava; update the method to compute isVarargs once and keep all
existing varargs handling (removing the duplicate call) so behavior of
getParameterTypes, receiver handling, and InferenceType.create remains
identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractExecutableType.java`:
- Around line 42-45: Fix the grammar in the Javadoc for AbstractExecutableType:
update the sentence "annotation mirror should be represented by with {`@code`
QualifierVar}" to read either "annotation mirror should be represented by {`@code`
QualifierVar}" or "annotation mirror should be represented by a {`@code`
QualifierVar}" so the comment is grammatically correct and clear; edit the
Javadoc block above the mapping field in class AbstractExecutableType to apply
this change.

---

Outside diff comments:
In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractInvocationType.java`:
- Around line 78-100: The method getParameterTypes currently calls
TreeUtils.isVarargsCall(invocation) twice; store the result in a local boolean
(e.g., boolean isVarargs = TreeUtils.isVarargsCall(invocation)) and use
isVarargs in both varargs branches so you only compute it once, then proceed to
adjust params and paramsJava as before (using eltType for AnnotatedArrayType and
TypeMirror for ArrayType) and finally call InferenceType.create(params,
paramsJava, map, qualifierVars, context).

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`:
- Around line 83-108: In getParameterTypes (CompileTimeDeclarationType) avoid
calling TreeUtils.isVarargsCall(methodRef) twice by storing its result in a
boolean (e.g., boolean isVarargs = TreeUtils.isVarargsCall(methodRef)) and use
isVarargs in the subsequent conditional and logic that handles varargs for both
params and paramsJava; update the method to compute isVarargs once and keep all
existing varargs handling (removing the duplicate call) so behavior of
getParameterTypes, receiver handling, and InferenceType.create remains
identical.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e4b65394-3f08-4ef3-b14a-fff79b7bb4f9

📥 Commits

Reviewing files that changed from the base of the PR and between 0765cf5 and 005ce4b.

📒 Files selected for processing (7)
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/InvocationTypeInference.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/constraint/AdditionalArgument.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/constraint/Expression.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractExecutableType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractInvocationType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/InferenceFactory.java

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractExecutableType.java (1)

89-96: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Potential bug: getJavaType() may return the wrong type.

The constructor's Javadoc (lines 53–55) explicitly states that executableType "is not always equal to annotatedExecutableType.getUnderlyingType()." However, this method returns annotatedExecutableType.getUnderlyingType() instead of the stored executableType field.

If the distinction matters (and the Javadoc suggests it does), this should return executableType.

🐛 Proposed fix
   public ExecutableType getJavaType() {
-    return annotatedExecutableType.getUnderlyingType();
+    return executableType;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractExecutableType.java`
around lines 89 - 96, The getJavaType method in AbstractExecutableType currently
returns annotatedExecutableType.getUnderlyingType(), but the class stores a
separate field executableType (and the constructor docs state they may differ);
change getJavaType to return the stored executableType field instead of calling
annotatedExecutableType.getUnderlyingType() so callers receive the actual Java
ExecutableType that was saved during construction.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java`:
- Around line 35-46: The Javadoc in CompileTimeDeclarationType contains
unresolved Git conflict markers; remove the markers (<<<<<<<, |||||||, =======,
>>>>>>>) and collapse the variants into the intended single sentence for the
example (e.g., use the final, type-explicit wording), so the Javadoc reads
something like: "The function type is {`@code` int compare(Comparator<MyClass>
this, MyClass o1, MyClass o2)} whereas the compile-time declaration type is
{`@code` int compareByField(MyClass this, MyClass other)}." Ensure the comment
around the example in CompileTimeDeclarationType and any references to
compare/compareByField are clean and free of conflict artifacts.

---

Outside diff comments:
In
`@framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractExecutableType.java`:
- Around line 89-96: The getJavaType method in AbstractExecutableType currently
returns annotatedExecutableType.getUnderlyingType(), but the class stores a
separate field executableType (and the constructor docs state they may differ);
change getJavaType to return the stored executableType field instead of calling
annotatedExecutableType.getUnderlyingType() so callers receive the actual Java
ExecutableType that was saved during construction.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 804c9bfd-753f-4dba-8a2d-677ece3907ac

📥 Commits

Reviewing files that changed from the base of the PR and between 1e83892 and 101e06c.

📒 Files selected for processing (5)
  • framework/src/main/java/org/checkerframework/framework/util/element/ElementAnnotationUtil.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractExecutableType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/AbstractInvocationType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/CompileTimeDeclarationType.java
  • framework/src/main/java/org/checkerframework/framework/util/typeinference8/types/QualifierVar.java

mernst
mernst previously approved these changes May 31, 2026
import org.checkerframework.javacutil.AnnotationMirrorSet;

/**
* An inference type for a method, constructor, or method reference. This is a wrapper around {@link
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be "a method or constructor invocation" rather than "a method or constructor"?


/**
* A mapping from polymorphic annotation to {@link QualifierVar}. It keeps track of which
* annotation mirror should be represented by which {@code QualifierVar}.
Copy link
Copy Markdown
Member

@mernst mernst May 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"should be" assumes that the reader already understands the design and the code. Please be more specific.
I think this is computed from the invocation, in which case maybe the map represents the instantiation?

@mernst mernst assigned smillst and unassigned mernst May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

StructuralEqualityComparer: unexpected combination

2 participants