Propagate unknown type better when mref is unresolved

This commit is contained in:
Clément Fournier
2024-11-14 17:27:45 +01:00
parent 2df68ed168
commit bf388d7fd0
8 changed files with 188 additions and 1 deletions

View File

@ -335,6 +335,13 @@ final class ExprCheckHelper {
checker.checkExprConstraint(infCtx, capture(r2), r);
}
completeMethodRefInference(mref, nonWildcard, fun, exactMethod, true);
} else if (TypeOps.isUnresolved(mref.getTypeToSearch())) {
// Then this is neither an exact nor inexact method ref,
// we just don't know what it is.
// The return values of the mref are assimilated to an (*unknown*) type.
checker.checkExprConstraint(infCtx, ts.UNKNOWN, fun.getReturnType());
completeMethodRefInference(mref, nonWildcard, fun, ts.UNRESOLVED_METHOD, false);
} else {
// Otherwise, the method reference is inexact, and:

View File

@ -227,7 +227,6 @@ final class ExprOps {
}
} else {
JClassType enclosing = mref.getEnclosingType();
accessible = mref.getTypeToSearch()
.streamMethods(TypeOps.accessibleMethodFilter(mref.getMethodName(), enclosing.getSymbol()))
.collect(OverloadSet.collectMostSpecific(enclosing));

View File

@ -55,6 +55,11 @@ class TypesTreeDumpTest extends BaseTreeDumpTest {
doTest("NestedLambdasAndMethodCalls");
}
@Test
void testUnresolvedThings() {
doTest("UnresolvedThings");
}
@Override
protected @NonNull String normalize(@NonNull String str) {
return super.normalize(str)

View File

@ -21,6 +21,7 @@ fun JavaNode.declaredMethodSignatures(): List<JMethodSig> = methodDeclarations()
fun JavaNode.methodCalls(): DescendantNodeStream<ASTMethodCall> = descendants(ASTMethodCall::class.java)
fun JavaNode.firstMethodCall() = methodCalls().crossFindBoundaries().firstOrThrow()
fun JavaNode.firstMethodCall(name: String) = methodCalls().crossFindBoundaries().filter { it.methodName == name }.firstOrThrow()
fun JavaNode.ctorCalls(): DescendantNodeStream<ASTConstructorCall> = descendants(ASTConstructorCall::class.java)
fun JavaNode.firstCtorCall() = ctorCalls().crossFindBoundaries().firstOrThrow()

View File

@ -664,4 +664,42 @@ class C {
fooToInt.referencedMethod.symbol shouldBe toIntFun
}
}
parserTest("Type inference should not resolve UNKNOWN bounded types to Object #5329") {
val (acu, _) = parser.parseWithTypeInferenceSpy(
"""
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.Collectors;
class Foo {
public Item methodA(List<Item> loads) {
List<SummaryDto.ItemDto> items = new ArrayList<>();
loads.stream()
// Here this collect call should have type
// Map<(*unknown*), List<*Item>>
// ie, key is unknown, not Object.
.collect(Collectors.groupingBy(Item::getValue))
.forEach((a, b) -> items.add(buildItem(a, b)));
}
private SummaryDto.ItemDto buildItem(BigDecimal a, List<Item> b) {
return SummaryDto.ItemDto.builder().build();
}
}
"""
)
val collect = acu.firstMethodCall("collect")
val buildItem = acu.firstMethodCall("buildItem")
val (_, buildItemDecl) = acu.methodDeclarations().toList { it.symbol }
val (itemT) = acu.descendants(ASTClassType::class.java).toList { it.typeMirror }
acu.withTypeDsl {
collect shouldHaveType java.util.Map::class[ts.UNKNOWN, java.util.List::class[itemT]]
buildItem.methodType.symbol shouldBe buildItemDecl
}
}
})

View File

@ -2177,4 +2177,45 @@ public class ObtainViaTest {
record Library(Collection<Book> books) {}
]]></code>
</test-code>
<test-code>
<description>#5324 UnusedPrivateMethod with unresolved types</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
class Foo {
public User methodA() {
val user = userOpt.orElseGet(() -> {
try {
return registerUser(email, firstName, lastName);
} catch (Exception e) {
throw new IllegalStateException("Failed to register user for " + email, e);
}
});
// ...
return user;
}
private User registerUser(String email, String firstName, String lastName) throws Exception {
// register user logic here...
}
}
]]></code>
</test-code>
<test-code>
<description>#5329 UnusedPrivateMethod with unresolved types</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
class Foo {
public User methodA() {
List<SummaryDto.ItemDto> items = new ArrayList<>();
loads.stream()
.collect(Collectors.groupingBy(Item::getValue))
.forEach((a, b) -> items.add(buildItem(a, b)));
}
private SummaryDto.ItemDto buildItem(BigDecimal a, List<Item> b) {
return SummaryDto.ItemDto.builder().build();
}
}
]]></code>
</test-code>
</test-data>

View File

@ -0,0 +1,16 @@
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.Collectors;
class Foo {
public User methodA(List<Item> loads) {
List<SummaryDto.ItemDto> items = new ArrayList<>();
loads.stream()
.collect(Collectors.groupingBy(Item::getValue))
.forEach((a, b) -> items.add(buildItem(a, b)));
}
private SummaryDto.ItemDto buildItem(BigDecimal a, List<Item> b) {
return SummaryDto.ItemDto.builder().build();
}
}

View File

@ -0,0 +1,80 @@
+- CompilationUnit[]
+- ImportDeclaration[]
+- ImportDeclaration[]
+- ImportDeclaration[]
+- ImportDeclaration[]
+- ClassDeclaration[@TypeMirror = "Foo"]
+- ModifierList[]
+- ClassBody[]
+- MethodDeclaration[@Name = "methodA"]
| +- ModifierList[]
| +- ClassType[@TypeMirror = "*User"]
| +- FormalParameters[]
| | +- FormalParameter[@TypeMirror = "java.util.List<*Item>"]
| | +- ModifierList[]
| | +- ClassType[@TypeMirror = "java.util.List<*Item>"]
| | | +- TypeArguments[]
| | | +- ClassType[@TypeMirror = "*Item"]
| | +- VariableId[@Name = "loads", @TypeMirror = "java.util.List<*Item>"]
| +- Block[]
| +- LocalVariableDeclaration[]
| | +- ModifierList[]
| | +- ClassType[@TypeMirror = "java.util.List<*SummaryDto.ItemDto>"]
| | | +- TypeArguments[]
| | | +- ClassType[@TypeMirror = "*SummaryDto.ItemDto"]
| | +- VariableDeclarator[]
| | +- VariableId[@Name = "items", @TypeMirror = "java.util.List<*SummaryDto.ItemDto>"]
| | +- ConstructorCall[@Failed = false, @Function = "java.util.ArrayList<*SummaryDto.ItemDto>.new() -> java.util.ArrayList<*SummaryDto.ItemDto>", @MethodName = "new", @TypeMirror = "java.util.ArrayList<*SummaryDto.ItemDto>", @Unchecked = false, @VarargsCall = false]
| | +- ClassType[@TypeMirror = "java.util.ArrayList"]
| | | +- TypeArguments[]
| | +- ArgumentList[]
| +- ExpressionStatement[]
| +- MethodCall[@Failed = false, @Function = "java.util.Map<(*unknown*), java.util.List<*Item>>.forEach(java.util.function.BiConsumer<? super (*unknown*), ? super java.util.List<*Item>>) -> void", @MethodName = "forEach", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false]
| +- MethodCall[@Failed = false, @Function = "java.util.stream.Stream<*Item>.<R, A> collect(java.util.stream.Collector<? super *Item, java.lang.Object, java.util.Map<(*unknown*), java.util.List<*Item>>>) -> java.util.Map<(*unknown*), java.util.List<*Item>>", @MethodName = "collect", @TypeMirror = "java.util.Map<(*unknown*), java.util.List<*Item>>", @Unchecked = false, @VarargsCall = false]
| | +- MethodCall[@Failed = false, @Function = "java.util.Collection<*Item>.stream() -> java.util.stream.Stream<*Item>", @MethodName = "stream", @TypeMirror = "java.util.stream.Stream<*Item>", @Unchecked = false, @VarargsCall = false]
| | | +- VariableAccess[@Name = "loads", @TypeMirror = "java.util.List<*Item>"]
| | | +- ArgumentList[]
| | +- ArgumentList[]
| | +- MethodCall[@Failed = false, @Function = "java.util.stream.Collectors.<T, K> groupingBy(java.util.function.Function<? super *Item, ? extends (*unknown*)>) -> java.util.stream.Collector<*Item, java.lang.Object, java.util.Map<(*unknown*), java.util.List<*Item>>>", @MethodName = "groupingBy", @TypeMirror = "java.util.stream.Collector<*Item, java.lang.Object, java.util.Map<(*unknown*), java.util.List<*Item>>>", @Unchecked = false, @VarargsCall = false]
| | +- TypeExpression[@TypeMirror = "java.util.stream.Collectors"]
| | | +- ClassType[@TypeMirror = "java.util.stream.Collectors"]
| | +- ArgumentList[]
| | +- MethodReference[@TypeMirror = "java.util.function.Function<*Item, (*unknown*)>"]
| | +- AmbiguousName[@TypeMirror = "(*unknown*)"]
| +- ArgumentList[]
| +- LambdaExpression[@TypeMirror = "java.util.function.BiConsumer<(*unknown*), java.util.List<*Item>>"]
| +- LambdaParameterList[]
| | +- LambdaParameter[@TypeMirror = "(*unknown*)"]
| | | +- ModifierList[]
| | | +- VariableId[@Name = "a", @TypeMirror = "(*unknown*)"]
| | +- LambdaParameter[@TypeMirror = "java.util.List<*Item>"]
| | +- ModifierList[]
| | +- VariableId[@Name = "b", @TypeMirror = "java.util.List<*Item>"]
| +- MethodCall[@Failed = false, @Function = "java.util.List<*SummaryDto.ItemDto>.add(*SummaryDto.ItemDto) -> boolean", @MethodName = "add", @TypeMirror = "boolean", @Unchecked = false, @VarargsCall = false]
| +- VariableAccess[@Name = "items", @TypeMirror = "java.util.List<*SummaryDto.ItemDto>"]
| +- ArgumentList[]
| +- MethodCall[@Failed = false, @Function = "Foo.buildItem(*BigDecimal, java.util.List<*Item>) -> *SummaryDto.ItemDto", @MethodName = "buildItem", @TypeMirror = "*SummaryDto.ItemDto", @Unchecked = false, @VarargsCall = false]
| +- ArgumentList[]
| +- VariableAccess[@Name = "a", @TypeMirror = "(*unknown*)"]
| +- VariableAccess[@Name = "b", @TypeMirror = "java.util.List<*Item>"]
+- MethodDeclaration[@Name = "buildItem"]
+- ModifierList[]
+- ClassType[@TypeMirror = "*SummaryDto.ItemDto"]
+- FormalParameters[]
| +- FormalParameter[@TypeMirror = "*BigDecimal"]
| | +- ModifierList[]
| | +- ClassType[@TypeMirror = "*BigDecimal"]
| | +- VariableId[@Name = "a", @TypeMirror = "*BigDecimal"]
| +- FormalParameter[@TypeMirror = "java.util.List<*Item>"]
| +- ModifierList[]
| +- ClassType[@TypeMirror = "java.util.List<*Item>"]
| | +- TypeArguments[]
| | +- ClassType[@TypeMirror = "*Item"]
| +- VariableId[@Name = "b", @TypeMirror = "java.util.List<*Item>"]
+- Block[]
+- ReturnStatement[]
+- MethodCall[@Failed = true, @Function = "(*unknown*).(*unknown method*)() -> (*unknown*)", @MethodName = "build", @TypeMirror = "(*unknown*)", @Unchecked = false, @VarargsCall = false]
+- MethodCall[@Failed = true, @Function = "(*unknown*).(*unknown method*)() -> (*unknown*)", @MethodName = "builder", @TypeMirror = "(*unknown*)", @Unchecked = false, @VarargsCall = false]
| +- AmbiguousName[@TypeMirror = "(*unknown*)"]
| +- ArgumentList[]
+- ArgumentList[]