8381642: javac provides the wrong line number for an error in a lambda expression

Reviewed-by: mcimadamore
This commit is contained in:
Dusan Balek 2026-04-21 08:59:24 +00:00 committed by Maurizio Cimadamore
parent a37fd2b7c7
commit 96f2cba774
15 changed files with 183 additions and 26 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -565,7 +565,7 @@ public class ArgumentAttr extends JCTree.Visitor {
Type lambdaType = targetInfo.descriptor;
Type currentTarget = targetInfo.target;
//check compatibility
checkLambdaCompatible(lambdaType, resultInfo);
checkLambdaCompatible(lambdaType, currentTarget, resultInfo);
return currentTarget;
} catch (FunctionDescriptorLookupError ex) {
resultInfo.checkContext.report(null, ex.getDiagnostic());
@ -574,7 +574,7 @@ public class ArgumentAttr extends JCTree.Visitor {
}
/** Check lambda against given target result */
private void checkLambdaCompatible(Type descriptor, ResultInfo resultInfo) {
private void checkLambdaCompatible(Type descriptor, Type target, ResultInfo resultInfo) {
CheckContext checkContext = resultInfo.checkContext;
ResultInfo bodyResultInfo = attr.lambdaBodyResult(speculativeTree, descriptor, resultInfo);
switch (speculativeTree.getBodyKind()) {
@ -588,7 +588,7 @@ public class ArgumentAttr extends JCTree.Visitor {
break;
}
attr.checkLambdaCompatible(speculativeTree, descriptor, checkContext);
attr.checkLambdaCompatible(speculativeTree, descriptor, target.tsym, checkContext);
}
/**

View File

@ -3241,7 +3241,7 @@ public class Attr extends JCTree.Visitor {
attribStats(that.params, localEnv);
if (arityMismatch) {
resultInfo.checkContext.report(that, diags.fragment(Fragments.IncompatibleArgTypesInLambda));
resultInfo.checkContext.report(that, diags.fragment(Fragments.WrongNumberArgsInLambda(currentTarget.tsym)));
result = that.type = types.createErrorType(currentTarget);
return;
}
@ -3274,7 +3274,7 @@ public class Attr extends JCTree.Visitor {
flow.analyzeLambda(env, that, make, isSpeculativeRound);
that.type = currentTarget; //avoids recovery at this stage
checkLambdaCompatible(that, lambdaType, resultInfo.checkContext);
checkLambdaCompatible(that, lambdaType, currentTarget.tsym, resultInfo.checkContext);
if (!isSpeculativeRound) {
//add thrown types as bounds to the thrown types free variables if needed:
@ -3550,7 +3550,7 @@ public class Attr extends JCTree.Visitor {
* (i) parameter types must be identical to those of the target descriptor; (ii) return
* types must be compatible with the return type of the expected descriptor.
*/
void checkLambdaCompatible(JCLambda tree, Type descriptor, CheckContext checkContext) {
void checkLambdaCompatible(JCLambda tree, Type descriptor, TypeSymbol target, CheckContext checkContext) {
Type returnType = checkContext.inferenceContext().asUndetVar(descriptor.getReturnType());
//return values have already been checked - but if lambda has no return
@ -3567,7 +3567,9 @@ public class Attr extends JCTree.Visitor {
List<Type> argTypes = checkContext.inferenceContext().asUndetVars(descriptor.getParameterTypes());
if (!types.isSameTypes(argTypes, TreeInfo.types(tree.params))) {
checkContext.report(tree, diags.fragment(Fragments.IncompatibleArgTypesInLambda));
checkContext.report(tree, diags.fragment(argTypes.size() != tree.params.size()
? Fragments.WrongNumberArgsInLambda(target)
: Fragments.IncompatibleArgTypesInLambda(argTypes, TreeInfo.types(tree.params))));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -427,7 +427,7 @@ public class DeferredAttr extends JCTree.Visitor {
ListBuffer<JCStatement> stats = new ListBuffer<>();
stats.addAll(that.params);
if (that.getBodyKind() == JCLambda.BodyKind.EXPRESSION) {
stats.add(make.Return((JCExpression)that.body));
stats.add(make.at(that.pos).Return((JCExpression)that.body));
} else {
stats.add((JCBlock)that.body);
}
@ -444,7 +444,7 @@ public class DeferredAttr extends JCTree.Visitor {
if (lambdaBody.hasTag(Tag.RETURN)) {
lambdaBody = ((JCReturn)lambdaBody).expr;
}
JCLambda speculativeLambda = make.Lambda(args, lambdaBody);
JCLambda speculativeLambda = make.at(that.pos).Lambda(args, lambdaBody);
attr.preFlow(speculativeLambda);
flow.analyzeLambda(env, speculativeLambda, make, false);
return speculativeLambda;
@ -845,7 +845,7 @@ public class DeferredAttr extends JCTree.Visitor {
if (descriptorType.getParameterTypes().length() != tree.params.length()) {
checkContext.report(tree,
diags.fragment(Fragments.IncompatibleArgTypesInLambda));
diags.fragment(Fragments.WrongNumberArgsInLambda(pt.tsym)));
}
Type currentReturnType = descriptorType.getReturnType();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -635,7 +635,7 @@ public class Infer {
//in the functional interface descriptors)
List<Type> descParameterTypes = types.findDescriptorType(formalInterface).getParameterTypes();
if (descParameterTypes.size() != paramTypes.size()) {
checkContext.report(pos, diags.fragment(Fragments.IncompatibleArgTypesInLambda));
checkContext.report(pos, diags.fragment(Fragments.WrongNumberArgsInLambda(funcInterface.tsym)));
return types.createErrorType(funcInterface);
}
for (Type p : descParameterTypes) {

View File

@ -996,8 +996,15 @@ compiler.err.lambda.body.neither.value.nor.void.compatible=\
compiler.err.incompatible.thrown.types.in.mref=\
incompatible thrown types {0} in functional expression
# 0: list of type or message segment, 1: list of type or message segment
compiler.misc.incompatible.arg.types.in.lambda=\
incompatible parameter types in lambda expression
incompatible parameter types in lambda expression\n\
required: {0}\n\
found: {1}
# 0: symbol
compiler.misc.wrong.number.args.in.lambda=\
wrong number of parameters in lambda expression for functional interface {0}
compiler.misc.incompatible.arg.types.in.mref=\
incompatible parameter types in method reference

View File

@ -0,0 +1,112 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8381642
* @summary IncompatibleArgTypesInLambda error reported at wrong position
* @library /tools/lib
* @modules
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* @build toolbox.ToolBox toolbox.JavacTask
* @run junit ${test.main.class}
*/
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import toolbox.JavacTask;
import toolbox.Task;
import toolbox.ToolBox;
public class IncompatibleArgTypesInLambda {
private static String SOURCE = """
import java.util.function.Supplier;
import javax.swing.JButton;
public class Test {
public void test(Supplier<String> sup) {
JButton button = new JButton("test");
button.addActionListener(() -> {
String s = (sup != null)
? sup.get()
: "";
IO.println(s);
});
}
}
""";
private Path base;
private ToolBox tb = new ToolBox();
@Test
public void testCompactDiags() throws Exception {
Path classes = base.resolve("classes");
Files.createDirectories(classes);
List<String> log = new JavacTask(tb)
.options("-d", classes.toString(), "-XDrawDiagnostics", "-Xdiags:compact")
.sources(SOURCE)
.run(Task.Expect.FAIL)
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
List<String> expected = List.of(
"Test.java:8:34: compiler.err.prob.found.req: (compiler.misc.wrong.number.args.in.lambda: java.awt.event.ActionListener)",
"- compiler.note.compressed.diags",
"1 error"
);
tb.checkEqual(expected, log);
}
@Test
public void testVerboseDiags() throws Exception {
Path classes = base.resolve("classes");
Files.createDirectories(classes);
List<String> log = new JavacTask(tb)
.options("-d", classes.toString(), "-XDrawDiagnostics", "-Xdiags:verbose")
.sources(SOURCE)
.run(Task.Expect.FAIL)
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
List<String> expected = List.of(
"Test.java:8:15: compiler.err.cant.apply.symbol: kindname.method, addActionListener, java.awt.event.ActionListener, @34, kindname.class, javax.swing.AbstractButton, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.wrong.number.args.in.lambda: java.awt.event.ActionListener))",
"1 error"
);
tb.checkEqual(expected, log);
}
@BeforeEach
public void setUp(TestInfo info) {
base = Paths.get(".")
.resolve(info.getTestMethod()
.orElseThrow()
.getName());
}
}

View File

@ -1,2 +1,2 @@
FlowCrashTest.java:18:42: compiler.err.cant.apply.symbols: kindname.method, toMap, @49,@19:49,@20:49,{(compiler.misc.inapplicable.method: kindname.method, java.util.stream.Collectors, <T,K,U>toMap(java.util.function.Function<? super T,? extends K>,java.util.function.Function<? super T,? extends U>), (compiler.misc.infer.arg.length.mismatch: T,K,U)),(compiler.misc.inapplicable.method: kindname.method, java.util.stream.Collectors, <T,K,U>toMap(java.util.function.Function<? super T,? extends K>,java.util.function.Function<? super T,? extends U>,java.util.function.BinaryOperator<U>), (compiler.misc.infer.no.conforming.assignment.exists: T,K,U, (compiler.misc.incompatible.arg.types.in.lambda))),(compiler.misc.inapplicable.method: kindname.method, java.util.stream.Collectors, <T,K,U,M>toMap(java.util.function.Function<? super T,? extends K>,java.util.function.Function<? super T,? extends U>,java.util.function.BinaryOperator<U>,java.util.function.Supplier<M>), (compiler.misc.infer.arg.length.mismatch: T,K,U,M))}
FlowCrashTest.java:18:42: compiler.err.cant.apply.symbols: kindname.method, toMap, @49,@19:49,@20:49,{(compiler.misc.inapplicable.method: kindname.method, java.util.stream.Collectors, <T,K,U>toMap(java.util.function.Function<? super T,? extends K>,java.util.function.Function<? super T,? extends U>), (compiler.misc.infer.arg.length.mismatch: T,K,U)),(compiler.misc.inapplicable.method: kindname.method, java.util.stream.Collectors, <T,K,U>toMap(java.util.function.Function<? super T,? extends K>,java.util.function.Function<? super T,? extends U>,java.util.function.BinaryOperator<U>), (compiler.misc.infer.no.conforming.assignment.exists: T,K,U, (compiler.misc.wrong.number.args.in.lambda: java.util.function.Function))),(compiler.misc.inapplicable.method: kindname.method, java.util.stream.Collectors, <T,K,U,M>toMap(java.util.function.Function<? super T,? extends K>,java.util.function.Function<? super T,? extends U>,java.util.function.BinaryOperator<U>,java.util.function.Supplier<M>), (compiler.misc.infer.arg.length.mismatch: T,K,U,M))}
1 error

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// key: compiler.err.cant.apply.symbol
// key: compiler.misc.no.conforming.assignment.exists
// key: compiler.misc.wrong.number.args.in.lambda
class WrongNumberArgsInLambda {
interface SAM {
void m(Integer x);
}
void op(SAM s) {}
void test() {
op((x, y) -> {});
}
}

View File

@ -2,7 +2,7 @@ T8202372.java:26:13: compiler.err.cant.apply.symbol: kindname.method, addVoid, T
T8202372.java:27:13: compiler.err.cant.apply.symbol: kindname.method, addVoid, T8202372.VoidFunc, @22, kindname.class, T8202372, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.unexpected.ret.val)))
T8202372.java:35:13: compiler.err.cant.apply.symbol: kindname.method, addNonVoid, T8202372.NonVoidFunc, @25, kindname.class, T8202372, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.missing.ret.val: java.lang.String)))
T8202372.java:36:13: compiler.err.cant.apply.symbol: kindname.method, addNonVoid, T8202372.NonVoidFunc, @25, kindname.class, T8202372, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.missing.ret.val)))
T8202372.java:40:13: compiler.err.cant.apply.symbol: kindname.method, addParam, T8202372.ParamFunc, @23, kindname.class, T8202372, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.arg.types.in.lambda))
T8202372.java:42:13: compiler.err.cant.apply.symbol: kindname.method, addParam, T8202372.ParamFunc, @23, kindname.class, T8202372, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.arg.types.in.lambda))
T8202372.java:43:13: compiler.err.cant.apply.symbol: kindname.method, addParam, T8202372.ParamFunc, @23, kindname.class, T8202372, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.arg.types.in.lambda))
T8202372.java:40:13: compiler.err.cant.apply.symbol: kindname.method, addParam, T8202372.ParamFunc, @23, kindname.class, T8202372, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.wrong.number.args.in.lambda: T8202372.ParamFunc))
T8202372.java:42:13: compiler.err.cant.apply.symbol: kindname.method, addParam, T8202372.ParamFunc, @23, kindname.class, T8202372, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.wrong.number.args.in.lambda: T8202372.ParamFunc))
T8202372.java:43:13: compiler.err.cant.apply.symbol: kindname.method, addParam, T8202372.ParamFunc, @23, kindname.class, T8202372, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.arg.types.in.lambda: java.lang.String, int))
7 errors

View File

@ -1,3 +1,3 @@
BadNestedLambda.java:9:35: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.not.a.functional.intf: void))
BadNestedLambda.java:9:24: compiler.err.prob.found.req: (compiler.misc.incompatible.arg.types.in.lambda)
BadNestedLambda.java:9:24: compiler.err.prob.found.req: (compiler.misc.wrong.number.args.in.lambda: java.lang.Runnable)
2 errors

View File

@ -1,4 +1,4 @@
BadRecovery.java:17:9: compiler.err.cant.apply.symbol: kindname.method, m, BadRecovery.SAM1, @11, kindname.class, BadRecovery, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.arg.types.in.lambda))
BadRecovery.java:17:9: compiler.err.cant.apply.symbol: kindname.method, m, BadRecovery.SAM1, @11, kindname.class, BadRecovery, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.wrong.number.args.in.lambda: BadRecovery.SAM1))
BadRecovery.java:17:38: compiler.err.cant.resolve.location.args: kindname.method, someMemberOfReceiver, , @60, (compiler.misc.location.1: kindname.variable, receiver, java.lang.Object)
BadRecovery.java:17:77: compiler.err.cant.resolve.location: kindname.variable, f, , , (compiler.misc.location: kindname.class, BadRecovery, null)
3 errors

View File

@ -1,2 +1,2 @@
LambdaConv10.java:15:39: compiler.err.prob.found.req: (compiler.misc.incompatible.arg.types.in.lambda)
LambdaConv10.java:15:39: compiler.err.prob.found.req: (compiler.misc.incompatible.arg.types.in.lambda: java.lang.Integer, int)
1 error

View File

@ -1,3 +1,3 @@
TargetType44.java:22:9: compiler.err.cant.apply.symbols: kindname.method, m, @11,{(compiler.misc.inapplicable.method: kindname.method, TargetType44, m(TargetType44.Unary), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.arg.types.in.lambda))),(compiler.misc.inapplicable.method: kindname.method, TargetType44, m(TargetType44.Binary), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.arg.types.in.lambda)))}
TargetType44.java:25:9: compiler.err.cant.apply.symbols: kindname.method, m, @11,{(compiler.misc.inapplicable.method: kindname.method, TargetType44, m(TargetType44.Unary), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.arg.types.in.lambda))),(compiler.misc.inapplicable.method: kindname.method, TargetType44, m(TargetType44.Binary), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.arg.types.in.lambda)))}
TargetType44.java:22:9: compiler.err.cant.apply.symbols: kindname.method, m, @11,{(compiler.misc.inapplicable.method: kindname.method, TargetType44, m(TargetType44.Unary), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.wrong.number.args.in.lambda: TargetType44.Unary))),(compiler.misc.inapplicable.method: kindname.method, TargetType44, m(TargetType44.Binary), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.wrong.number.args.in.lambda: TargetType44.Binary)))}
TargetType44.java:25:9: compiler.err.cant.apply.symbols: kindname.method, m, @11,{(compiler.misc.inapplicable.method: kindname.method, TargetType44, m(TargetType44.Unary), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.wrong.number.args.in.lambda: TargetType44.Unary))),(compiler.misc.inapplicable.method: kindname.method, TargetType44, m(TargetType44.Binary), (compiler.misc.no.conforming.assignment.exists: (compiler.misc.wrong.number.args.in.lambda: TargetType44.Binary)))}
2 errors

View File

@ -1,5 +1,5 @@
VoidLambdaParameter.java:7:19: compiler.err.void.not.allowed.here
VoidLambdaParameter.java:7:18: compiler.err.prob.found.req: (compiler.misc.incompatible.arg.types.in.lambda)
VoidLambdaParameter.java:7:18: compiler.err.prob.found.req: (compiler.misc.wrong.number.args.in.lambda: java.lang.Runnable)
VoidLambdaParameter.java:8:12: compiler.err.void.not.allowed.here
VoidLambdaParameter.java:10:23: compiler.err.void.not.allowed.here
4 errors

View File

@ -1,2 +1,2 @@
InvalidExpression4.java:16:17: compiler.err.prob.found.req: (compiler.misc.incompatible.arg.types.in.lambda)
InvalidExpression4.java:16:17: compiler.err.prob.found.req: (compiler.misc.incompatible.arg.types.in.lambda: int, java.lang.Integer)
1 error