Skip to content

Use lambda parameter counts and block bodies for improved resolution #4796

Merged
jlerbsc merged 3 commits intojavaparser:masterfrom
johannescoetzee:johannes/use-lambda-param-ret-for-resolve
Jul 15, 2025
Merged

Use lambda parameter counts and block bodies for improved resolution #4796
jlerbsc merged 3 commits intojavaparser:masterfrom
johannescoetzee:johannes/use-lambda-param-ret-for-resolve

Conversation

@johannescoetzee
Copy link
Copy Markdown
Collaborator

This PR re-opens #4757 with a fix for the last crash I mentioned that occurred when attempting to find the functional method. Please see the bottom of the PR description for a description of the issue and fix.

Original PR description

This PR improves lambda-related method resolution by using the lambda parameter count and body for disambiguation in two cases described below. I implemented this by adding the required information to the LambdaArgumentTypePlaceholder class, but made sure that, if this information is not provided (such as for MethodReferenceExpr, type resolution behaviour will be the same as before this PR.

Different parameter counts

import java.util.function.Consumer;

class Test {
    void foo(Consumer<String> consumer) {}
    void foo(Runnable r) {}

    void test() {
        foo(input -> {});
    }
}

Consumer.accept has one parameter while Runnable.run has zero, so since the lambda has one parameter, we know this must resolve to foo(Consumer<String>)

Disambiguation by return type

import java.util.function.Consumer;
import java.util.function.Function;

class Test {
    void foo(Consumer<String> consumer) {}
    void foo(Function<Integer, String> func) {}

    void test() {
        foo(input -> {});
    }
}

In this example, the body of the lambda is a block statement which does not contain any return statements. This means that the lambda can only define a method with a void return type, so it must define Consumer<String> (the same would be true for foo(input -> { return; }).

If it were foo(input -> { return ""; }) instead, then the reverse logic would apply. Since the return type of the lambda is not void, it cannot define Consumer and must therefore define Function.

New in this PR

This issue occurred when attempting to resolve a call to a method where one of the parameters is a functional interface that extends a different functional interface but overrides one of the generic types, for example:

import java.util.function.Function;
 
@FunctionalInterface
interface ReturnStringFunction<T> extends Function<T, String> {
  String apply(T t);
}

class Foo {
  static <T> String foo(T t) { return null; }
}

public class Test {
  <T> String acceptsFunction(ReturnStringFunction<T> consumer) { return null; }

  void test() {
    acceptsFunction(Foo::foo);
  }
}

In this example, the Foo::foo method reference has to be resolved to resolve the acceptsFunction call. 2 candidate methods for the method implemented by Foo::foo are found:

R apply(T t) from java.util.function.Function<T, R>

and

String apply(T t) from ReturnStringFunction<T>

At some point during the resolution, it must be determined whether these method return types are substitutable or not. The logic for doing this already exists, but method1.isReturnTypeSubstitutable(method2.returnType()) only handles the case where the return type of method2 is a type variable. If the return type of method1 is a type variable, then this crashes with an UnsupportedOperationException. I've added a fix where, if the return type of method1 is a type variable and all other checks have been done, false is returned since in general the concrete return type of method2 cannot be substituted for the type variable.

@johannescoetzee johannescoetzee force-pushed the johannes/use-lambda-param-ret-for-resolve branch from 9fe9a79 to 5222d6b Compare July 10, 2025 10:46
@codecov
Copy link
Copy Markdown

codecov bot commented Jul 10, 2025

Codecov Report

Attention: Patch coverage is 30.43478% with 32 lines in your changes missing coverage. Please review.

Project coverage is 58.326%. Comparing base (5b97054) to head (12e4459).
Report is 5 commits behind head on master.

Files with missing lines Patch % Lines
...parser/resolution/logic/MethodResolutionLogic.java 0.000% 20 Missing ⚠️
...esolution/model/LambdaArgumentTypePlaceholder.java 0.000% 10 Missing ⚠️
...lution/declarations/ResolvedMethodDeclaration.java 0.000% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@               Coverage Diff               @@
##              master     #4796       +/-   ##
===============================================
- Coverage     58.351%   58.326%   -0.025%     
- Complexity      2514      2518        +4     
===============================================
  Files            671       671               
  Lines          38782     38826       +44     
  Branches        7041      7054       +13     
===============================================
+ Hits           22630     22646       +16     
- Misses         13266     13295       +29     
+ Partials        2886      2885        -1     
Flag Coverage Δ
AlsoSlowTests 58.326% <30.434%> (-0.025%) ⬇️
javaparser-core 58.326% <30.434%> (-0.025%) ⬇️
javaparser-symbol-solver 58.326% <30.434%> (-0.025%) ⬇️
jdk-10 57.876% <30.434%> (-0.027%) ⬇️
jdk-11 57.877% <30.434%> (-0.025%) ⬇️
jdk-12 57.877% <30.434%> (-0.025%) ⬇️
jdk-13 57.877% <30.434%> (-0.025%) ⬇️
jdk-14 58.124% <30.434%> (-0.025%) ⬇️
jdk-15 58.124% <30.434%> (-0.025%) ⬇️
jdk-16 58.099% <30.434%> (-0.025%) ⬇️
jdk-17 58.253% <30.434%> (-0.025%) ⬇️
jdk-18 58.253% <30.434%> (-0.025%) ⬇️
jdk-8 57.879% <30.434%> (-0.025%) ⬇️
jdk-9 57.876% <30.434%> (-0.025%) ⬇️
macos-latest 58.319% <30.434%> (-0.025%) ⬇️
ubuntu-latest 58.314% <30.434%> (-0.025%) ⬇️
windows-latest 58.308% <30.434%> (-0.025%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...symbolsolver/javaparsermodel/JavaParserFacade.java 77.887% <100.000%> (+0.991%) ⬆️
...lution/declarations/ResolvedMethodDeclaration.java 0.000% <0.000%> (ø)
...esolution/model/LambdaArgumentTypePlaceholder.java 0.000% <0.000%> (ø)
...parser/resolution/logic/MethodResolutionLogic.java 0.000% <0.000%> (ø)

... and 2 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 862450e...12e4459. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

parameterValue = ((EnclosedExpr) parameterValue).getInner();
}
if (parameterValue.isLambdaExpr() || parameterValue.isMethodReferenceExpr()) {
if (parameterValue.isLambdaExpr()) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you add comment here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Apologies for taking so long to get back to this. I've added a comment as requested explaining the reasoning behind adding this information to the LambdaArgumentTypePlaceholder. Please let me know if anything in the comment is unclear, or if I've missed something important

@jlerbsc
Copy link
Copy Markdown
Collaborator

jlerbsc commented Jul 10, 2025

Very good work once again. Thanks again.

@johannescoetzee johannescoetzee changed the title Johannes/use lambda param ret for resolve Use lambda parameter counts and block bodies for improved resolution Jul 15, 2025
@jlerbsc jlerbsc merged commit af19adc into javaparser:master Jul 15, 2025
34 of 35 checks passed
@jlerbsc jlerbsc added this to the next release milestone Jul 15, 2025
@jlerbsc jlerbsc added the PR: Fixed A PR that offers a fix or correction label Jul 15, 2025
@johannescoetzee johannescoetzee deleted the johannes/use-lambda-param-ret-for-resolve branch July 15, 2025 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: Fixed A PR that offers a fix or correction

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants