Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions graphify/extractors/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,35 @@ def _php_method_return_type_node(method_node):
return c
return None

# Kotlin stdlib scalar/collection/core types that appear constantly as type
# annotations but carry no useful semantic meaning as graph nodes (mirrors
# _JAVA_BUILTIN_TYPES / _PYTHON_ANNOTATION_NOISE / _GO_PREDECLARED_TYPES).
# Kotlin compiles to the JVM and freely references java.* types too, so this
# is combined with _JAVA_BUILTIN_TYPES at the call site rather than duplicated.
_KOTLIN_BUILTIN_TYPES = frozenset({
# kotlin — scalars & core
"Any", "Unit", "Nothing", "Boolean", "Byte", "Short", "Int", "Long",
"Float", "Double", "Char", "String", "CharSequence", "Number",
"Comparable", "Enum", "Annotation", "Pair", "Triple", "Lazy",
"Function",
# kotlin — throwables
"Throwable", "Exception", "RuntimeException", "Error",
"IllegalArgumentException", "IllegalStateException", "NullPointerException",
"IndexOutOfBoundsException", "ClassCastException", "NumberFormatException",
"ArithmeticException", "UnsupportedOperationException",
"NoSuchElementException", "ConcurrentModificationException",
"StackOverflowError", "OutOfMemoryError", "AssertionError",
"InterruptedException",
# kotlin.collections
"Array", "List", "MutableList", "ArrayList", "Set", "MutableSet",
"HashSet", "LinkedHashSet", "Map", "MutableMap", "HashMap",
"LinkedHashMap", "Collection", "MutableCollection", "Iterable",
"MutableIterable", "Iterator", "MutableIterator", "ListIterator",
"MutableListIterator", "Sequence", "Comparator",
# kotlin.text
"Regex", "MatchResult", "StringBuilder",
})

def _kotlin_user_type_name(user_type_node, source: bytes) -> str | None:
"""Return the head identifier text from a Kotlin user_type node (without generics)."""
if user_type_node is None:
Expand Down Expand Up @@ -636,14 +665,14 @@ def _kotlin_collect_type_refs(node, source: bytes, generic: bool, out: list[tupl
for c in node.children:
if c.type in ("identifier", "type_identifier"):
text = _read_text(c, source)
if text:
if text and text not in _KOTLIN_BUILTIN_TYPES and text not in _JAVA_BUILTIN_TYPES:
out.append((text, "generic_arg" if generic else "type"))
break
if c.type == "simple_user_type":
for sub in c.children:
if sub.type in ("identifier", "type_identifier"):
text = _read_text(sub, source)
if text:
if text and text not in _KOTLIN_BUILTIN_TYPES and text not in _JAVA_BUILTIN_TYPES:
out.append((text, "generic_arg" if generic else "type"))
break
break
Expand All @@ -659,7 +688,7 @@ def _kotlin_collect_type_refs(node, source: bytes, generic: bool, out: list[tupl
return
if t in ("identifier", "type_identifier"):
text = _read_text(node, source)
if text:
if text and text not in _KOTLIN_BUILTIN_TYPES and text not in _JAVA_BUILTIN_TYPES:
out.append((text, "generic_arg" if generic else "type"))
return
if t in ("nullable_type", "parenthesized_type", "type_reference"):
Expand Down
21 changes: 21 additions & 0 deletions tests/test_languages.py
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,27 @@ def test_kotlin_parameter_return_generic_and_field_contexts():
assert ("run", "DataProcessor") in _edge_labels(r, "references", "generic_arg")
assert ("DataProcessor", "Result") in _edge_labels(r, "references", "field")

def test_kotlin_builtin_types_not_emitted_as_references():
# kotlin.* scalar/collection/core types used as parameter, return, or field
# types carry no useful graph meaning: they never resolve to a project node,
# so emitting `references` edges to them is pure noise (mirrors the Java
# _JAVA_BUILTIN_TYPES / Python _PYTHON_ANNOTATION_NOISE handling).
r = extract_kotlin(FIXTURES / "sample.kt")
ref_targets = {target for (_, target) in _edge_labels(r, "references")}
for builtin in ("String", "Int"):
assert builtin not in ref_targets, (
f"builtin type {builtin!r} should not be a references target"
)

def test_kotlin_user_types_still_emit_references():
# Guard against over-filtering: a user-defined class sharing its name with a
# common domain-modeling identifier (Result) must still resolve to a real
# edge - the builtin filter is a fixed name list, so it must stay narrow
# enough not to swallow common user-chosen names like a sealed-class "Result".
r = extract_kotlin(FIXTURES / "sample.kt")
assert ("DataProcessor", "Result") in _edge_labels(r, "references", "field")
assert ("run", "DataProcessor") in _edge_labels(r, "references", "parameter_type")


# ── Scala ─────────────────────────────────────────────────────────────────────

Expand Down