feat(java): suppress java stdlib types from references edges (#1603)

Adds a _JAVA_BUILTIN_TYPES skip list so ubiquitous java.lang/util/io/time/etc.
type names (String, List, Map, Optional, ...) are not emitted as references
edges (they never resolve to a project node). Mirrors _GO_PREDECLARED_TYPES /
_PYTHON_ANNOTATION_NOISE. Nested user-type generic args still resolve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
NydiaChung
2026-07-06 01:01:53 +01:00
committed by safishamsi
co-authored by Claude Opus 4.8
parent f2b81a9980
commit 92edf78005
3 changed files with 124 additions and 4 deletions
+70 -3
View File
@@ -868,6 +868,69 @@ def _java_type_parameters_in_scope(node, source: bytes) -> frozenset[str]:
return frozenset(names)
# java.lang (auto-imported) plus the ubiquitous java.util / java.io / java.time /
# java.util.{stream,function,concurrent} / java.math / java.nio.file types that
# appear as field, parameter, return, and generic-argument annotations. They never
# resolve to a project node, so emitting `references` edges to them is pure noise
# (mirrors _GO_PREDECLARED_TYPES / _PYTHON_ANNOTATION_NOISE). Suppressed at the
# type-ref walker so they are never created as nodes or emitted as edges. The
# boxed-scalar/`void` primitives are already dropped by grammar node type above;
# these are the class/interface names the grammar reports as identifiers.
_JAVA_BUILTIN_TYPES = frozenset({
# java.lang — core
"Object", "String", "CharSequence", "StringBuilder", "StringBuffer",
"Number", "Byte", "Short", "Integer", "Long", "Float", "Double",
"Boolean", "Character", "Void", "Class", "Enum", "Record", "Math",
"System", "Thread", "Runnable", "Comparable", "Iterable", "Cloneable",
"AutoCloseable", "Appendable", "Readable", "Process", "ProcessBuilder",
"Runtime", "Package", "ThreadLocal", "InheritableThreadLocal",
# java.lang — throwables
"Throwable", "Exception", "RuntimeException", "Error",
"IllegalArgumentException", "IllegalStateException", "NullPointerException",
"IndexOutOfBoundsException", "ArrayIndexOutOfBoundsException",
"ClassCastException", "NumberFormatException", "ArithmeticException",
"UnsupportedOperationException", "InterruptedException",
"CloneNotSupportedException", "SecurityException", "StackOverflowError",
"OutOfMemoryError", "AssertionError",
# java.util — collections & core
"Collection", "List", "ArrayList", "LinkedList", "Vector", "Stack",
"Set", "HashSet", "LinkedHashSet", "TreeSet", "SortedSet", "NavigableSet",
"EnumSet", "Map", "HashMap", "LinkedHashMap", "TreeMap", "SortedMap",
"NavigableMap", "Hashtable", "EnumMap", "Properties", "Queue", "Deque",
"ArrayDeque", "PriorityQueue", "Iterator", "ListIterator", "Comparator",
"Optional", "OptionalInt", "OptionalLong", "OptionalDouble", "Collections",
"Arrays", "Objects", "Date", "Calendar", "Random", "UUID", "Scanner",
"StringJoiner", "StringTokenizer", "BitSet", "Spliterator", "Locale",
"NoSuchElementException", "ConcurrentModificationException",
# java.util.stream
"Stream", "IntStream", "LongStream", "DoubleStream", "Collector",
"Collectors",
# java.util.function
"Function", "BiFunction", "Consumer", "BiConsumer", "Supplier",
"Predicate", "BiPredicate", "UnaryOperator", "BinaryOperator",
"IntFunction", "ToIntFunction", "ToLongFunction", "ToDoubleFunction",
# java.util.concurrent
"Callable", "Future", "CompletableFuture", "CompletionStage", "Executor",
"ExecutorService", "Executors", "ScheduledExecutorService", "TimeUnit",
"ConcurrentHashMap", "ConcurrentMap", "CopyOnWriteArrayList",
"BlockingQueue", "CountDownLatch", "Semaphore", "CyclicBarrier",
"AtomicInteger", "AtomicLong", "AtomicBoolean", "AtomicReference",
# java.time
"Instant", "Duration", "Period", "LocalDate", "LocalTime", "LocalDateTime",
"ZonedDateTime", "OffsetDateTime", "ZoneId", "ZoneOffset", "DayOfWeek",
"Month", "Year", "Clock", "DateTimeFormatter",
# java.io / java.nio.file
"IOException", "UncheckedIOException", "FileNotFoundException", "File",
"InputStream", "OutputStream", "Reader", "Writer", "BufferedReader",
"BufferedWriter", "InputStreamReader", "OutputStreamWriter", "FileReader",
"FileWriter", "PrintStream", "PrintWriter", "ByteArrayInputStream",
"ByteArrayOutputStream", "Serializable", "Closeable", "Path", "Paths",
"Files",
# java.math
"BigDecimal", "BigInteger",
})
def _java_collect_type_refs(
node,
source: bytes,
@@ -885,19 +948,23 @@ def _java_collect_type_refs(
return
if t == "type_identifier":
name = _read_text(node, source)
if name and name not in skip:
if name and name not in skip and name not in _JAVA_BUILTIN_TYPES:
out.append((name, "generic_arg" if generic else "type"))
return
if t == "scoped_type_identifier":
text = _read_text(node, source).rsplit(".", 1)[-1]
if text:
if text and text not in _JAVA_BUILTIN_TYPES:
out.append((text, "generic_arg" if generic else "type"))
return
if t == "generic_type":
for c in node.children:
if c.type in ("type_identifier", "scoped_type_identifier"):
text = _read_text(c, source).rsplit(".", 1)[-1]
if text and (c.type == "scoped_type_identifier" or text not in skip):
if (
text
and text not in _JAVA_BUILTIN_TYPES
and (c.type == "scoped_type_identifier" or text not in skip)
):
out.append((text, "generic_arg" if generic else "type"))
break
for c in node.children:
+51
View File
@@ -150,6 +150,57 @@ def test_java_type_parameters_do_not_resolve_to_real_class(tmp_path: Path):
assert ("Generic", "references", "T") not in references
def test_java_builtin_library_types_not_emitted_as_references(tmp_path: Path):
# Built-in / standard-library types (java.lang, java.util, …) used as field,
# parameter, or return types carry no useful graph meaning: they never resolve
# to a project node, so emitting `references` edges to them is pure noise.
svc = _write(
tmp_path / "Svc.java",
"package com.app;\n"
"import java.util.List;\n"
"import java.util.Map;\n"
"public class Svc {\n"
" private String name;\n"
" private List<Integer> ids;\n"
" public Map<String, Object> lookup(Long id) { return null; }\n"
" public java.util.Optional<Boolean> flag() { return null; }\n"
"}\n",
)
result = extract([svc], cache_root=tmp_path)
ref_targets = {
by_label
for (src, rel, by_label) in _label_edges(result, {"references"})
}
for builtin in (
"String", "Integer", "Map", "Object", "Long",
"List", "Optional", "Boolean",
):
assert builtin not in ref_targets, (
f"builtin/library type {builtin!r} should not be a references target"
)
def test_java_user_types_still_emit_references(tmp_path: Path):
# Guard against over-skipping: a user-defined type sharing the field/return
# shape must still resolve to a real `references` edge.
dto = _write(tmp_path / "OrderDto.java",
"package com.app;\npublic class OrderDto {}\n")
svc = _write(
tmp_path / "OrderSvc.java",
"package com.app;\n"
"public class OrderSvc {\n"
" private java.util.List<OrderDto> orders;\n"
" public OrderDto first() { return null; }\n"
"}\n",
)
result = extract([dto, svc], cache_root=tmp_path)
ref_targets = {
by_label for (_, _, by_label) in _label_edges(result, {"references"})
}
assert "OrderDto" in ref_targets, "user type OrderDto must still emit references"
def test_java_cross_file_constructor_call_resolves(tmp_path: Path):
# #1373: `new Foo(...)` in a method body must produce a cross-file edge to the
# Foo definition. Foo is NOT used as a return type here, so the edge can only
+3 -1
View File
@@ -472,7 +472,9 @@ def test_java_record_component_type_references(tmp_path):
result = extract_java(source)
assert ("Order", "Payload") in _edge_labels(result, "references", "field")
assert ("Order", "List") in _edge_labels(result, "references", "field")
# `List` is a java.util library type: skipped as noise, so only its user-type
# generic argument (`Item`) survives, not the container itself.
assert ("Order", "List") not in _edge_labels(result, "references")
assert ("Order", "Item") in _edge_labels(result, "references", "generic_arg")
assert ("Order", "Attachment") in _edge_labels(result, "references", "field")