wave : ReductionStep.WAVES) {
if (solveBatchProgressed(varsToSolve, wave)) {
@@ -481,7 +553,7 @@ final class InferenceContext {
}
}
}
- return freeVars.isEmpty();
+ return true;
}
/**
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/TypeInferenceLogger.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/TypeInferenceLogger.java
index 023e422a49..cb7fe3e6d3 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/TypeInferenceLogger.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/TypeInferenceLogger.java
@@ -12,6 +12,7 @@ import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -21,6 +22,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.lang.java.ast.JavaNode;
+import net.sourceforge.pmd.lang.java.internal.JavaLanguageProperties;
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
import net.sourceforge.pmd.lang.java.types.JMethodSig;
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
@@ -33,6 +35,12 @@ import net.sourceforge.pmd.util.StringUtil;
/**
* A strategy to log the execution traces of {@link Infer}.
+ * The default does nothing, so the logger calls can be optimized out
+ * at runtime, while not having to check that logging is enabled at the
+ * call sites.
+ *
+ * To enable logging for the CLI, use the language property ({@link JavaLanguageProperties})
+ * {@code xTypeInferenceLogging}. From tests, see {@code JavaParsingHelper#logTypeInferenceVerbose()}.
*/
@SuppressWarnings("PMD.UncommentedEmptyMethodBody")
public interface TypeInferenceLogger {
@@ -61,7 +69,9 @@ public interface TypeInferenceLogger {
default void ctxInitialization(InferenceContext ctx, JMethodSig sig) { }
- default void applicabilityTest(InferenceContext ctx, JMethodSig sig) { }
+ default void applicabilityTest(InferenceContext ctx) { }
+
+ default void finishApplicabilityTest() { }
default void startArgsChecks() { }
@@ -81,6 +91,8 @@ public interface TypeInferenceLogger {
default void propagateAndAbort(InferenceContext context, InferenceContext parent) { }
+ default void contextDependenciesChanged(InferenceContext ctx) { }
+
// ivar events
@@ -90,6 +102,8 @@ public interface TypeInferenceLogger {
default void ivarInstantiated(InferenceContext ctx, InferenceVar var, JTypeMirror inst) { }
+ default void ivarDependencyRegistered(InferenceContext ctx, InferenceVar var, Set deps) { }
+
/**
* Log that the instantiation of the method type m for the given
@@ -136,9 +150,11 @@ public interface TypeInferenceLogger {
protected final PrintStream out;
- protected static final int LEVEL_INCREMENT = 4;
- private int level;
private String indent;
+ /**
+ * Four spaces.
+ */
+ protected static final String BASE_INDENT = " ";
protected static final String ANSI_RESET = "\u001B[0m";
protected static final String ANSI_BLUE = "\u001B[34m";
@@ -177,16 +193,24 @@ public interface TypeInferenceLogger {
public SimpleLogger(PrintStream out) {
this.out = out;
- updateLevel(0);
+ this.indent = "";
}
- protected int getLevel() {
- return level;
+ protected void addIndentSegment(String segment) {
+ indent += segment;
}
- protected void updateLevel(int increment) {
- level += increment;
- indent = StringUtils.repeat(' ', level);
+ protected void removeIndentSegment(String segment) {
+ assert indent.endsWith(segment) : "mismatched end section!";
+ indent = StringUtils.removeEnd(indent, segment);
+ }
+
+ protected void setIndent(String indent) {
+ this.indent = indent;
+ }
+
+ protected String getIndent() {
+ return indent;
}
protected void println(String str) {
@@ -196,13 +220,13 @@ public interface TypeInferenceLogger {
protected void endSection(String footer) {
- updateLevel(-LEVEL_INCREMENT);
+ removeIndentSegment(BASE_INDENT);
println(footer);
}
protected void startSection(String header) {
println(header);
- updateLevel(+LEVEL_INCREMENT);
+ addIndentSegment(BASE_INDENT);
}
@Override
@@ -335,7 +359,7 @@ public interface TypeInferenceLogger {
class VerboseLogger extends SimpleLogger {
- private final Deque marks = new ArrayDeque<>();
+ private final Deque marks = new ArrayDeque<>();
public VerboseLogger(PrintStream out) {
super(out);
@@ -343,16 +367,16 @@ public interface TypeInferenceLogger {
}
void mark() {
- marks.push(getLevel());
+ marks.push(getIndent());
}
void rollback(String lastWords) {
- int pop = marks.pop();
- updateLevel(pop - getLevel()); // back to normal
+ final String savedIndent = marks.pop();
+ setIndent(savedIndent); // back to normal
if (!lastWords.isEmpty()) {
- updateLevel(+LEVEL_INCREMENT);
+ addIndentSegment(BASE_INDENT);
println(lastWords);
- updateLevel(-LEVEL_INCREMENT);
+ setIndent(savedIndent);
}
}
@@ -369,8 +393,14 @@ public interface TypeInferenceLogger {
}
@Override
- public void applicabilityTest(InferenceContext ctx, JMethodSig sig) {
- println(String.format("Applicability testing with Context %-11d%s", ctx.getId(), ppHighlight(ctx.mapToIVars(sig))));
+ public void applicabilityTest(InferenceContext ctx) {
+ println(String.format("Solving with context %d for applicability testing", ctx.getId()));
+ addIndentSegment("| ");
+ }
+
+ @Override
+ public void finishApplicabilityTest() {
+ removeIndentSegment("| ");
}
@Override
@@ -404,7 +434,7 @@ public interface TypeInferenceLogger {
@Override
public void startArg(int i, ExprMirror expr, JTypeMirror formalType) {
- startSection("Checking arg " + i + " against " + formalType);
+ startSection("Checking arg " + i + " against " + colorIvars(formalType));
printExpr(expr);
}
@@ -452,6 +482,16 @@ public interface TypeInferenceLogger {
println(addCtxInfo(ctx, "Ivar instantiated") + color(var + " := ", ANSI_BLUE) + colorIvars(inst));
}
+ @Override
+ public void ivarDependencyRegistered(InferenceContext ctx, InferenceVar var, Set deps) {
+ println(addCtxInfo(ctx, "Ivar dependency registered: ") + color(var + " -> ", ANSI_BLUE) + colorIvars(deps));
+ }
+
+ @Override
+ public void contextDependenciesChanged(InferenceContext ctx) {
+ println("Recomputing dependency graph (ctx " + ctx.getId() + ")");
+ }
+
private @NonNull String addCtxInfo(InferenceContext ctx, String event) {
return String.format("%-20s(ctx %d): ", event, ctx.getId());
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/VarWalkStrategy.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/VarWalkStrategy.java
index 2177a1cf92..19774ca49e 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/VarWalkStrategy.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/VarWalkStrategy.java
@@ -90,6 +90,14 @@ interface VarWalkStrategy extends Iterator> {
}
}
+ ctx.getInstantiationDependencies().forEach((ivar, deps) -> {
+ Vertex vertex = graph.addLeaf(ivar);
+ for (InferenceVar dep : deps) {
+ Vertex target = graph.addLeaf(dep);
+ graph.addEdge(vertex, target);
+ }
+ });
+
// Here, "α depends on β" is modelled by an edge α -> β
// Merge strongly connected components into a "super node".
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java
index f69b79479c..50d22c663d 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypesTreeDumpTest.java
@@ -50,6 +50,16 @@ class TypesTreeDumpTest extends BaseTreeDumpTest {
doTest("UnnamedPatterns");
}
+ @Test
+ void testNestedLambdasAndMethodCalls() {
+ doTest("NestedLambdasAndMethodCalls");
+ }
+
+ @Test
+ void testUnresolvedThings() {
+ doTest("UnresolvedThings");
+ }
+
@Override
protected @NonNull String normalize(@NonNull String str) {
return super.normalize(str)
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/internal/infer/InferenceCtxUnitTests.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/internal/infer/InferenceCtxUnitTests.java
index 5e33c6dd01..ba1d834261 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/internal/infer/InferenceCtxUnitTests.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/internal/infer/InferenceCtxUnitTests.java
@@ -8,7 +8,10 @@ import static net.sourceforge.pmd.lang.java.types.TestUtilitiesForTypesKt.captur
import static net.sourceforge.pmd.lang.java.types.internal.infer.BaseTypeInferenceUnitTest.Bound.eqBound;
import static net.sourceforge.pmd.lang.java.types.internal.infer.BaseTypeInferenceUnitTest.Bound.lower;
import static net.sourceforge.pmd.lang.java.types.internal.infer.BaseTypeInferenceUnitTest.Bound.upper;
+import static net.sourceforge.pmd.util.CollectionUtil.setOf;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -19,11 +22,17 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import java.util.List;
+import java.util.Set;
+
+import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
import net.sourceforge.pmd.lang.java.types.TypeOps;
import net.sourceforge.pmd.lang.java.types.internal.infer.InferenceVar.BoundKind;
+import net.sourceforge.pmd.lang.java.types.internal.infer.VarWalkStrategy.GraphWalk;
+import net.sourceforge.pmd.util.IteratorUtil;
/**
*
@@ -331,4 +340,88 @@ class InferenceCtxUnitTests extends BaseTypeInferenceUnitTest {
assertThat(a, hasBoundsExactly(upper(ts.BOOLEAN.box())));
}
+
+ private static @NotNull List> createBatchSetsFromGraph(InferenceContext ctx) {
+ GraphWalk graphWalk = new GraphWalk(ctx, false);
+ List> batches = IteratorUtil.toList(graphWalk);
+ return batches;
+ }
+
+ @Test
+ void testGraphBuilding() {
+ InferenceContext ctx = emptyCtx();
+ InferenceVar a = newIvar(ctx);
+ InferenceVar b = newIvar(ctx);
+
+ List> batches = createBatchSetsFromGraph(ctx);
+ // no dependency: unordered
+ assertThat(batches, containsInAnyOrder(setOf(a), setOf(b)));
+ }
+
+ @Test
+ void testGraphBuildingWithDependency() {
+ InferenceContext ctx = emptyCtx();
+ InferenceVar a = newIvar(ctx);
+ InferenceVar b = newIvar(ctx);
+
+ // a -> b
+ addSubtypeConstraint(ctx, a, ts.arrayType(b));
+
+ List> batches = createBatchSetsFromGraph(ctx);
+
+ assertThat(batches, contains(setOf(b), setOf(a)));
+ }
+
+ @Test
+ void testGraphBuildingWithDependency2() {
+ InferenceContext ctx = emptyCtx();
+ InferenceVar a = newIvar(ctx);
+ InferenceVar b = newIvar(ctx);
+
+ // a -> b
+ // b -> a (because of propagation)
+ addSubtypeConstraint(ctx, a, b);
+
+ List> batches = createBatchSetsFromGraph(ctx);
+
+ assertThat(batches, contains(setOf(b, a)));
+ }
+
+
+
+
+ @Test
+ void testGraphBuildingWithExtraDependency() {
+ InferenceContext ctx = emptyCtx();
+ InferenceVar a = newIvar(ctx);
+ InferenceVar b = newIvar(ctx);
+
+ // b -> a
+ ctx.addInstantiationDependencies(setOf(b), setOf(a));
+
+ List> batches = createBatchSetsFromGraph(ctx);
+
+ assertThat(batches, contains(setOf(a), setOf(b)));
+ }
+
+ @Test
+ void testGraphBuildingWithDependencyCycle() {
+ InferenceContext ctx = emptyCtx();
+ InferenceVar a = newIvar(ctx);
+ InferenceVar b = newIvar(ctx);
+ InferenceVar c = newIvar(ctx);
+
+ // a -> b, b -> a,
+ // a -> c, b -> c
+ a.addBound(BoundKind.UPPER, b);
+ a.addBound(BoundKind.EQ, listType(c));
+ b.addBound(BoundKind.LOWER, a);
+ b.addBound(BoundKind.LOWER, listType(c));
+
+
+ List> batches = createBatchSetsFromGraph(ctx);
+
+ assertThat(batches, contains(setOf(c), setOf(b, a)));
+ }
+
}
diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/AstTestUtil.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/AstTestUtil.kt
index 1545c93f7e..e5c04d1ac5 100644
--- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/AstTestUtil.kt
+++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/AstTestUtil.kt
@@ -21,6 +21,7 @@ fun JavaNode.declaredMethodSignatures(): List = methodDeclarations()
fun JavaNode.methodCalls(): DescendantNodeStream = 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 = descendants(ASTConstructorCall::class.java)
fun JavaNode.firstCtorCall() = ctorCalls().crossFindBoundaries().firstOrThrow()
diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/UnresolvedTypesRecoveryTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/UnresolvedTypesRecoveryTest.kt
index 24d7ec3c04..45c13cf569 100644
--- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/UnresolvedTypesRecoveryTest.kt
+++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/UnresolvedTypesRecoveryTest.kt
@@ -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- loads) {
+ List 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
- 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
+ }
+ }
})
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml
index e81d963fcb..1a6dd1488d 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml
@@ -2134,4 +2134,88 @@ public class ObtainViaTest {
}
]]>
+
+ #5324 UnusedPrivateMethod with method reference
+ 0
+
> map = new Main().run(library);
+ System.out.println(map);
+ }
+
+ private Map> run(Library library) {
+ return library
+ .books()
+ .stream()
+ .map(book -> book.lenders().stream().collect(Collectors.toMap(Lender::name, lender -> Map.of(book.title(), lender.status()))))
+ .reduce(this::reduceBooksAndLenderStatusByLender)
+ .orElse(null);
+ }
+
+ private Map> reduceBooksAndLenderStatusByLender(
+ Map> previousMap,
+ Map> nextMap
+ ) {
+ previousMap.putAll(nextMap);
+ return previousMap;
+ }
+ }
+
+
+ record Lender(String name, String status) {}
+ record Book(String title, Collection lenders) {}
+ record Library(Collection books) {}
+ ]]>
+
+
+ #5324 UnusedPrivateMethod with unresolved types
+ 0
+ {
+ 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...
+ }
+ }
+ ]]>
+
+
+ #5329 UnusedPrivateMethod with unresolved types
+ 0
+ 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- b) {
+ return SummaryDto.ItemDto.builder().build();
+ }
+ }
+ ]]>
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InsufficientStringBufferDeclaration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InsufficientStringBufferDeclaration.xml
index ec267fcfcc..bd5d364eb1 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InsufficientStringBufferDeclaration.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InsufficientStringBufferDeclaration.xml
@@ -1419,4 +1419,23 @@ public class LiteralExpression {
}
]]>
+
+
+ #5314 [java] InsufficientStringBufferDeclarationRule: Lack of handling for char type parameters
+ 0
+
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/NestedLambdasAndMethodCalls.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/NestedLambdasAndMethodCalls.java
new file mode 100644
index 0000000000..33914e0a68
--- /dev/null
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/NestedLambdasAndMethodCalls.java
@@ -0,0 +1,38 @@
+package org.example.unusedPrivateMethod;
+
+import static java.util.Collections.emptySet;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class NestedLambdasAndMethodCalls {
+
+ public static void main(String[] args) {
+ Library library = new Library(emptySet());
+ Map> map = new Main().run(library);
+ System.out.println(map);
+ }
+
+ private Map> run(Library library) {
+ return library
+ .books()
+ .stream()
+ .map(book -> book.lenders().stream().collect(Collectors.toMap(Lender::name, lender -> Map.of(book.title(), lender.status()))))
+ .reduce(this::reduceBooksAndLenderStatusByLender)
+ .orElse(null);
+ }
+
+ private Map> reduceBooksAndLenderStatusByLender(
+ Map> previousMap,
+ Map> nextMap
+ ) {
+ previousMap.putAll(nextMap);
+ return previousMap;
+ }
+}
+
+
+record Lender(String name, String status) {}
+record Book(String title, Collection lenders) {}
+record Library(Collection books) {}
\ No newline at end of file
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/NestedLambdasAndMethodCalls.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/NestedLambdasAndMethodCalls.txt
new file mode 100644
index 0000000000..9099296ff8
--- /dev/null
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/NestedLambdasAndMethodCalls.txt
@@ -0,0 +1,194 @@
++- CompilationUnit[]
+ +- PackageDeclaration[]
+ | +- ModifierList[]
+ +- ImportDeclaration[]
+ +- ImportDeclaration[]
+ +- ImportDeclaration[]
+ +- ImportDeclaration[]
+ +- ClassDeclaration[@TypeMirror = "org.example.unusedPrivateMethod.NestedLambdasAndMethodCalls"]
+ | +- ModifierList[]
+ | +- ClassBody[]
+ | +- MethodDeclaration[@Name = "main"]
+ | | +- ModifierList[]
+ | | +- VoidType[@TypeMirror = "void"]
+ | | +- FormalParameters[]
+ | | | +- FormalParameter[@TypeMirror = "java.lang.String[]"]
+ | | | +- ModifierList[]
+ | | | +- ArrayType[@TypeMirror = "java.lang.String[]"]
+ | | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | | +- ArrayDimensions[]
+ | | | | +- ArrayTypeDim[]
+ | | | +- VariableId[@Name = "args", @TypeMirror = "java.lang.String[]"]
+ | | +- Block[]
+ | | +- LocalVariableDeclaration[]
+ | | | +- ModifierList[]
+ | | | +- ClassType[@TypeMirror = "org.example.unusedPrivateMethod.Library"]
+ | | | +- VariableDeclarator[]
+ | | | +- VariableId[@Name = "library", @TypeMirror = "org.example.unusedPrivateMethod.Library"]
+ | | | +- ConstructorCall[@Failed = false, @Function = "org.example.unusedPrivateMethod.Library.new(java.util.Collection) -> org.example.unusedPrivateMethod.Library", @MethodName = "new", @TypeMirror = "org.example.unusedPrivateMethod.Library", @Unchecked = false, @VarargsCall = false]
+ | | | +- ClassType[@TypeMirror = "org.example.unusedPrivateMethod.Library"]
+ | | | +- ArgumentList[]
+ | | | +- MethodCall[@Failed = false, @Function = "java.util.Collections. emptySet() -> java.util.Set", @MethodName = "emptySet", @TypeMirror = "java.util.Set", @Unchecked = false, @VarargsCall = false]
+ | | | +- ArgumentList[]
+ | | +- LocalVariableDeclaration[]
+ | | | +- ModifierList[]
+ | | | +- ClassType[@TypeMirror = "java.util.Map>"]
+ | | | | +- TypeArguments[]
+ | | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | | +- ClassType[@TypeMirror = "java.util.Map"]
+ | | | | +- TypeArguments[]
+ | | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | +- VariableDeclarator[]
+ | | | +- VariableId[@Name = "map", @TypeMirror = "java.util.Map>"]
+ | | | +- MethodCall[@Failed = true, @Function = "(*unknown*).(*unknown method*)() -> (*unknown*)", @MethodName = "run", @TypeMirror = "(*unknown*)", @Unchecked = false, @VarargsCall = false]
+ | | | +- ConstructorCall[@Failed = true, @Function = "(*unknown*).(*unknown method*)() -> (*unknown*)", @MethodName = "new", @TypeMirror = "*Main", @Unchecked = false, @VarargsCall = false]
+ | | | | +- ClassType[@TypeMirror = "*Main"]
+ | | | | +- ArgumentList[]
+ | | | +- ArgumentList[]
+ | | | +- VariableAccess[@Name = "library", @TypeMirror = "org.example.unusedPrivateMethod.Library"]
+ | | +- ExpressionStatement[]
+ | | +- MethodCall[@Failed = false, @Function = "java.io.PrintStream.println(java.lang.Object) -> void", @MethodName = "println", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false]
+ | | +- FieldAccess[@Name = "out", @TypeMirror = "java.io.PrintStream"]
+ | | | +- TypeExpression[@TypeMirror = "java.lang.System"]
+ | | | +- ClassType[@TypeMirror = "java.lang.System"]
+ | | +- ArgumentList[]
+ | | +- VariableAccess[@Name = "map", @TypeMirror = "java.util.Map>"]
+ | +- MethodDeclaration[@Name = "run"]
+ | | +- ModifierList[]
+ | | +- ClassType[@TypeMirror = "java.util.Map>"]
+ | | | +- TypeArguments[]
+ | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | +- ClassType[@TypeMirror = "java.util.Map"]
+ | | | +- TypeArguments[]
+ | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | +- FormalParameters[]
+ | | | +- FormalParameter[@TypeMirror = "org.example.unusedPrivateMethod.Library"]
+ | | | +- ModifierList[]
+ | | | +- ClassType[@TypeMirror = "org.example.unusedPrivateMethod.Library"]
+ | | | +- VariableId[@Name = "library", @TypeMirror = "org.example.unusedPrivateMethod.Library"]
+ | | +- Block[]
+ | | +- ReturnStatement[]
+ | | +- MethodCall[@Failed = false, @Function = "java.util.Optional>>.orElse(java.util.Map>) -> java.util.Map>", @MethodName = "orElse", @TypeMirror = "java.util.Map>", @Unchecked = false, @VarargsCall = false]
+ | | +- MethodCall[@Failed = false, @Function = "java.util.stream.Stream>>.reduce(java.util.function.BinaryOperator>>) -> java.util.Optional>>", @MethodName = "reduce", @TypeMirror = "java.util.Optional>>", @Unchecked = false, @VarargsCall = false]
+ | | | +- MethodCall[@Failed = false, @Function = "java.util.stream.Stream. map(java.util.function.Function super org.example.unusedPrivateMethod.Book, ? extends java.util.Map>>) -> java.util.stream.Stream>>", @MethodName = "map", @TypeMirror = "java.util.stream.Stream>>", @Unchecked = false, @VarargsCall = false]
+ | | | | +- MethodCall[@Failed = false, @Function = "java.util.Collection.stream() -> java.util.stream.Stream", @MethodName = "stream", @TypeMirror = "java.util.stream.Stream", @Unchecked = false, @VarargsCall = false]
+ | | | | | +- MethodCall[@Failed = false, @Function = "org.example.unusedPrivateMethod.Library.books() -> java.util.Collection", @MethodName = "books", @TypeMirror = "java.util.Collection", @Unchecked = false, @VarargsCall = false]
+ | | | | | | +- VariableAccess[@Name = "library", @TypeMirror = "org.example.unusedPrivateMethod.Library"]
+ | | | | | | +- ArgumentList[]
+ | | | | | +- ArgumentList[]
+ | | | | +- ArgumentList[]
+ | | | | +- LambdaExpression[@TypeMirror = "java.util.function.Function>>"]
+ | | | | +- LambdaParameterList[]
+ | | | | | +- LambdaParameter[@TypeMirror = "org.example.unusedPrivateMethod.Book"]
+ | | | | | +- ModifierList[]
+ | | | | | +- VariableId[@Name = "book", @TypeMirror = "org.example.unusedPrivateMethod.Book"]
+ | | | | +- MethodCall[@Failed = false, @Function = "java.util.stream.Stream. collect(java.util.stream.Collector super org.example.unusedPrivateMethod.Lender, java.lang.Object, java.util.Map>>) -> java.util.Map>", @MethodName = "collect", @TypeMirror = "java.util.Map>", @Unchecked = false, @VarargsCall = false]
+ | | | | +- MethodCall[@Failed = false, @Function = "java.util.Collection.stream() -> java.util.stream.Stream", @MethodName = "stream", @TypeMirror = "java.util.stream.Stream", @Unchecked = false, @VarargsCall = false]
+ | | | | | +- MethodCall[@Failed = false, @Function = "org.example.unusedPrivateMethod.Book.lenders() -> java.util.Collection", @MethodName = "lenders", @TypeMirror = "java.util.Collection", @Unchecked = false, @VarargsCall = false]
+ | | | | | | +- VariableAccess[@Name = "book", @TypeMirror = "org.example.unusedPrivateMethod.Book"]
+ | | | | | | +- ArgumentList[]
+ | | | | | +- ArgumentList[]
+ | | | | +- ArgumentList[]
+ | | | | +- MethodCall[@Failed = false, @Function = "java.util.stream.Collectors. toMap(java.util.function.Function super org.example.unusedPrivateMethod.Lender, ? extends java.lang.String>, java.util.function.Function super org.example.unusedPrivateMethod.Lender, ? extends java.util.Map>) -> java.util.stream.Collector>>", @MethodName = "toMap", @TypeMirror = "java.util.stream.Collector>>", @Unchecked = false, @VarargsCall = false]
+ | | | | +- TypeExpression[@TypeMirror = "java.util.stream.Collectors"]
+ | | | | | +- ClassType[@TypeMirror = "java.util.stream.Collectors"]
+ | | | | +- ArgumentList[]
+ | | | | +- MethodReference[@TypeMirror = "java.util.function.Function"]
+ | | | | | +- TypeExpression[@TypeMirror = "org.example.unusedPrivateMethod.Lender"]
+ | | | | | +- ClassType[@TypeMirror = "org.example.unusedPrivateMethod.Lender"]
+ | | | | +- LambdaExpression[@TypeMirror = "java.util.function.Function>"]
+ | | | | +- LambdaParameterList[]
+ | | | | | +- LambdaParameter[@TypeMirror = "org.example.unusedPrivateMethod.Lender"]
+ | | | | | +- ModifierList[]
+ | | | | | +- VariableId[@Name = "lender", @TypeMirror = "org.example.unusedPrivateMethod.Lender"]
+ | | | | +- MethodCall[@Failed = false, @Function = "java.util.Map. of(java.lang.String, java.lang.String) -> java.util.Map", @MethodName = "of", @TypeMirror = "java.util.Map", @Unchecked = false, @VarargsCall = false]
+ | | | | +- TypeExpression[@TypeMirror = "java.util.Map"]
+ | | | | | +- ClassType[@TypeMirror = "java.util.Map"]
+ | | | | +- ArgumentList[]
+ | | | | +- MethodCall[@Failed = false, @Function = "org.example.unusedPrivateMethod.Book.title() -> java.lang.String", @MethodName = "title", @TypeMirror = "java.lang.String", @Unchecked = false, @VarargsCall = false]
+ | | | | | +- VariableAccess[@Name = "book", @TypeMirror = "org.example.unusedPrivateMethod.Book"]
+ | | | | | +- ArgumentList[]
+ | | | | +- MethodCall[@Failed = false, @Function = "org.example.unusedPrivateMethod.Lender.status() -> java.lang.String", @MethodName = "status", @TypeMirror = "java.lang.String", @Unchecked = false, @VarargsCall = false]
+ | | | | +- VariableAccess[@Name = "lender", @TypeMirror = "org.example.unusedPrivateMethod.Lender"]
+ | | | | +- ArgumentList[]
+ | | | +- ArgumentList[]
+ | | | +- MethodReference[@TypeMirror = "java.util.function.BinaryOperator>>"]
+ | | | +- ThisExpression[@TypeMirror = "org.example.unusedPrivateMethod.NestedLambdasAndMethodCalls"]
+ | | +- ArgumentList[]
+ | | +- NullLiteral[@TypeMirror = "null"]
+ | +- MethodDeclaration[@Name = "reduceBooksAndLenderStatusByLender"]
+ | +- ModifierList[]
+ | +- ClassType[@TypeMirror = "java.util.Map>"]
+ | | +- TypeArguments[]
+ | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | +- ClassType[@TypeMirror = "java.util.Map"]
+ | | +- TypeArguments[]
+ | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | +- FormalParameters[]
+ | | +- FormalParameter[@TypeMirror = "java.util.Map>"]
+ | | | +- ModifierList[]
+ | | | +- ClassType[@TypeMirror = "java.util.Map>"]
+ | | | | +- TypeArguments[]
+ | | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | | +- ClassType[@TypeMirror = "java.util.Map"]
+ | | | | +- TypeArguments[]
+ | | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | +- VariableId[@Name = "previousMap", @TypeMirror = "java.util.Map>"]
+ | | +- FormalParameter[@TypeMirror = "java.util.Map>"]
+ | | +- ModifierList[]
+ | | +- ClassType[@TypeMirror = "java.util.Map>"]
+ | | | +- TypeArguments[]
+ | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | +- ClassType[@TypeMirror = "java.util.Map"]
+ | | | +- TypeArguments[]
+ | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | +- VariableId[@Name = "nextMap", @TypeMirror = "java.util.Map>"]
+ | +- Block[]
+ | +- ExpressionStatement[]
+ | | +- MethodCall[@Failed = false, @Function = "java.util.Map>.putAll(java.util.Map extends java.lang.String, ? extends java.util.Map>) -> void", @MethodName = "putAll", @TypeMirror = "void", @Unchecked = false, @VarargsCall = false]
+ | | +- VariableAccess[@Name = "previousMap", @TypeMirror = "java.util.Map>"]
+ | | +- ArgumentList[]
+ | | +- VariableAccess[@Name = "nextMap", @TypeMirror = "java.util.Map>"]
+ | +- ReturnStatement[]
+ | +- VariableAccess[@Name = "previousMap", @TypeMirror = "java.util.Map>"]
+ +- RecordDeclaration[@TypeMirror = "org.example.unusedPrivateMethod.Lender"]
+ | +- ModifierList[]
+ | +- RecordComponentList[]
+ | | +- RecordComponent[@TypeMirror = "java.lang.String"]
+ | | | +- ModifierList[]
+ | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | +- VariableId[@Name = "name", @TypeMirror = "java.lang.String"]
+ | | +- RecordComponent[@TypeMirror = "java.lang.String"]
+ | | +- ModifierList[]
+ | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | +- VariableId[@Name = "status", @TypeMirror = "java.lang.String"]
+ | +- RecordBody[]
+ +- RecordDeclaration[@TypeMirror = "org.example.unusedPrivateMethod.Book"]
+ | +- ModifierList[]
+ | +- RecordComponentList[]
+ | | +- RecordComponent[@TypeMirror = "java.lang.String"]
+ | | | +- ModifierList[]
+ | | | +- ClassType[@TypeMirror = "java.lang.String"]
+ | | | +- VariableId[@Name = "title", @TypeMirror = "java.lang.String"]
+ | | +- RecordComponent[@TypeMirror = "java.util.Collection"]
+ | | +- ModifierList[]
+ | | +- ClassType[@TypeMirror = "java.util.Collection"]
+ | | | +- TypeArguments[]
+ | | | +- ClassType[@TypeMirror = "org.example.unusedPrivateMethod.Lender"]
+ | | +- VariableId[@Name = "lenders", @TypeMirror = "java.util.Collection"]
+ | +- RecordBody[]
+ +- RecordDeclaration[@TypeMirror = "org.example.unusedPrivateMethod.Library"]
+ +- ModifierList[]
+ +- RecordComponentList[]
+ | +- RecordComponent[@TypeMirror = "java.util.Collection"]
+ | +- ModifierList[]
+ | +- ClassType[@TypeMirror = "java.util.Collection"]
+ | | +- TypeArguments[]
+ | | +- ClassType[@TypeMirror = "org.example.unusedPrivateMethod.Book"]
+ | +- VariableId[@Name = "books", @TypeMirror = "java.util.Collection"]
+ +- RecordBody[]
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnnamedPatterns.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnnamedPatterns.txt
index 25eb7dd0d7..e1fa78ded1 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnnamedPatterns.txt
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnnamedPatterns.txt
@@ -583,7 +583,7 @@
| +- ArgumentList[]
| +- StringLiteral[@TypeMirror = "java.lang.String"]
+- ExpressionStatement[]
- +- MethodCall[@Failed = false, @Function = "java.util.stream.Stream. collect(java.util.stream.Collector super java.lang.String, java.lang.Object, java.util.Map>) -> java.util.Map", @MethodName = "collect", @TypeMirror = "java.util.Map", @Unchecked = false, @VarargsCall = false]
+ +- MethodCall[@Failed = false, @Function = "java.util.stream.Stream. collect(java.util.stream.Collector super java.lang.String, java.lang.Object, java.util.Map>) -> java.util.Map", @MethodName = "collect", @TypeMirror = "java.util.Map", @Unchecked = false, @VarargsCall = false]
+- MethodCall[@Failed = false, @Function = "java.util.Collection.stream() -> java.util.stream.Stream", @MethodName = "stream", @TypeMirror = "java.util.stream.Stream", @Unchecked = false, @VarargsCall = false]
| +- MethodCall[@Failed = false, @Function = "java.util.List. of(java.lang.String, java.lang.String) -> java.util.List", @MethodName = "of", @TypeMirror = "java.util.List", @Unchecked = false, @VarargsCall = false]
| | +- TypeExpression[@TypeMirror = "java.util.List"]
@@ -593,14 +593,14 @@
| | +- StringLiteral[@TypeMirror = "java.lang.String"]
| +- ArgumentList[]
+- ArgumentList[]
- +- MethodCall[@Failed = false, @Function = "java.util.stream.Collectors. toMap(java.util.function.Function super java.lang.String, ?>, java.util.function.Function super java.lang.String, ?>) -> java.util.stream.Collector>", @MethodName = "toMap", @TypeMirror = "java.util.stream.Collector>", @Unchecked = false, @VarargsCall = false]
+ +- MethodCall[@Failed = false, @Function = "java.util.stream.Collectors. toMap(java.util.function.Function super java.lang.String, ?>, java.util.function.Function super java.lang.String, ? extends java.lang.String>) -> java.util.stream.Collector>", @MethodName = "toMap", @TypeMirror = "java.util.stream.Collector>", @Unchecked = false, @VarargsCall = false]
+- TypeExpression[@TypeMirror = "java.util.stream.Collectors"]
| +- ClassType[@TypeMirror = "java.util.stream.Collectors"]
+- ArgumentList[]
+- MethodReference[@TypeMirror = "java.util.function.Function"]
| +- TypeExpression[@TypeMirror = "java.lang.String"]
| +- ClassType[@TypeMirror = "java.lang.String"]
- +- LambdaExpression[@TypeMirror = "java.util.function.Function"]
+ +- LambdaExpression[@TypeMirror = "java.util.function.Function"]
+- LambdaParameterList[]
| +- LambdaParameter[@TypeMirror = "java.lang.String"]
| +- ModifierList[]
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnresolvedThings.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnresolvedThings.java
new file mode 100644
index 0000000000..83eb9d62b4
--- /dev/null
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnresolvedThings.java
@@ -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- loads) {
+ List 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
- b) {
+ return SummaryDto.ItemDto.builder().build();
+ }
+}
\ No newline at end of file
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnresolvedThings.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnresolvedThings.txt
new file mode 100644
index 0000000000..32f1f2643e
--- /dev/null
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/types/dumptests/UnresolvedThings.txt
@@ -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>. 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. 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[]