adl-toolkit 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/INSTALL_TOOLKIT.md +163 -0
  2. package/README.md +269 -2
  3. package/adl_drift/__init__.py +6 -0
  4. package/adl_drift/cli/__init__.py +1 -0
  5. package/adl_drift/cli/commands.py +993 -0
  6. package/adl_drift/cli/main.py +744 -0
  7. package/adl_drift/cortex/__init__.py +1 -0
  8. package/adl_drift/cortex/embeddings.py +96 -0
  9. package/adl_drift/cortex/memory.py +1749 -0
  10. package/adl_drift/cortex/patterns.py +89 -0
  11. package/adl_drift/cortex/telemetry.py +192 -0
  12. package/adl_drift/detectors/__init__.py +1 -0
  13. package/adl_drift/detectors/async_patterns.py +115 -0
  14. package/adl_drift/detectors/base.py +121 -0
  15. package/adl_drift/detectors/config.py +122 -0
  16. package/adl_drift/detectors/data_access.py +128 -0
  17. package/adl_drift/detectors/decorators.py +122 -0
  18. package/adl_drift/detectors/dynamic_patterns.py +260 -0
  19. package/adl_drift/detectors/errors.py +134 -0
  20. package/adl_drift/detectors/imports.py +132 -0
  21. package/adl_drift/detectors/logging_patterns.py +108 -0
  22. package/adl_drift/detectors/routes.py +257 -0
  23. package/adl_drift/detectors/security.py +236 -0
  24. package/adl_drift/detectors/taint.py +406 -0
  25. package/adl_drift/detectors/tests.py +95 -0
  26. package/adl_drift/detectors/type_conventions.py +81 -0
  27. package/adl_drift/graph/__init__.py +1 -0
  28. package/adl_drift/graph/builder.py +2670 -0
  29. package/adl_drift/graph/cfg.py +461 -0
  30. package/adl_drift/graph/cluster.py +40 -0
  31. package/adl_drift/graph/edges.py +86 -0
  32. package/adl_drift/graph/node.py +90 -0
  33. package/adl_drift/harness/__init__.py +10 -0
  34. package/adl_drift/harness/setup.py +832 -0
  35. package/adl_drift/harness/skills/drift-antigravity.md +19 -0
  36. package/adl_drift/harness/skills/drift-generic.md +19 -0
  37. package/adl_drift/harness/skills/drift.md +25 -0
  38. package/adl_drift/harness/watch.py +84 -0
  39. package/adl_drift/lang/__init__.py +8 -0
  40. package/adl_drift/lang/base.py +464 -0
  41. package/adl_drift/lang/java/__init__.py +6 -0
  42. package/adl_drift/lang/java/backend.py +97 -0
  43. package/adl_drift/lang/java/cfg.py +1424 -0
  44. package/adl_drift/lang/java/config.py +707 -0
  45. package/adl_drift/lang/java/detectors/__init__.py +29 -0
  46. package/adl_drift/lang/java/detectors/additional.py +445 -0
  47. package/adl_drift/lang/java/detectors/base.py +154 -0
  48. package/adl_drift/lang/java/detectors/data_access.py +148 -0
  49. package/adl_drift/lang/java/detectors/imports.py +53 -0
  50. package/adl_drift/lang/java/detectors/routes.py +159 -0
  51. package/adl_drift/lang/java/detectors/security.py +386 -0
  52. package/adl_drift/lang/java/detectors/tests.py +74 -0
  53. package/adl_drift/lang/java/extractor.py +582 -0
  54. package/adl_drift/lang/java/ids.py +99 -0
  55. package/adl_drift/lang/java/parser.py +49 -0
  56. package/adl_drift/lang/java/resolution/__init__.py +1 -0
  57. package/adl_drift/lang/java/resolution/constants.py +1619 -0
  58. package/adl_drift/lang/java/resolution/constructors.py +278 -0
  59. package/adl_drift/lang/java/resolution/context.py +305 -0
  60. package/adl_drift/lang/java/resolution/dispatch.py +540 -0
  61. package/adl_drift/lang/java/resolution/functional.py +1318 -0
  62. package/adl_drift/lang/java/resolution/generated.py +95 -0
  63. package/adl_drift/lang/java/resolution/model.py +153 -0
  64. package/adl_drift/lang/java/resolution/orchestration.py +866 -0
  65. package/adl_drift/lang/java/resolution/overloads.py +1275 -0
  66. package/adl_drift/lang/java/resolution/scopes.py +1111 -0
  67. package/adl_drift/lang/java/resolution/types.py +2947 -0
  68. package/adl_drift/lang/java/resolver.py +1401 -0
  69. package/adl_drift/lang/java/scopes.py +69 -0
  70. package/adl_drift/lang/java/taint.py +500 -0
  71. package/adl_drift/lang/java/type_provenance.py +342 -0
  72. package/adl_drift/lang/python/__init__.py +8 -0
  73. package/adl_drift/lang/python/backend.py +94 -0
  74. package/adl_drift/lang/python/cfg.py +537 -0
  75. package/adl_drift/lang/python/extractor.py +454 -0
  76. package/adl_drift/lang/python/parser.py +41 -0
  77. package/adl_drift/lang/python/resolver.py +1789 -0
  78. package/adl_drift/lang/python/taint.py +365 -0
  79. package/adl_drift/lang/registry.py +117 -0
  80. package/adl_drift/parser/__init__.py +1 -0
  81. package/adl_drift/parser/cache.py +38 -0
  82. package/adl_drift/parser/core.py +33 -0
  83. package/adl_drift/parser/extractor.py +17 -0
  84. package/adl_drift/parser/literal_eval.py +326 -0
  85. package/adl_drift/scanner/__init__.py +1 -0
  86. package/adl_drift/scanner/discovery.py +338 -0
  87. package/adl_drift/scanner/incremental.py +261 -0
  88. package/adl_drift/scanner/parallel.py +224 -0
  89. package/adl_drift/server/__init__.py +1 -0
  90. package/adl_drift/server/handlers.py +2849 -0
  91. package/adl_drift/server/mcp_server.py +127 -0
  92. package/adl_drift/server/mcp_stdio.py +335 -0
  93. package/adl_drift/server/protocol.py +71 -0
  94. package/adl_drift/server/static/index.html +696 -0
  95. package/adl_drift/server/web.py +350 -0
  96. package/adl_drift/utils/__init__.py +1 -0
  97. package/adl_drift/utils/config.py +77 -0
  98. package/adl_drift/utils/fswalk.py +52 -0
  99. package/adl_drift/utils/types.py +345 -0
  100. package/bin/adl-drift.mjs +35 -0
  101. package/bin/adl-toolkit.mjs +488 -0
  102. package/package.json +29 -4
  103. package/pyproject.toml +49 -0
  104. package/scripts/install_toolkit.py +3584 -0
@@ -0,0 +1,1619 @@
1
+ """Policy tables and exact external facts used by Java call resolution."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from typing import Dict, FrozenSet, List, Set, Tuple, Union
7
+
8
+ from adl_drift.lang.java.scopes import TYPE_BODIES
9
+
10
+ # P2.7-equivalent deny-list for Java: `java.lang.Object` methods (present on
11
+ # every instance whether declared or not) + common Collection/Map/Iterator
12
+ # method names. Unlike Python's `PYTHON_BUILTINS` (which also guards a
13
+ # whole-project unique-simple-name fallback that Java's call resolver has no
14
+ # equivalent of), `JavaCallResolver` no longer consults this set at all for
15
+ # call resolution — own-class and ancestor lookup are both fully structural
16
+ # (real CONTAINS/INHERITS/IMPLEMENTS edges, never a guess), so gating either
17
+ # of them here only cost recall for zero precision benefit (P4.4 regression
18
+ # fix; `_resolve_own_or_ancestor` used to early-return unresolved whenever
19
+ # the receiver's own class didn't declare a denied name itself, even when an
20
+ # ancestor genuinely did). `JAVA_BUILTINS` still backs
21
+ # `ResolverConfig.builtins_deny_list`, consumed by
22
+ # `CallGraphBuilder._is_denied()` for the language-agnostic `god_nodes()`
23
+ # metric — that usage is unrelated to call resolution and unaffected here.
24
+ JAVA_OBJECT_METHODS: Set[str] = {
25
+ "equals", "hashCode", "toString", "getClass", "clone", "finalize",
26
+ "notify", "notifyAll", "wait",
27
+ }
28
+ JAVA_COMMON_COLLECTION_METHODS: Set[str] = {
29
+ "get", "set", "add", "remove", "size", "isEmpty", "contains", "clear",
30
+ "put", "putAll", "containsKey", "containsValue", "keySet", "values",
31
+ "entrySet", "iterator", "next", "hasNext", "forEach", "stream",
32
+ "toArray", "addAll", "removeAll", "retainAll", "indexOf", "sort",
33
+ "getOrDefault", "compute", "computeIfAbsent", "computeIfPresent",
34
+ "merge", "of", "asList", "peek", "poll", "push", "pop", "offer",
35
+ }
36
+ JAVA_BUILTINS: Set[str] = JAVA_OBJECT_METHODS | JAVA_COMMON_COLLECTION_METHODS
37
+
38
+ # Lombok-style annotation simple name -> name *prefixes* it generates on the
39
+ # annotated class. Illustrative, not exhaustive — the Java analogue of
40
+ # DECORATOR_STUB_TABLE, scoped to "simple proxies" per this issue; Spring
41
+ # AOP/DI dispatch is P4.12, not here.
42
+ JAVA_ANNOTATION_STUB_TABLE: Dict[str, FrozenSet[str]] = {
43
+ "Getter": frozenset({"get", "is"}),
44
+ "Setter": frozenset({"set"}),
45
+ "Data": frozenset({"get", "is", "set", "toString", "hashCode", "equals", "canEqual"}),
46
+ "Value": frozenset({"get", "is", "toString", "hashCode", "equals", "canEqual"}),
47
+ "Builder": frozenset({"build", "builder", "toBuilder"}),
48
+ "EqualsAndHashCode": frozenset({"equals", "hashCode", "canEqual"}),
49
+ "ToString": frozenset({"toString"}),
50
+ }
51
+
52
+ # argument_list / formal_parameters punctuation children to skip when
53
+ # counting/walking positional entries.
54
+ _PUNCTUATION = (",", "(", ")")
55
+
56
+ # Tree-sitter keeps comments as *named* children of `argument_list`
57
+ # (`line_comment` / `block_comment`), so `call(a, // note\n b)` has three
58
+ # named children but two arguments. Punctuation is unnamed; comments are
59
+ # named trivia. Every positional walk over an argument list must go through
60
+ # here so arity and per-argument typing never disagree.
61
+ _COMMENT_CST_TYPES = frozenset({"line_comment", "block_comment", "comment"})
62
+
63
+
64
+ def argument_nodes(args_node) -> list:
65
+ """Positional argument expressions of an `argument_list` node, in order,
66
+ skipping punctuation and comment trivia. `None` -> `[]`."""
67
+ if args_node is None:
68
+ return []
69
+ return [
70
+ child for child in args_node.children
71
+ if child.is_named and child.type not in _COMMENT_CST_TYPES
72
+ ]
73
+
74
+ _LITERAL_ARG_TYPE = {
75
+ "character_literal": "char",
76
+ "string_literal": "String",
77
+ "true": "boolean",
78
+ "false": "boolean",
79
+ }
80
+
81
+ # Sentinel "type" for the `null` literal. `null` is a Java keyword, so no real
82
+ # type is ever named `null` and this cannot collide with a declared type. It
83
+ # carries the JLS "null type": assignable to every *reference* type (class,
84
+ # interface, array, boxed primitive), never to a primitive. Treated as a
85
+ # *known* argument type by overload selection (not "unknown"), so `save(null)`
86
+ # runs through most-specific ranking among reference candidates instead of
87
+ # fanning out across every arity match — see `_types_compatible`.
88
+ _NULL_ARG_TYPE = "null"
89
+ # Must survive `erased_type()` unchanged. Angle-bracket sentinels are invalid
90
+ # here because erasure correctly treats them as a generic argument list and
91
+ # reduces the value to an empty type.
92
+ _LAMBDA_ARG_TYPE = "$drift_lambda$"
93
+ # Placeholder for one argument position whose type genuinely cannot be
94
+ # determined (an external/JDK method-call result with no exact-return fact,
95
+ # for example). Unlike `_NULL_ARG_TYPE`/`_LAMBDA_ARG_TYPE` — both real,
96
+ # *known* facts about the argument — this means "no information," so
97
+ # `_types_compatible` treats it as compatible with every parameter shape
98
+ # rather than ruling anything in or out. It lets a mixed argument list (some
99
+ # positions known, one unresolvable) still exclude a candidate that is
100
+ # provably incompatible on a position that *is* known, without ever
101
+ # guessing the unresolvable one. See `_narrow_candidates_by_known_arg_types`.
102
+ _UNKNOWN_ARG_TYPE = "$drift_unknown$"
103
+ # Marks a local variable initialized from a Mockito `mock(...)`/`spy(...)`
104
+ # factory call (see `ScopeInferenceMixin._is_mockito_factory_call`). Stored
105
+ # through the same allocation-tracking path as a real `new Impl()` so
106
+ # `_abstract_fan_out`'s allocation-narrowing step can recognize it and
107
+ # decline to fan out at all — a mock has no real declared implementation to
108
+ # dispatch to, so guessing any concrete implementor (unrelated project code
109
+ # that happens to declare the same interface method) is a wrong edge every
110
+ # stubbed call site, not an unresolved one.
111
+ _MOCK_ALLOCATION_TYPE = "$drift_mock$"
112
+ _JAVA_FUNCTIONAL_INTERFACES: FrozenSet[str] = frozenset({
113
+ "BooleanSupplier", "Callable", "Comparator", "Consumer", "BiConsumer", "Function",
114
+ "BiFunction", "Predicate", "BiPredicate", "Supplier", "Runnable",
115
+ "UnaryOperator", "BinaryOperator",
116
+ })
117
+ # (abstract-method arity, is-void-returning) for each JDK functional
118
+ # interface above. Used only to narrow a lambda-expression argument's
119
+ # candidate overloads by two syntactic, JLS-exact facts about the lambda
120
+ # itself — its own declared parameter count, and (for a block body) whether
121
+ # it contains a `return <expr>;` — against a *sibling* candidate's SAM shape
122
+ # (E1, JAVA_METRICS_SPRING_MODULITH_PRECISION_SAMPLE.md:
123
+ # `Scenario.stimulate(Runnable/Supplier/Function/BiConsumer/BiFunction)` all
124
+ # accepted any lambda regardless of its own arity/return shape). Excludes
125
+ # `Comparator`/`BiPredicate` from the shape table entries that would collide
126
+ # with `Function`/`BiFunction`'s arity on purpose: `Comparator` and
127
+ # `BiFunction` are both arity-2/non-void, so they are never distinguishable
128
+ # by shape alone anyway; leaving both in the table changes nothing since
129
+ # neither ever gets excluded from the other.
130
+ _JAVA_FUNCTIONAL_INTERFACE_SHAPE: Dict[str, Tuple[int, bool]] = {
131
+ "BooleanSupplier": (0, False),
132
+ "Callable": (0, False),
133
+ "Comparator": (2, False),
134
+ "Consumer": (1, True),
135
+ "BiConsumer": (2, True),
136
+ "Function": (1, False),
137
+ "BiFunction": (2, False),
138
+ "Predicate": (1, False),
139
+ "BiPredicate": (2, False),
140
+ "Supplier": (0, False),
141
+ "Runnable": (0, True),
142
+ "UnaryOperator": (1, False),
143
+ "BinaryOperator": (2, False),
144
+ }
145
+
146
+ # Integer/floating-point literal node types whose Java type depends on a
147
+ # suffix character in the literal *text*, not just the node type (a
148
+ # `decimal_integer_literal` is `int` by default but `long` when it ends in
149
+ # `l`/`L`; a `decimal_floating_point_literal` is `double` by default but
150
+ # `float` when it ends in `f`/`F` — JLS 3.10.1/3.10.2). Same rule applies to
151
+ # hex/octal/binary integer literals.
152
+ _INTEGER_LITERAL_TYPES = frozenset({
153
+ "decimal_integer_literal", "hex_integer_literal",
154
+ "octal_integer_literal", "binary_integer_literal",
155
+ })
156
+ _FLOATING_POINT_LITERAL_TYPES = frozenset({
157
+ "decimal_floating_point_literal", "hex_floating_point_literal",
158
+ })
159
+
160
+ # The CST node types that carry a caller body `resolve_calls` must walk:
161
+ # regular methods, ordinary constructors, and a `record`'s *compact*
162
+ # canonical constructor (`R { ... }`) — the last has a real body whose call
163
+ # sites would otherwise be dropped (it is a distinct node type from
164
+ # `constructor_declaration`). Shared by `_iter_method_cst` (what to yield as
165
+ # a caller) and `_NESTED_SCOPE_BOUNDARY` (where a body walk must stop
166
+ # descending) so the two never drift; `JavaSymbolExtractor._CONSTRUCTOR_TYPES`
167
+ # is the extractor-side counterpart that emits the matching FUNCTION nodes.
168
+ _METHOD_CST_TYPES = (
169
+ "method_declaration", "constructor_declaration", "compact_constructor_declaration",
170
+ )
171
+
172
+ # Node types whose call sites belong to a *different* enclosing method than
173
+ # whatever body we're currently walking — mirrors Python's "don't descend
174
+ # into nested function_definition" guard in `_collect_calls`. Java can't
175
+ # nest a method inside a method, but a method body can contain a local or
176
+ # anonymous class with its own `method_declaration`(s); those get their own
177
+ # top-level pass in `_iter_method_cst`; a type-body boundary marks where
178
+ # nested-class call sites start belonging to that class's own methods. Uses
179
+ # `scopes.TYPE_BODIES`, not just `class_body` — tree-sitter-java pairs
180
+ # `interface_declaration`/`enum_declaration`/`annotation_type_declaration`
181
+ # with their own `interface_body`/`enum_body`/`annotation_type_body`, so a
182
+ # `class_body`-only check walks straight through a local enum's `static {
183
+ # }` initializer or an interface constant's field initializer and
184
+ # misattributes its call sites to the enclosing method (PROBLEMS.md P4.6;
185
+ # same bug fixed in `lang/java/taint.py::_NESTED_SCOPE_BOUNDARY`, confirmed
186
+ # live via a spurious `CALLS` edge before this fix).
187
+ _NESTED_SCOPE_BOUNDARY = (*_METHOD_CST_TYPES, *TYPE_BODIES)
188
+
189
+ # Direct-parent node types of a bare instance-initializer `{ }` block (JLS
190
+ # 8.6) — i.e. every `TYPE_BODIES` container *except* that an enum's constant
191
+ # list wraps its trailing member section (fields, initializer blocks,
192
+ # methods) in an extra `enum_body_declarations` node tree-sitter-java uses
193
+ # nowhere else, so `block.parent.type` there is `enum_body_declarations`,
194
+ # not `enum_body` itself. Used by `_iter_initializer_sites` (below) to tell
195
+ # a real instance-initializer block apart from an ordinary nested `{ }`
196
+ # inside a method/constructor body (whose parent is never one of these
197
+ # types) without walking further up.
198
+ _INIT_BLOCK_PARENTS = TYPE_BODIES | {"enum_body_declarations"}
199
+
200
+ # JDK functional interfaces whose SAM parameter types are derivable without
201
+ # indexing JDK sources. Integers reference the interface's generic arguments;
202
+ # strings are concrete primitive types. Custom interfaces use graph metadata
203
+ # and the same generic-substitution path as normal Java calls.
204
+ _JDK_SAM_PARAMS: Dict[str, Tuple[Union[int, str], ...]] = {
205
+ "Runnable": (), "Callable": (), "Supplier": (), "BooleanSupplier": (),
206
+ "IntSupplier": (), "LongSupplier": (), "DoubleSupplier": (),
207
+ "Consumer": (0,), "Function": (0,), "Predicate": (0,), "UnaryOperator": (0,),
208
+ "BiConsumer": (0, 1), "BiFunction": (0, 1), "BiPredicate": (0, 1),
209
+ "BinaryOperator": (0, 0), "Comparator": (0, 0),
210
+ "IntConsumer": ("int",), "LongConsumer": ("long",),
211
+ "DoubleConsumer": ("double",), "IntFunction": ("int",),
212
+ "LongFunction": ("long",), "DoubleFunction": ("double",),
213
+ "IntPredicate": ("int",), "LongPredicate": ("long",),
214
+ "DoublePredicate": ("double",), "IntUnaryOperator": ("int",),
215
+ "LongUnaryOperator": ("long",), "DoubleUnaryOperator": ("double",),
216
+ "IntBinaryOperator": ("int", "int"),
217
+ "LongBinaryOperator": ("long", "long"),
218
+ "DoubleBinaryOperator": ("double", "double"),
219
+ "ToIntFunction": (0,), "ToLongFunction": (0,), "ToDoubleFunction": (0,),
220
+ "ToIntBiFunction": (0, 1), "ToLongBiFunction": (0, 1),
221
+ "ToDoubleBiFunction": (0, 1), "ObjIntConsumer": (0, "int"),
222
+ "ObjLongConsumer": (0, "long"), "ObjDoubleConsumer": (0, "double"),
223
+ "IntToLongFunction": ("int",), "IntToDoubleFunction": ("int",),
224
+ "LongToIntFunction": ("long",), "LongToDoubleFunction": ("long",),
225
+ "DoubleToIntFunction": ("double",), "DoubleToLongFunction": ("double",),
226
+ }
227
+
228
+ _JDK_SAM_RETURNS: Dict[str, Union[int, str]] = {
229
+ "Runnable": "void", "Callable": 0, "Supplier": 0,
230
+ "BooleanSupplier": "boolean", "IntSupplier": "int",
231
+ "LongSupplier": "long", "DoubleSupplier": "double",
232
+ "Consumer": "void", "BiConsumer": "void", "Function": 1,
233
+ "BiFunction": 2, "Predicate": "boolean", "BiPredicate": "boolean",
234
+ "UnaryOperator": 0, "BinaryOperator": 0, "Comparator": "int",
235
+ "IntConsumer": "void", "LongConsumer": "void", "DoubleConsumer": "void",
236
+ "IntFunction": 0, "LongFunction": 0, "DoubleFunction": 0,
237
+ "IntPredicate": "boolean", "LongPredicate": "boolean", "DoublePredicate": "boolean",
238
+ "IntUnaryOperator": "int", "LongUnaryOperator": "long", "DoubleUnaryOperator": "double",
239
+ "IntBinaryOperator": "int", "LongBinaryOperator": "long", "DoubleBinaryOperator": "double",
240
+ "ToIntFunction": "int", "ToLongFunction": "long", "ToDoubleFunction": "double",
241
+ "ToIntBiFunction": "int", "ToLongBiFunction": "long", "ToDoubleBiFunction": "double",
242
+ "ObjIntConsumer": "void", "ObjLongConsumer": "void", "ObjDoubleConsumer": "void",
243
+ "IntToLongFunction": "long", "IntToDoubleFunction": "double",
244
+ "LongToIntFunction": "int", "LongToDoubleFunction": "double",
245
+ "DoubleToIntFunction": "int", "DoubleToLongFunction": "long",
246
+ }
247
+
248
+
249
+ # Type-declaration CST node kinds that can enclose a nested `@interface`
250
+ # (F-01 fix). Mirrors `JavaSymbolExtractor._CLASS_TYPES` plus
251
+ # `annotation_type_declaration` itself (an annotation type may nest inside
252
+ # another one), kept as a local copy rather than importing the extractor's
253
+ # constant — `_collect_annotation_type_names` walks raw CST nodes the
254
+ # extractor never turns into `Symbol`s, so the two have no shared id/scope
255
+ # logic to drift apart, only this grammar-level type list.
256
+ _ENCLOSING_TYPE_CST_TYPES = (
257
+ "class_declaration",
258
+ "interface_declaration",
259
+ "enum_declaration",
260
+ "record_declaration",
261
+ "annotation_type_declaration",
262
+ )
263
+
264
+ # ---------------------------------------------------------------- P4.12 fan-out
265
+ # Annotation simple names (see codegraph-java-deep-dive.md §7.3) that mark a
266
+ # field for dependency injection — a receiver typed as an interface but read
267
+ # off such a field has its concrete bean chosen by the Spring container, so DI
268
+ # narrowing (below) can resolve it precisely where a bare interface receiver
269
+ # only fans out.
270
+ JAVA_INJECTION_ANNOTATIONS: FrozenSet[str] = frozenset({
271
+ "Autowired", "Inject", "Resource",
272
+ })
273
+ # Annotation simple names that register a class as a Spring bean (a concrete
274
+ # injectable candidate for any interface it implements).
275
+ JAVA_COMPONENT_ANNOTATIONS: FrozenSet[str] = frozenset({
276
+ "Component", "Service", "Repository", "Controller", "RestController",
277
+ })
278
+ JAVA_DI_ANNOTATION_FQNS: FrozenSet[str] = frozenset({
279
+ "org.springframework.beans.factory.annotation.Autowired",
280
+ "org.springframework.beans.factory.annotation.Qualifier",
281
+ "org.springframework.context.annotation.Bean",
282
+ "org.springframework.context.annotation.Primary",
283
+ "org.springframework.stereotype.Component",
284
+ "org.springframework.stereotype.Controller",
285
+ "org.springframework.stereotype.Repository",
286
+ "org.springframework.stereotype.Service",
287
+ "org.springframework.web.bind.annotation.RestController",
288
+ "jakarta.inject.Inject",
289
+ "jakarta.inject.Qualifier",
290
+ "jakarta.annotation.Resource",
291
+ "javax.inject.Inject",
292
+ "javax.inject.Qualifier",
293
+ "javax.annotation.Resource",
294
+ })
295
+
296
+ # Generic method signatures for JDK/container types whose source declaration is
297
+ # outside the analyzed project. Values are declared return types in terms of the
298
+ # receiver's class type parameters; source classes use the same substitution
299
+ # path via `_generic_return_type_from_source_method`.
300
+ JAVA_GENERIC_METHOD_STUB_RETURNS: Dict[Tuple[str, str], str] = {
301
+ # `java.lang.String` instance members whose declared return is the same
302
+ # across *every* overload (JDK 17+ javadoc). Only such fixed-return
303
+ # members belong here: a member with overload-dependent returns would
304
+ # turn a narrowing hint into a guess. Needed so a `String`-typed chain
305
+ # used as a call argument (`"...".formatted(a, b)`, `name.toUpperCase()`,
306
+ # `text.substring(1)`) can exclude a same-arity `Supplier<String>`/
307
+ # primitive sibling overload instead of fanning out at 0.55 (JUnit5
308
+ # FP/FN catalog #16476 and the wider `overload/java_resolver@0.55`
309
+ # stratum).
310
+ ("String", "trim"): "String",
311
+ ("String", "strip"): "String",
312
+ ("String", "stripLeading"): "String",
313
+ ("String", "stripTrailing"): "String",
314
+ ("String", "stripIndent"): "String",
315
+ ("String", "formatted"): "String",
316
+ ("String", "toUpperCase"): "String",
317
+ ("String", "toLowerCase"): "String",
318
+ ("String", "substring"): "String",
319
+ ("String", "replace"): "String",
320
+ ("String", "replaceAll"): "String",
321
+ ("String", "replaceFirst"): "String",
322
+ ("String", "concat"): "String",
323
+ ("String", "repeat"): "String",
324
+ ("String", "intern"): "String",
325
+ ("String", "indent"): "String",
326
+ ("String", "translateEscapes"): "String",
327
+ ("String", "length"): "int",
328
+ ("String", "indexOf"): "int",
329
+ ("String", "lastIndexOf"): "int",
330
+ ("String", "compareTo"): "int",
331
+ ("String", "compareToIgnoreCase"): "int",
332
+ ("String", "codePointAt"): "int",
333
+ ("String", "charAt"): "char",
334
+ ("String", "isEmpty"): "boolean",
335
+ ("String", "isBlank"): "boolean",
336
+ ("String", "contains"): "boolean",
337
+ ("String", "startsWith"): "boolean",
338
+ ("String", "endsWith"): "boolean",
339
+ ("String", "equals"): "boolean",
340
+ ("String", "equalsIgnoreCase"): "boolean",
341
+ ("String", "contentEquals"): "boolean",
342
+ ("String", "matches"): "boolean",
343
+ ("String", "regionMatches"): "boolean",
344
+ ("String", "toCharArray"): "char[]",
345
+ ("String", "getBytes"): "byte[]",
346
+ ("String", "split"): "String[]",
347
+ ("String", "lines"): "Stream<String>",
348
+ ("String", "chars"): "IntStream",
349
+ ("Class", "getClassLoader"): "ClassLoader",
350
+ ("Class", "getName"): "String",
351
+ ("Class", "getSimpleName"): "String",
352
+ ("Class", "getPackage"): "java.lang.Package",
353
+ ("Class", "getDeclaredMethod"): "java.lang.reflect.Method",
354
+ ("Package", "getName"): "String",
355
+ ("Method", "getName"): "String",
356
+ ("Method", "getParameterTypes"): "Class[]",
357
+ ("Iterable", "iterator"): "Iterator<T>",
358
+ ("Collection", "iterator"): "Iterator<E>",
359
+ ("List", "iterator"): "Iterator<E>",
360
+ ("ArrayList", "iterator"): "Iterator<E>",
361
+ ("LinkedList", "iterator"): "Iterator<E>",
362
+ ("Collection", "stream"): "Stream<E>",
363
+ ("List", "stream"): "Stream<E>",
364
+ ("ArrayList", "stream"): "Stream<E>",
365
+ ("LinkedList", "stream"): "Stream<E>",
366
+ ("Set", "stream"): "Stream<E>",
367
+ ("SortedSet", "stream"): "Stream<E>",
368
+ ("Collection", "parallelStream"): "Stream<E>",
369
+ ("List", "parallelStream"): "Stream<E>",
370
+ ("Set", "parallelStream"): "Stream<E>",
371
+ ("Iterator", "next"): "T",
372
+ ("ListIterator", "next"): "E",
373
+ ("List", "get"): "E",
374
+ # Java 21 SequencedCollection API. A declared List receiver retains its
375
+ # element type through `getFirst()` just as it does through indexed get.
376
+ ("List", "getFirst"): "E",
377
+ ("ArrayList", "get"): "E",
378
+ ("ArrayList", "getFirst"): "E",
379
+ ("LinkedList", "get"): "E",
380
+ ("LinkedList", "getFirst"): "E",
381
+ ("Optional", "get"): "T",
382
+ ("Optional", "orElseThrow"): "T",
383
+ ("Map", "get"): "V",
384
+ ("Map", "getOrDefault"): "V",
385
+ ("Map", "values"): "Collection<V>",
386
+ ("Map", "keySet"): "Set<K>",
387
+ ("Map", "entrySet"): "Set<Map.Entry<K,V>>",
388
+ ("Entry", "getKey"): "K",
389
+ ("Entry", "getValue"): "V",
390
+ ("HashMap", "get"): "V",
391
+ ("HashMap", "values"): "Collection<V>",
392
+ ("HashMap", "keySet"): "Set<K>",
393
+ ("HashMap", "entrySet"): "Set<Map.Entry<K,V>>",
394
+ ("LinkedHashMap", "get"): "V",
395
+ ("LinkedHashMap", "values"): "Collection<V>",
396
+ ("LinkedHashMap", "keySet"): "Set<K>",
397
+ ("LinkedHashMap", "entrySet"): "Set<Map.Entry<K,V>>",
398
+ ("TreeMap", "get"): "V",
399
+ ("TreeMap", "values"): "Collection<V>",
400
+ ("TreeMap", "keySet"): "Set<K>",
401
+ ("TreeMap", "entrySet"): "Set<Map.Entry<K,V>>",
402
+ ("Stream", "filter"): "Stream<T>",
403
+ ("Stream", "peek"): "Stream<T>",
404
+ ("Stream", "distinct"): "Stream<T>",
405
+ ("Stream", "sorted"): "Stream<T>",
406
+ ("Stream", "toList"): "List<T>",
407
+ ("Stream", "findFirst"): "Optional<T>",
408
+ ("Optional", "filter"): "Optional<T>",
409
+ # Primitive stream specializations (`IntStream`/`LongStream`/
410
+ # `DoubleStream`) are not generic (JLS: no type parameter to
411
+ # substitute), so each fixed contract return is written as a plain,
412
+ # non-generic string here, the same way the `Collection.contains() ->
413
+ # boolean` facts above are. `map`/`filter`/`sorted`/`distinct` return
414
+ # the same specialized stream type (not a boxed `Stream<T>`); the
415
+ # reduction terminal ops (`sum`/`count`/`average`/`max`/`min`) have one
416
+ # fixed primitive/`OptionalX` result regardless of the pipeline's own
417
+ # element-producing lambda, so a *known* argument this narrows can
418
+ # never be defeated by an unrelated unresolvable sibling (P5 finding
419
+ # #1's `IntStream.range(...).map(...).sum()` shape).
420
+ ("IntStream", "map"): "IntStream",
421
+ ("IntStream", "filter"): "IntStream",
422
+ ("IntStream", "sorted"): "IntStream",
423
+ ("IntStream", "distinct"): "IntStream",
424
+ ("IntStream", "boxed"): "Stream<Integer>",
425
+ ("IntStream", "toArray"): "int[]",
426
+ ("IntStream", "sum"): "int",
427
+ ("IntStream", "count"): "long",
428
+ ("IntStream", "average"): "OptionalDouble",
429
+ ("IntStream", "max"): "OptionalInt",
430
+ ("IntStream", "min"): "OptionalInt",
431
+ ("LongStream", "map"): "LongStream",
432
+ ("LongStream", "filter"): "LongStream",
433
+ ("LongStream", "sorted"): "LongStream",
434
+ ("LongStream", "distinct"): "LongStream",
435
+ ("LongStream", "boxed"): "Stream<Long>",
436
+ ("LongStream", "toArray"): "long[]",
437
+ ("LongStream", "sum"): "long",
438
+ ("LongStream", "count"): "long",
439
+ ("LongStream", "average"): "OptionalDouble",
440
+ ("LongStream", "max"): "OptionalLong",
441
+ ("LongStream", "min"): "OptionalLong",
442
+ ("DoubleStream", "map"): "DoubleStream",
443
+ ("DoubleStream", "filter"): "DoubleStream",
444
+ ("DoubleStream", "sorted"): "DoubleStream",
445
+ ("DoubleStream", "distinct"): "DoubleStream",
446
+ ("DoubleStream", "boxed"): "Stream<Double>",
447
+ ("DoubleStream", "toArray"): "double[]",
448
+ ("DoubleStream", "sum"): "double",
449
+ ("DoubleStream", "count"): "long",
450
+ ("DoubleStream", "average"): "OptionalDouble",
451
+ ("DoubleStream", "max"): "OptionalDouble",
452
+ ("DoubleStream", "min"): "OptionalDouble",
453
+ # `Stream<T>.mapToInt/mapToLong/mapToDouble` bridge to the matching
454
+ # primitive stream; the bridging lambda's own element type does not
455
+ # change this fixed contract return.
456
+ ("Stream", "mapToInt"): "IntStream",
457
+ ("Stream", "mapToLong"): "LongStream",
458
+ ("Stream", "mapToDouble"): "DoubleStream",
459
+ # Function/Supplier results are needed when a fluent call is assigned to
460
+ # `var` and used as the receiver of a later source call.
461
+ ("Function", "apply"): "R",
462
+ ("Supplier", "get"): "T",
463
+ # Invoking the single abstract method of a typed JDK functional value
464
+ # yields its declared result variable (`BiFunction<A,B,R>.apply -> R`).
465
+ ("BiFunction", "apply"): "R",
466
+ ("UnaryOperator", "apply"): "T",
467
+ ("BinaryOperator", "apply"): "T",
468
+ ("Callable", "call"): "V",
469
+ # Exact `java.lang.reflect` facts, spelled fully qualified so the
470
+ # element type is import-independent at the call site. They are
471
+ # consulted only for a receiver whose declared type is not a source
472
+ # class, so a project type named `Method` or `Class` is never stubbed.
473
+ ("Class", "getDeclaredMethods"): "java.lang.reflect.Method[]",
474
+ ("Class", "getMethods"): "java.lang.reflect.Method[]",
475
+ ("Class", "getDeclaredFields"): "java.lang.reflect.Field[]",
476
+ ("Class", "getFields"): "java.lang.reflect.Field[]",
477
+ ("Executable", "getParameters"): "java.lang.reflect.Parameter[]",
478
+ ("Method", "getParameters"): "java.lang.reflect.Parameter[]",
479
+ ("Constructor", "getParameters"): "java.lang.reflect.Parameter[]",
480
+ ("Executable", "getGenericParameterTypes"): "java.lang.reflect.Type[]",
481
+ ("Method", "getGenericParameterTypes"): "java.lang.reflect.Type[]",
482
+ ("Constructor", "getGenericParameterTypes"): "java.lang.reflect.Type[]",
483
+ # Fixed (non-generic) `Collection`/`Map` contract returns, needed for
484
+ # overload-argument narrowing (P5 finding #1): `list.contains(x)`'s
485
+ # result is always primitive `boolean`, never a candidate's guessed
486
+ # reference type, regardless of the element type `E`/`K`/`V`.
487
+ ("Collection", "contains"): "boolean",
488
+ ("Collection", "isEmpty"): "boolean",
489
+ ("List", "contains"): "boolean",
490
+ ("List", "isEmpty"): "boolean",
491
+ ("ArrayList", "contains"): "boolean",
492
+ ("ArrayList", "isEmpty"): "boolean",
493
+ ("LinkedList", "contains"): "boolean",
494
+ ("LinkedList", "isEmpty"): "boolean",
495
+ ("Set", "contains"): "boolean",
496
+ ("Set", "isEmpty"): "boolean",
497
+ ("SortedSet", "contains"): "boolean",
498
+ ("SortedSet", "isEmpty"): "boolean",
499
+ ("Map", "containsKey"): "boolean",
500
+ ("Map", "containsValue"): "boolean",
501
+ ("Map", "isEmpty"): "boolean",
502
+ ("HashMap", "containsKey"): "boolean",
503
+ ("HashMap", "containsValue"): "boolean",
504
+ ("HashMap", "isEmpty"): "boolean",
505
+ ("LinkedHashMap", "containsKey"): "boolean",
506
+ ("LinkedHashMap", "containsValue"): "boolean",
507
+ ("LinkedHashMap", "isEmpty"): "boolean",
508
+ ("TreeMap", "containsKey"): "boolean",
509
+ ("TreeMap", "containsValue"): "boolean",
510
+ ("TreeMap", "isEmpty"): "boolean",
511
+ # Batch 24 — more fixed, non-generic JDK contract returns, all
512
+ # identical across every overload of the member. Same narrowing use as
513
+ # `contains`/`isEmpty` above; the largest remaining `overload@0.55`
514
+ # shapes in JUnit5 were `assertEquals(2, list.size())` (56 sites) and
515
+ # atomic/boxed accessors (`counter.get()`, `value.intValue()`).
516
+ ("Collection", "size"): "int",
517
+ ("List", "size"): "int",
518
+ ("ArrayList", "size"): "int",
519
+ ("LinkedList", "size"): "int",
520
+ ("Set", "size"): "int",
521
+ ("SortedSet", "size"): "int",
522
+ ("Map", "size"): "int",
523
+ ("HashMap", "size"): "int",
524
+ ("LinkedHashMap", "size"): "int",
525
+ ("TreeMap", "size"): "int",
526
+ ("Stream", "count"): "long",
527
+ ("Optional", "isPresent"): "boolean",
528
+ ("Optional", "isEmpty"): "boolean",
529
+ ("AtomicInteger", "get"): "int",
530
+ ("AtomicInteger", "intValue"): "int",
531
+ ("AtomicInteger", "incrementAndGet"): "int",
532
+ ("AtomicInteger", "getAndIncrement"): "int",
533
+ ("AtomicInteger", "decrementAndGet"): "int",
534
+ ("AtomicInteger", "getAndDecrement"): "int",
535
+ ("AtomicInteger", "addAndGet"): "int",
536
+ ("AtomicInteger", "getAndAdd"): "int",
537
+ ("AtomicInteger", "getAndSet"): "int",
538
+ ("AtomicInteger", "updateAndGet"): "int",
539
+ ("AtomicInteger", "compareAndSet"): "boolean",
540
+ ("AtomicLong", "get"): "long",
541
+ ("AtomicLong", "longValue"): "long",
542
+ ("AtomicLong", "incrementAndGet"): "long",
543
+ ("AtomicLong", "getAndIncrement"): "long",
544
+ ("AtomicLong", "decrementAndGet"): "long",
545
+ ("AtomicLong", "getAndDecrement"): "long",
546
+ ("AtomicLong", "addAndGet"): "long",
547
+ ("AtomicLong", "getAndAdd"): "long",
548
+ ("AtomicLong", "getAndSet"): "long",
549
+ ("AtomicLong", "updateAndGet"): "long",
550
+ ("AtomicLong", "compareAndSet"): "boolean",
551
+ ("AtomicBoolean", "get"): "boolean",
552
+ ("AtomicBoolean", "getAndSet"): "boolean",
553
+ ("AtomicBoolean", "compareAndSet"): "boolean",
554
+ ("AtomicReference", "get"): "V",
555
+ ("AtomicReference", "compareAndSet"): "boolean",
556
+ ("Number", "intValue"): "int",
557
+ ("Number", "longValue"): "long",
558
+ ("Number", "doubleValue"): "double",
559
+ ("Number", "floatValue"): "float",
560
+ ("Number", "shortValue"): "short",
561
+ ("Number", "byteValue"): "byte",
562
+ ("Integer", "intValue"): "int",
563
+ ("Integer", "longValue"): "long",
564
+ ("Integer", "doubleValue"): "double",
565
+ ("Long", "intValue"): "int",
566
+ ("Long", "longValue"): "long",
567
+ ("Long", "doubleValue"): "double",
568
+ ("Short", "intValue"): "int",
569
+ ("Short", "shortValue"): "short",
570
+ ("Byte", "intValue"): "int",
571
+ ("Byte", "byteValue"): "byte",
572
+ ("Double", "intValue"): "int",
573
+ ("Double", "longValue"): "long",
574
+ ("Double", "doubleValue"): "double",
575
+ ("Float", "intValue"): "int",
576
+ ("Float", "floatValue"): "float",
577
+ ("Float", "doubleValue"): "double",
578
+ ("Boolean", "booleanValue"): "boolean",
579
+ ("Character", "charValue"): "char",
580
+ ("BooleanSupplier", "getAsBoolean"): "boolean",
581
+ ("IntSupplier", "getAsInt"): "int",
582
+ ("LongSupplier", "getAsLong"): "long",
583
+ ("DoubleSupplier", "getAsDouble"): "double",
584
+ ("Duration", "isNegative"): "boolean",
585
+ ("Duration", "isZero"): "boolean",
586
+ ("Duration", "toMillis"): "long",
587
+ ("Duration", "toNanos"): "long",
588
+ ("Duration", "toSeconds"): "long",
589
+ ("Duration", "getSeconds"): "long",
590
+ # Batch 25 — fixed-return JDK members behind the remaining
591
+ # `assertTrue/assertFalse/assumeTrue(boolean)` vs `(BooleanSupplier)`
592
+ # fan-outs in JUnit5 (`filter.toPredicate().test(x)`, `stack.isEmpty()`,
593
+ # `lock.tryLock()`, `tempDir.exists()`, `stream.anyMatch(..)`,
594
+ # `Foo.class.getMethod(..).isBridge()`, `executor.awaitTermination(..)`)
595
+ # plus the fixed `String`/`Class`/reflection returns those chains pass
596
+ # through. Every entry is identical across all overloads of the member
597
+ # and holds for every element/type argument, exactly like the
598
+ # `contains`/`isEmpty` facts above; a source class sharing a JDK simple
599
+ # name is still never stubbed (`_generic_method_invocation_return_type`
600
+ # checks source resolution first).
601
+ ("Predicate", "test"): "boolean",
602
+ ("BiPredicate", "test"): "boolean",
603
+ ("Stream", "anyMatch"): "boolean",
604
+ ("Stream", "allMatch"): "boolean",
605
+ ("Stream", "noneMatch"): "boolean",
606
+ ("IntStream", "anyMatch"): "boolean",
607
+ ("IntStream", "allMatch"): "boolean",
608
+ ("IntStream", "noneMatch"): "boolean",
609
+ ("Iterator", "hasNext"): "boolean",
610
+ ("ListIterator", "hasNext"): "boolean",
611
+ ("ListIterator", "hasPrevious"): "boolean",
612
+ ("Collection", "add"): "boolean",
613
+ ("List", "add"): "boolean",
614
+ ("ArrayList", "add"): "boolean",
615
+ ("LinkedList", "add"): "boolean",
616
+ ("Set", "add"): "boolean",
617
+ ("SortedSet", "add"): "boolean",
618
+ ("Collection", "containsAll"): "boolean",
619
+ ("List", "containsAll"): "boolean",
620
+ ("Set", "containsAll"): "boolean",
621
+ ("HashSet", "contains"): "boolean",
622
+ ("HashSet", "isEmpty"): "boolean",
623
+ ("HashSet", "add"): "boolean",
624
+ ("HashSet", "size"): "int",
625
+ ("LinkedHashSet", "contains"): "boolean",
626
+ ("LinkedHashSet", "isEmpty"): "boolean",
627
+ ("LinkedHashSet", "add"): "boolean",
628
+ ("LinkedHashSet", "size"): "int",
629
+ ("TreeSet", "contains"): "boolean",
630
+ ("TreeSet", "isEmpty"): "boolean",
631
+ ("TreeSet", "add"): "boolean",
632
+ ("TreeSet", "size"): "int",
633
+ ("EnumSet", "contains"): "boolean",
634
+ ("EnumSet", "isEmpty"): "boolean",
635
+ ("EnumSet", "containsAll"): "boolean",
636
+ ("EnumSet", "size"): "int",
637
+ ("Stack", "isEmpty"): "boolean",
638
+ ("Stack", "empty"): "boolean",
639
+ ("Stack", "contains"): "boolean",
640
+ ("Stack", "add"): "boolean",
641
+ ("Stack", "size"): "int",
642
+ ("Stack", "search"): "int",
643
+ ("Stack", "peek"): "E",
644
+ ("Stack", "pop"): "E",
645
+ ("Stack", "push"): "E",
646
+ ("Deque", "isEmpty"): "boolean",
647
+ ("Deque", "contains"): "boolean",
648
+ ("Deque", "size"): "int",
649
+ ("ArrayDeque", "isEmpty"): "boolean",
650
+ ("ArrayDeque", "contains"): "boolean",
651
+ ("ArrayDeque", "size"): "int",
652
+ ("Queue", "isEmpty"): "boolean",
653
+ ("Queue", "contains"): "boolean",
654
+ ("Queue", "size"): "int",
655
+ ("Map", "isEmpty"): "boolean",
656
+ ("ConcurrentHashMap", "containsKey"): "boolean",
657
+ ("ConcurrentHashMap", "isEmpty"): "boolean",
658
+ ("ConcurrentHashMap", "size"): "int",
659
+ ("ConcurrentMap", "containsKey"): "boolean",
660
+ ("ConcurrentMap", "isEmpty"): "boolean",
661
+ ("ConcurrentMap", "size"): "int",
662
+ ("Lock", "tryLock"): "boolean",
663
+ ("ReentrantLock", "tryLock"): "boolean",
664
+ ("ReentrantLock", "isLocked"): "boolean",
665
+ ("ReentrantLock", "isHeldByCurrentThread"): "boolean",
666
+ ("ReentrantLock", "hasQueuedThreads"): "boolean",
667
+ ("ReentrantLock", "isFair"): "boolean",
668
+ ("ReentrantLock", "getHoldCount"): "int",
669
+ ("ThreadLocal", "get"): "T",
670
+ ("Future", "get"): "V",
671
+ ("Future", "isDone"): "boolean",
672
+ ("Future", "isCancelled"): "boolean",
673
+ ("Future", "cancel"): "boolean",
674
+ ("CompletableFuture", "get"): "T",
675
+ ("CompletableFuture", "join"): "T",
676
+ ("CompletableFuture", "getNow"): "T",
677
+ ("CompletableFuture", "isDone"): "boolean",
678
+ ("CompletableFuture", "isCancelled"): "boolean",
679
+ ("CompletableFuture", "isCompletedExceptionally"): "boolean",
680
+ ("CompletableFuture", "cancel"): "boolean",
681
+ ("CompletableFuture", "complete"): "boolean",
682
+ ("ExecutorService", "awaitTermination"): "boolean",
683
+ ("ExecutorService", "isShutdown"): "boolean",
684
+ ("ExecutorService", "isTerminated"): "boolean",
685
+ ("ScheduledExecutorService", "awaitTermination"): "boolean",
686
+ ("ScheduledExecutorService", "isShutdown"): "boolean",
687
+ ("ScheduledExecutorService", "isTerminated"): "boolean",
688
+ ("ThreadPoolExecutor", "awaitTermination"): "boolean",
689
+ ("ThreadPoolExecutor", "isShutdown"): "boolean",
690
+ ("ThreadPoolExecutor", "isTerminated"): "boolean",
691
+ ("ForkJoinPool", "awaitTermination"): "boolean",
692
+ ("ForkJoinPool", "awaitQuiescence"): "boolean",
693
+ ("ForkJoinPool", "isShutdown"): "boolean",
694
+ ("ForkJoinPool", "isTerminated"): "boolean",
695
+ ("ForkJoinPool", "isQuiescent"): "boolean",
696
+ ("ForkJoinPool", "getParallelism"): "int",
697
+ ("CountDownLatch", "await"): "boolean",
698
+ ("CountDownLatch", "getCount"): "long",
699
+ ("Semaphore", "tryAcquire"): "boolean",
700
+ ("Semaphore", "availablePermits"): "int",
701
+ ("Thread", "isInterrupted"): "boolean",
702
+ ("Thread", "isAlive"): "boolean",
703
+ ("Thread", "isDaemon"): "boolean",
704
+ ("Thread", "isVirtual"): "boolean",
705
+ ("Thread", "getName"): "String",
706
+ ("Thread", "getPriority"): "int",
707
+ ("Thread", "getId"): "long",
708
+ ("Thread", "threadId"): "long",
709
+ ("File", "exists"): "boolean",
710
+ ("File", "isDirectory"): "boolean",
711
+ ("File", "isFile"): "boolean",
712
+ ("File", "isAbsolute"): "boolean",
713
+ ("File", "isHidden"): "boolean",
714
+ ("File", "canRead"): "boolean",
715
+ ("File", "canWrite"): "boolean",
716
+ ("File", "canExecute"): "boolean",
717
+ ("File", "createNewFile"): "boolean",
718
+ ("File", "delete"): "boolean",
719
+ ("File", "mkdir"): "boolean",
720
+ ("File", "mkdirs"): "boolean",
721
+ ("File", "renameTo"): "boolean",
722
+ ("File", "setReadOnly"): "boolean",
723
+ ("File", "setReadable"): "boolean",
724
+ ("File", "setWritable"): "boolean",
725
+ ("File", "setExecutable"): "boolean",
726
+ ("File", "setLastModified"): "boolean",
727
+ ("File", "getName"): "String",
728
+ ("File", "getPath"): "String",
729
+ ("File", "getParent"): "String",
730
+ ("File", "getAbsolutePath"): "String",
731
+ ("File", "getCanonicalPath"): "String",
732
+ ("File", "getParentFile"): "java.io.File",
733
+ ("File", "getAbsoluteFile"): "java.io.File",
734
+ ("File", "getCanonicalFile"): "java.io.File",
735
+ ("File", "toPath"): "java.nio.file.Path",
736
+ ("File", "length"): "long",
737
+ ("File", "lastModified"): "long",
738
+ ("File", "listFiles"): "java.io.File[]",
739
+ ("File", "list"): "String[]",
740
+ ("Path", "toFile"): "java.io.File",
741
+ ("Path", "getFileName"): "java.nio.file.Path",
742
+ ("Path", "getParent"): "java.nio.file.Path",
743
+ ("Path", "getRoot"): "java.nio.file.Path",
744
+ ("Path", "toAbsolutePath"): "java.nio.file.Path",
745
+ ("Path", "normalize"): "java.nio.file.Path",
746
+ ("Path", "resolve"): "java.nio.file.Path",
747
+ ("Path", "resolveSibling"): "java.nio.file.Path",
748
+ ("Path", "relativize"): "java.nio.file.Path",
749
+ ("Path", "toRealPath"): "java.nio.file.Path",
750
+ ("Path", "startsWith"): "boolean",
751
+ ("Path", "endsWith"): "boolean",
752
+ ("Path", "isAbsolute"): "boolean",
753
+ ("Path", "getNameCount"): "int",
754
+ ("Class", "isArray"): "boolean",
755
+ ("Class", "isPrimitive"): "boolean",
756
+ ("Class", "isInterface"): "boolean",
757
+ ("Class", "isEnum"): "boolean",
758
+ ("Class", "isRecord"): "boolean",
759
+ ("Class", "isAnnotation"): "boolean",
760
+ ("Class", "isAssignableFrom"): "boolean",
761
+ ("Class", "isInstance"): "boolean",
762
+ ("Class", "isAnonymousClass"): "boolean",
763
+ ("Class", "isMemberClass"): "boolean",
764
+ ("Class", "isLocalClass"): "boolean",
765
+ ("Class", "isSynthetic"): "boolean",
766
+ ("Class", "isSealed"): "boolean",
767
+ ("Class", "isHidden"): "boolean",
768
+ ("Class", "isNestmateOf"): "boolean",
769
+ ("Class", "isAnnotationPresent"): "boolean",
770
+ ("Class", "desiredAssertionStatus"): "boolean",
771
+ ("Class", "getComponentType"): "Class",
772
+ ("Class", "getSuperclass"): "Class",
773
+ ("Class", "getEnclosingClass"): "Class",
774
+ ("Class", "getDeclaringClass"): "Class",
775
+ ("Class", "getNestHost"): "Class",
776
+ ("Class", "getInterfaces"): "Class[]",
777
+ ("Class", "getMethod"): "java.lang.reflect.Method",
778
+ ("Class", "getConstructor"): "java.lang.reflect.Constructor",
779
+ ("Class", "getDeclaredConstructor"): "java.lang.reflect.Constructor",
780
+ ("Class", "getField"): "java.lang.reflect.Field",
781
+ ("Class", "getDeclaredField"): "java.lang.reflect.Field",
782
+ ("Class", "getModifiers"): "int",
783
+ ("Class", "getCanonicalName"): "String",
784
+ ("Class", "getTypeName"): "String",
785
+ ("Class", "getPackageName"): "String",
786
+ ("Class", "getModule"): "java.lang.Module",
787
+ ("Module", "isNamed"): "boolean",
788
+ ("Module", "getName"): "String",
789
+ ("Method", "isBridge"): "boolean",
790
+ ("Method", "isSynthetic"): "boolean",
791
+ ("Method", "isDefault"): "boolean",
792
+ ("Method", "isVarArgs"): "boolean",
793
+ ("Method", "isAnnotationPresent"): "boolean",
794
+ ("Method", "getModifiers"): "int",
795
+ ("Method", "getParameterCount"): "int",
796
+ ("Method", "getReturnType"): "Class",
797
+ ("Method", "getDeclaringClass"): "Class",
798
+ ("Constructor", "isSynthetic"): "boolean",
799
+ ("Constructor", "isVarArgs"): "boolean",
800
+ ("Constructor", "getModifiers"): "int",
801
+ ("Constructor", "getParameterCount"): "int",
802
+ ("Constructor", "getDeclaringClass"): "Class",
803
+ ("Field", "getName"): "String",
804
+ ("Field", "getType"): "Class",
805
+ ("Field", "getDeclaringClass"): "Class",
806
+ ("Field", "getModifiers"): "int",
807
+ ("Field", "isSynthetic"): "boolean",
808
+ ("Field", "isEnumConstant"): "boolean",
809
+ ("Parameter", "getName"): "String",
810
+ ("Parameter", "getType"): "Class",
811
+ ("Parameter", "isNamePresent"): "boolean",
812
+ ("Parameter", "isVarArgs"): "boolean",
813
+ ("Pattern", "matcher"): "java.util.regex.Matcher",
814
+ ("Pattern", "pattern"): "String",
815
+ ("Matcher", "matches"): "boolean",
816
+ ("Matcher", "find"): "boolean",
817
+ ("Matcher", "lookingAt"): "boolean",
818
+ ("Matcher", "hitEnd"): "boolean",
819
+ ("Matcher", "group"): "String",
820
+ ("Matcher", "groupCount"): "int",
821
+ ("Matcher", "start"): "int",
822
+ ("Matcher", "end"): "int",
823
+ ("Throwable", "getMessage"): "String",
824
+ ("Throwable", "getLocalizedMessage"): "String",
825
+ ("Throwable", "getCause"): "Throwable",
826
+ ("Throwable", "getStackTrace"): "StackTraceElement[]",
827
+ ("Throwable", "getSuppressed"): "Throwable[]",
828
+ ("Exception", "getMessage"): "String",
829
+ ("Exception", "getLocalizedMessage"): "String",
830
+ ("Exception", "getCause"): "Throwable",
831
+ ("Exception", "getStackTrace"): "StackTraceElement[]",
832
+ ("RuntimeException", "getMessage"): "String",
833
+ ("RuntimeException", "getCause"): "Throwable",
834
+ ("RuntimeException", "getStackTrace"): "StackTraceElement[]",
835
+ ("Error", "getMessage"): "String",
836
+ ("Error", "getCause"): "Throwable",
837
+ ("Error", "getStackTrace"): "StackTraceElement[]",
838
+ ("AssertionError", "getMessage"): "String",
839
+ ("AssertionError", "getCause"): "Throwable",
840
+ ("AssertionError", "getStackTrace"): "StackTraceElement[]",
841
+ ("IllegalArgumentException", "getMessage"): "String",
842
+ ("IllegalArgumentException", "getCause"): "Throwable",
843
+ ("IllegalStateException", "getMessage"): "String",
844
+ ("IllegalStateException", "getCause"): "Throwable",
845
+ ("UnsupportedOperationException", "getMessage"): "String",
846
+ ("NullPointerException", "getMessage"): "String",
847
+ ("IOException", "getMessage"): "String",
848
+ ("IOException", "getCause"): "Throwable",
849
+ ("UncheckedIOException", "getMessage"): "String",
850
+ ("UncheckedIOException", "getCause"): "IOException",
851
+ ("InterruptedException", "getMessage"): "String",
852
+ ("ExecutionException", "getMessage"): "String",
853
+ ("ExecutionException", "getCause"): "Throwable",
854
+ ("TimeoutException", "getMessage"): "String",
855
+ ("ReflectiveOperationException", "getMessage"): "String",
856
+ ("ClassNotFoundException", "getMessage"): "String",
857
+ ("NoSuchMethodException", "getMessage"): "String",
858
+ ("InvocationTargetException", "getMessage"): "String",
859
+ ("InvocationTargetException", "getCause"): "Throwable",
860
+ ("InvocationTargetException", "getTargetException"): "Throwable",
861
+ ("StackTraceElement", "getClassName"): "String",
862
+ ("StackTraceElement", "getMethodName"): "String",
863
+ ("StackTraceElement", "getFileName"): "String",
864
+ ("StackTraceElement", "getLineNumber"): "int",
865
+ ("StackTraceElement", "isNativeMethod"): "boolean",
866
+ # Batch 26 — `java.time` accessors (fixed `int`/`long`/`boolean`
867
+ # results on every temporal type that declares them), `Comparable`,
868
+ # primitive-stream pipeline steps with a fixed result stream, and the
869
+ # `InputStream`/`Reader`/`ClassLoader` members behind remaining
870
+ # `assertEquals` fan-outs.
871
+ ("LocalDate", "getYear"): "int",
872
+ ("LocalDate", "getMonthValue"): "int",
873
+ ("LocalDate", "getDayOfMonth"): "int",
874
+ ("LocalDate", "getDayOfYear"): "int",
875
+ ("LocalDate", "lengthOfMonth"): "int",
876
+ ("LocalDate", "lengthOfYear"): "int",
877
+ ("LocalDate", "toEpochDay"): "long",
878
+ ("LocalDate", "isLeapYear"): "boolean",
879
+ ("LocalDate", "isBefore"): "boolean",
880
+ ("LocalDate", "isAfter"): "boolean",
881
+ ("LocalDate", "isEqual"): "boolean",
882
+ ("LocalDateTime", "getYear"): "int",
883
+ ("LocalDateTime", "getMonthValue"): "int",
884
+ ("LocalDateTime", "getDayOfMonth"): "int",
885
+ ("LocalDateTime", "getDayOfYear"): "int",
886
+ ("LocalDateTime", "getHour"): "int",
887
+ ("LocalDateTime", "getMinute"): "int",
888
+ ("LocalDateTime", "getSecond"): "int",
889
+ ("LocalDateTime", "getNano"): "int",
890
+ ("LocalDateTime", "isBefore"): "boolean",
891
+ ("LocalDateTime", "isAfter"): "boolean",
892
+ ("LocalDateTime", "isEqual"): "boolean",
893
+ ("LocalTime", "getHour"): "int",
894
+ ("LocalTime", "getMinute"): "int",
895
+ ("LocalTime", "getSecond"): "int",
896
+ ("LocalTime", "getNano"): "int",
897
+ ("LocalTime", "toSecondOfDay"): "int",
898
+ ("LocalTime", "toNanoOfDay"): "long",
899
+ ("LocalTime", "isBefore"): "boolean",
900
+ ("LocalTime", "isAfter"): "boolean",
901
+ ("ZonedDateTime", "getYear"): "int",
902
+ ("ZonedDateTime", "getMonthValue"): "int",
903
+ ("ZonedDateTime", "getDayOfMonth"): "int",
904
+ ("ZonedDateTime", "getHour"): "int",
905
+ ("ZonedDateTime", "getMinute"): "int",
906
+ ("ZonedDateTime", "getSecond"): "int",
907
+ ("ZonedDateTime", "getNano"): "int",
908
+ ("ZonedDateTime", "toEpochSecond"): "long",
909
+ ("ZonedDateTime", "isBefore"): "boolean",
910
+ ("ZonedDateTime", "isAfter"): "boolean",
911
+ ("OffsetDateTime", "getYear"): "int",
912
+ ("OffsetDateTime", "getMonthValue"): "int",
913
+ ("OffsetDateTime", "getDayOfMonth"): "int",
914
+ ("OffsetDateTime", "getHour"): "int",
915
+ ("OffsetDateTime", "getMinute"): "int",
916
+ ("OffsetDateTime", "getSecond"): "int",
917
+ ("OffsetDateTime", "toEpochSecond"): "long",
918
+ ("Instant", "getEpochSecond"): "long",
919
+ ("Instant", "toEpochMilli"): "long",
920
+ ("Instant", "getNano"): "int",
921
+ ("Instant", "isBefore"): "boolean",
922
+ ("Instant", "isAfter"): "boolean",
923
+ ("Year", "getValue"): "int",
924
+ ("Year", "length"): "int",
925
+ ("Year", "isLeap"): "boolean",
926
+ ("Year", "isBefore"): "boolean",
927
+ ("Year", "isAfter"): "boolean",
928
+ ("YearMonth", "getYear"): "int",
929
+ ("YearMonth", "getMonthValue"): "int",
930
+ ("YearMonth", "lengthOfMonth"): "int",
931
+ ("YearMonth", "lengthOfYear"): "int",
932
+ ("Period", "getYears"): "int",
933
+ ("Period", "getMonths"): "int",
934
+ ("Period", "getDays"): "int",
935
+ ("Period", "toTotalMonths"): "long",
936
+ ("Period", "isNegative"): "boolean",
937
+ ("Period", "isZero"): "boolean",
938
+ ("Duration", "toDays"): "long",
939
+ ("Duration", "toHours"): "long",
940
+ ("Duration", "toMinutes"): "long",
941
+ ("Duration", "getNano"): "int",
942
+ ("Duration", "compareTo"): "int",
943
+ ("Comparable", "compareTo"): "int",
944
+ ("Comparator", "compare"): "int",
945
+ ("IntStream", "limit"): "IntStream",
946
+ ("IntStream", "skip"): "IntStream",
947
+ ("IntStream", "peek"): "IntStream",
948
+ ("IntStream", "asLongStream"): "LongStream",
949
+ ("IntStream", "asDoubleStream"): "DoubleStream",
950
+ ("LongStream", "limit"): "LongStream",
951
+ ("LongStream", "skip"): "LongStream",
952
+ ("LongStream", "peek"): "LongStream",
953
+ ("DoubleStream", "limit"): "DoubleStream",
954
+ ("DoubleStream", "skip"): "DoubleStream",
955
+ ("DoubleStream", "peek"): "DoubleStream",
956
+ ("Stream", "limit"): "Stream<T>",
957
+ ("Stream", "skip"): "Stream<T>",
958
+ ("InputStream", "read"): "int",
959
+ ("InputStream", "available"): "int",
960
+ ("InputStream", "readAllBytes"): "byte[]",
961
+ ("Reader", "read"): "int",
962
+ ("BufferedReader", "readLine"): "String",
963
+ ("ClassLoader", "loadClass"): "Class",
964
+ ("ClassLoader", "getResource"): "java.net.URL",
965
+ ("ClassLoader", "getParent"): "ClassLoader",
966
+ }
967
+
968
+ JAVA_GENERIC_TYPE_PARAM_STUBS: Dict[str, List[str]] = {
969
+ "Iterable": ["T"],
970
+ "AtomicReference": ["V"],
971
+ "Collection": ["E"],
972
+ "List": ["E"],
973
+ "Set": ["E"],
974
+ "SortedSet": ["E"],
975
+ "ArrayList": ["E"],
976
+ "LinkedList": ["E"],
977
+ "Iterator": ["T"],
978
+ "ListIterator": ["E"],
979
+ "Optional": ["T"],
980
+ "Stream": ["T"],
981
+ "Map": ["K", "V"],
982
+ "Entry": ["K", "V"],
983
+ "HashMap": ["K", "V"],
984
+ "LinkedHashMap": ["K", "V"],
985
+ "TreeMap": ["K", "V"],
986
+ "Function": ["T", "R"],
987
+ "Supplier": ["T"],
988
+ "BiFunction": ["T", "U", "R"],
989
+ "UnaryOperator": ["T"],
990
+ "BinaryOperator": ["T"],
991
+ "Callable": ["V"],
992
+ "HashSet": ["E"],
993
+ "LinkedHashSet": ["E"],
994
+ "TreeSet": ["E"],
995
+ "Stack": ["E"],
996
+ "ThreadLocal": ["T"],
997
+ "Future": ["V"],
998
+ "CompletableFuture": ["T"],
999
+ }
1000
+ # Stream operations whose first functional argument consumes one stream
1001
+ # element. This is used only to type an implicit lambda parameter from an
1002
+ # already-proven `Stream<T>` receiver; it does not fabricate a call target
1003
+ # for the external JDK method itself.
1004
+ JAVA_STREAM_ELEMENT_LAMBDA_METHODS: FrozenSet[str] = frozenset({
1005
+ "allMatch", "anyMatch", "filter", "forEach", "forEachOrdered", "map",
1006
+ "noneMatch", "peek",
1007
+ # Batch 26: the remaining element-consuming single-lambda steps —
1008
+ # `mapToObj`/`mapToInt`/`mapToLong`/`mapToDouble`/`flatMap`/
1009
+ # `takeWhile`/`dropWhile` all receive one element as their sole lambda
1010
+ # parameter (`IntStream.range(..).mapToObj(n -> ..)` is the JUnit5
1011
+ # dynamic-test idiom).
1012
+ "mapToObj", "mapToInt", "mapToLong", "mapToDouble", "flatMap",
1013
+ "takeWhile", "dropWhile",
1014
+ # `Comparator<? super T>` consumers: every lambda parameter is `T`
1015
+ # (`sorted((l, r) -> ...)`, `min`, `max`). `reduce` is excluded because
1016
+ # its identity overload places the accumulator at argument index 1.
1017
+ "max", "min", "sorted",
1018
+ })
1019
+ JAVA_OPTIONAL_ELEMENT_LAMBDA_METHODS: FrozenSet[str] = frozenset({
1020
+ "filter", "flatMap", "ifPresent", "ifPresentOrElse", "map",
1021
+ })
1022
+ # `org.assertj.core.api` static-factory owners whose `assertThat(actual)`
1023
+ # this resolver recognizes by argument type alone (AssertJ's own overload
1024
+ # set lives outside the analyzed source and cannot be resolved structurally).
1025
+ # Used only to type a `Consumer`/`Predicate` argument passed to the returned
1026
+ # assert wrapper's own fluent methods one hop later (`hasValueSatisfying`,
1027
+ # `satisfies`, `allSatisfy`, ...) — never to fabricate a call edge to AssertJ
1028
+ # itself.
1029
+ ASSERTJ_ASSERT_FACTORY_OWNERS: FrozenSet[str] = frozenset({
1030
+ "org.assertj.core.api.Assertions",
1031
+ "org.assertj.core.api.AssertionsForClassTypes",
1032
+ "org.assertj.core.api.AssertionsForInterfaceTypes",
1033
+ })
1034
+ # AssertJ assert-wrapper simple name -> its `Consumer<element>`-typed fluent
1035
+ # methods, keyed by the wrapper this resolver derives in
1036
+ # `_assertj_assertthat_return_type` (`ObjectAssert`, `OptionalAssert`,
1037
+ # `ListAssert`). Real AssertJ also exposes `IterableAssert` for a plain
1038
+ # `Iterable` actual; kept here too since `allSatisfy`/`anySatisfy`/
1039
+ # `noneSatisfy` share the identical signature on both.
1040
+ ASSERTJ_CONSUMER_SATISFIES_METHODS: FrozenSet[str] = frozenset({
1041
+ "hasValueSatisfying", "satisfies", "allSatisfy", "anySatisfy", "noneSatisfy",
1042
+ })
1043
+ # Same wrappers' `Predicate<element>`-typed fluent methods.
1044
+ ASSERTJ_PREDICATE_METHODS: FrozenSet[str] = frozenset({
1045
+ "filteredOn", "allMatch", "anyMatch", "noneMatch",
1046
+ })
1047
+ # `java.lang` simple names that are in scope without an import. A file that
1048
+ # spells one of these without a same-package or imported declaration means the
1049
+ # JDK type; it must never be recovered as an unrelated project class that
1050
+ # happens to share the simple name.
1051
+ JAVA_LANG_SIMPLE_TYPES: FrozenSet[str] = frozenset({
1052
+ "AssertionError", "Boolean", "Byte", "Character", "CharSequence", "Class",
1053
+ "ClassLoader",
1054
+ "CloneNotSupportedException",
1055
+ "Comparable", "Double", "Enum", "Error", "Exception", "Float", "Integer",
1056
+ "IllegalAccessException", "InterruptedException", "Iterable", "LinkageError", "Long", "Math", "NullPointerException", "Number",
1057
+ "Object",
1058
+ "Record", "Runnable",
1059
+ "RuntimeException", "Short", "String", "StringBuilder", "StringBuffer",
1060
+ "System", "Thread", "Throwable", "Void",
1061
+ })
1062
+ # Exact external static-return facts needed for overload argument inference.
1063
+ # Keyed by FQN so a project-local same-simple-name class never matches.
1064
+ JAVA_STATIC_METHOD_STUB_RETURNS: Dict[Tuple[str, str], str] = {
1065
+ ("java.lang.String", "valueOf"): "String",
1066
+ # Every overload of `String.format`/`String.join` returns `String`. A
1067
+ # `String.format(...)` argument is the most common shape of the JUnit5
1068
+ # `overload/java_resolver@0.55` stratum alongside `"...".formatted(...)`
1069
+ # (`assertTrue(cond, String.format(...))` vs the `Supplier<String>`
1070
+ # sibling; `new PreconditionViolationException(String.format(...))`).
1071
+ ("java.lang.String", "format"): "String",
1072
+ ("java.lang.String", "join"): "String",
1073
+ # Batch 24: `java.lang` parsers (no import needed — see
1074
+ # `_external_static_call_return_type`'s `java.lang` default) and the
1075
+ # `java.nio.file.Files` predicates/readers; every overload of each has
1076
+ # the one fixed return listed. `assertTrue(Files.exists(dir))` (JUnit5
1077
+ # `TempDirectory*Tests`, 51 sites) fanned out to `assertTrue
1078
+ # (BooleanSupplier)` before.
1079
+ ("java.lang.Integer", "parseInt"): "int",
1080
+ ("java.lang.Long", "parseLong"): "long",
1081
+ ("java.lang.Short", "parseShort"): "short",
1082
+ ("java.lang.Byte", "parseByte"): "byte",
1083
+ ("java.lang.Double", "parseDouble"): "double",
1084
+ ("java.lang.Float", "parseFloat"): "float",
1085
+ ("java.lang.Boolean", "parseBoolean"): "boolean",
1086
+ ("java.nio.file.Files", "exists"): "boolean",
1087
+ ("java.nio.file.Files", "notExists"): "boolean",
1088
+ ("java.nio.file.Files", "isDirectory"): "boolean",
1089
+ ("java.nio.file.Files", "isRegularFile"): "boolean",
1090
+ ("java.nio.file.Files", "isReadable"): "boolean",
1091
+ ("java.nio.file.Files", "isWritable"): "boolean",
1092
+ ("java.nio.file.Files", "isExecutable"): "boolean",
1093
+ ("java.nio.file.Files", "isHidden"): "boolean",
1094
+ ("java.nio.file.Files", "isSymbolicLink"): "boolean",
1095
+ ("java.nio.file.Files", "isSameFile"): "boolean",
1096
+ ("java.nio.file.Files", "deleteIfExists"): "boolean",
1097
+ ("java.nio.file.Files", "readAllLines"): "List<String>",
1098
+ ("java.nio.file.Files", "readString"): "String",
1099
+ ("java.nio.file.Files", "readAllBytes"): "byte[]",
1100
+ ("java.nio.file.Files", "lines"): "Stream<String>",
1101
+ ("java.nio.file.Files", "size"): "long",
1102
+ # Every overload of `Paths.get` has the fixed declared return `Path`.
1103
+ # Keep the key FQN-scoped so a source `Paths` type never receives this
1104
+ # external JDK contract accidentally.
1105
+ ("java.nio.file.Paths", "get"): "Path",
1106
+ ("java.time.Clock", "systemDefaultZone"): "Clock",
1107
+ # `java.time.Year`'s three fixed-return static factories — needed for
1108
+ # overload narrowing: `YearHasPassed.of(int)` derives a `Year` via one of
1109
+ # these and passes it to the sibling `YearHasPassed.of(Year)`, which used
1110
+ # to fan out against the `int` overload itself since each nested call's
1111
+ # return type was unknown (spring-modulith benchmark, one call site each
1112
+ # in `YearHasPassed.java` (`of`), `Moments.java` (`from`) and
1113
+ # `MomentsUnitTests.java` (`now`)).
1114
+ ("java.time.Year", "of"): "Year",
1115
+ ("java.time.Year", "from"): "Year",
1116
+ ("java.time.Year", "now"): "Year",
1117
+ # `IntStream`/`LongStream`'s two static range factories are fixed to
1118
+ # their own (non-generic) stream type regardless of the bound
1119
+ # expressions, feeding the primitive-stream terminal-op facts in
1120
+ # `JAVA_GENERIC_METHOD_STUB_RETURNS` above.
1121
+ ("java.util.stream.IntStream", "range"): "IntStream",
1122
+ ("java.util.stream.IntStream", "rangeClosed"): "IntStream",
1123
+ ("java.util.stream.LongStream", "range"): "LongStream",
1124
+ ("java.util.stream.LongStream", "rangeClosed"): "LongStream",
1125
+ # Batch 25 — fixed static returns behind remaining JUnit5 boolean/int/
1126
+ # String overload arguments (`Thread.interrupted()`, `Modifier.isPublic
1127
+ # (m)`, `Objects.equals(a, b)`, `Arrays.toString(x)`, `System.
1128
+ # getProperty(k)`) and the fluent roots they chain from
1129
+ # (`Thread.currentThread().isInterrupted()`, `Pattern.compile(p).
1130
+ # matcher(s).matches()`, `Files.createTempDirectory(p).toFile()`).
1131
+ ("java.lang.Thread", "interrupted"): "boolean",
1132
+ ("java.lang.Thread", "currentThread"): "Thread",
1133
+ ("java.lang.Thread", "activeCount"): "int",
1134
+ ("java.lang.System", "currentTimeMillis"): "long",
1135
+ ("java.lang.System", "nanoTime"): "long",
1136
+ ("java.lang.System", "getProperty"): "String",
1137
+ ("java.lang.System", "getenv"): "String",
1138
+ ("java.lang.System", "lineSeparator"): "String",
1139
+ ("java.lang.System", "identityHashCode"): "int",
1140
+ ("java.lang.Boolean", "toString"): "String",
1141
+ ("java.lang.Boolean", "logicalAnd"): "boolean",
1142
+ ("java.lang.Boolean", "logicalOr"): "boolean",
1143
+ ("java.lang.Boolean", "logicalXor"): "boolean",
1144
+ ("java.lang.Boolean", "getBoolean"): "boolean",
1145
+ ("java.lang.Character", "isDigit"): "boolean",
1146
+ ("java.lang.Character", "isLetter"): "boolean",
1147
+ ("java.lang.Character", "isLetterOrDigit"): "boolean",
1148
+ ("java.lang.Character", "isWhitespace"): "boolean",
1149
+ ("java.lang.Character", "isSpaceChar"): "boolean",
1150
+ ("java.lang.Character", "isUpperCase"): "boolean",
1151
+ ("java.lang.Character", "isLowerCase"): "boolean",
1152
+ ("java.lang.Character", "isAlphabetic"): "boolean",
1153
+ ("java.lang.Character", "isJavaIdentifierStart"): "boolean",
1154
+ ("java.lang.Character", "isJavaIdentifierPart"): "boolean",
1155
+ ("java.lang.Integer", "toString"): "String",
1156
+ ("java.lang.Integer", "toHexString"): "String",
1157
+ ("java.lang.Integer", "toBinaryString"): "String",
1158
+ ("java.lang.Integer", "compare"): "int",
1159
+ ("java.lang.Integer", "signum"): "int",
1160
+ ("java.lang.Integer", "bitCount"): "int",
1161
+ ("java.lang.Integer", "sum"): "int",
1162
+ ("java.lang.Integer", "max"): "int",
1163
+ ("java.lang.Integer", "min"): "int",
1164
+ ("java.lang.Long", "toString"): "String",
1165
+ ("java.lang.Long", "toHexString"): "String",
1166
+ ("java.lang.Long", "compare"): "int",
1167
+ ("java.lang.Long", "signum"): "int",
1168
+ ("java.lang.Long", "sum"): "long",
1169
+ ("java.lang.Long", "max"): "long",
1170
+ ("java.lang.Long", "min"): "long",
1171
+ ("java.lang.Double", "toString"): "String",
1172
+ ("java.lang.Double", "compare"): "int",
1173
+ ("java.lang.Double", "isNaN"): "boolean",
1174
+ ("java.lang.Double", "isInfinite"): "boolean",
1175
+ ("java.lang.Double", "isFinite"): "boolean",
1176
+ ("java.lang.Double", "sum"): "double",
1177
+ ("java.lang.Double", "max"): "double",
1178
+ ("java.lang.Double", "min"): "double",
1179
+ ("java.lang.Float", "isNaN"): "boolean",
1180
+ ("java.lang.Float", "isInfinite"): "boolean",
1181
+ ("java.lang.Float", "isFinite"): "boolean",
1182
+ ("java.lang.Math", "sqrt"): "double",
1183
+ ("java.lang.Math", "pow"): "double",
1184
+ ("java.lang.Math", "floor"): "double",
1185
+ ("java.lang.Math", "ceil"): "double",
1186
+ ("java.lang.Math", "random"): "double",
1187
+ ("java.lang.Math", "toIntExact"): "int",
1188
+ ("java.lang.String", "copyValueOf"): "String",
1189
+ ("java.lang.Class", "forName"): "Class",
1190
+ # Batch 26 — boxed `valueOf` factories (every overload returns the box),
1191
+ # `IntStream`/`Stream` sources whose result type is fixed, class loading.
1192
+ ("java.lang.Integer", "valueOf"): "Integer",
1193
+ ("java.lang.Long", "valueOf"): "Long",
1194
+ ("java.lang.Short", "valueOf"): "Short",
1195
+ ("java.lang.Byte", "valueOf"): "Byte",
1196
+ ("java.lang.Double", "valueOf"): "Double",
1197
+ ("java.lang.Float", "valueOf"): "Float",
1198
+ ("java.lang.Boolean", "valueOf"): "Boolean",
1199
+ ("java.lang.Character", "valueOf"): "Character",
1200
+ ("java.util.stream.IntStream", "of"): "IntStream",
1201
+ ("java.util.stream.IntStream", "iterate"): "IntStream",
1202
+ ("java.util.stream.IntStream", "generate"): "IntStream",
1203
+ ("java.util.stream.IntStream", "concat"): "IntStream",
1204
+ ("java.util.stream.IntStream", "empty"): "IntStream",
1205
+ ("java.util.stream.LongStream", "of"): "LongStream",
1206
+ ("java.util.stream.LongStream", "iterate"): "LongStream",
1207
+ ("java.util.stream.LongStream", "generate"): "LongStream",
1208
+ ("java.util.stream.LongStream", "concat"): "LongStream",
1209
+ ("java.util.stream.DoubleStream", "of"): "DoubleStream",
1210
+ ("java.util.stream.DoubleStream", "iterate"): "DoubleStream",
1211
+ ("java.util.stream.DoubleStream", "generate"): "DoubleStream",
1212
+ ("java.lang.reflect.Modifier", "isPublic"): "boolean",
1213
+ ("java.lang.reflect.Modifier", "isPrivate"): "boolean",
1214
+ ("java.lang.reflect.Modifier", "isProtected"): "boolean",
1215
+ ("java.lang.reflect.Modifier", "isStatic"): "boolean",
1216
+ ("java.lang.reflect.Modifier", "isFinal"): "boolean",
1217
+ ("java.lang.reflect.Modifier", "isAbstract"): "boolean",
1218
+ ("java.lang.reflect.Modifier", "isInterface"): "boolean",
1219
+ ("java.lang.reflect.Modifier", "isNative"): "boolean",
1220
+ ("java.lang.reflect.Modifier", "isSynchronized"): "boolean",
1221
+ ("java.lang.reflect.Modifier", "isTransient"): "boolean",
1222
+ ("java.lang.reflect.Modifier", "isVolatile"): "boolean",
1223
+ ("java.lang.reflect.Modifier", "toString"): "String",
1224
+ ("java.util.Objects", "equals"): "boolean",
1225
+ ("java.util.Objects", "deepEquals"): "boolean",
1226
+ ("java.util.Objects", "isNull"): "boolean",
1227
+ ("java.util.Objects", "nonNull"): "boolean",
1228
+ ("java.util.Objects", "hash"): "int",
1229
+ ("java.util.Objects", "hashCode"): "int",
1230
+ ("java.util.Objects", "toString"): "String",
1231
+ ("java.util.Arrays", "equals"): "boolean",
1232
+ ("java.util.Arrays", "deepEquals"): "boolean",
1233
+ ("java.util.Arrays", "toString"): "String",
1234
+ ("java.util.Arrays", "deepToString"): "String",
1235
+ ("java.util.Arrays", "hashCode"): "int",
1236
+ ("java.util.Arrays", "deepHashCode"): "int",
1237
+ ("java.util.Collections", "disjoint"): "boolean",
1238
+ ("java.util.Collections", "addAll"): "boolean",
1239
+ ("java.util.Collections", "frequency"): "int",
1240
+ ("java.util.regex.Pattern", "compile"): "java.util.regex.Pattern",
1241
+ ("java.util.regex.Pattern", "matches"): "boolean",
1242
+ ("java.util.regex.Pattern", "quote"): "String",
1243
+ ("java.nio.file.Path", "of"): "Path",
1244
+ ("java.nio.file.Files", "createFile"): "Path",
1245
+ ("java.nio.file.Files", "createDirectory"): "Path",
1246
+ ("java.nio.file.Files", "createDirectories"): "Path",
1247
+ ("java.nio.file.Files", "createTempFile"): "Path",
1248
+ ("java.nio.file.Files", "createTempDirectory"): "Path",
1249
+ ("java.nio.file.Files", "write"): "Path",
1250
+ ("java.nio.file.Files", "writeString"): "Path",
1251
+ ("java.nio.file.Files", "copy"): "Path",
1252
+ ("java.nio.file.Files", "move"): "Path",
1253
+ ("java.nio.file.Files", "walk"): "Stream<Path>",
1254
+ ("java.nio.file.Files", "list"): "Stream<Path>",
1255
+ ("java.nio.file.Files", "getLastModifiedTime"): "java.nio.file.attribute.FileTime",
1256
+ # Every overload of Spring's `ClassUtils.getPackageName` (`Class<?>` or
1257
+ # `String` FQN) is fixed to `String` regardless of the argument. Needed
1258
+ # for overload narrowing (E3, JAVA_METRICS_SPRING_MODULITH_PRECISION_SAMPLE.md):
1259
+ # `PackageName.of(ClassUtils.getPackageName(type))` fanned out between
1260
+ # `of(String)`/`of(String[])` because the nested call's return type was
1261
+ # unknown to the narrowing pass.
1262
+ ("org.springframework.util.ClassUtils", "getPackageName"): "String",
1263
+ ("org.springframework.util.ClassUtils", "resolveClassName"): "Class",
1264
+ # Spring's AOP helpers return a JDK Class even though their declarations
1265
+ # live outside the analyzed source. Without this exact external contract,
1266
+ # `var listenerType = AopUtils.getTargetClass(initializer)` leaves
1267
+ # `listenerType` unknown and later overload calls fan out across
1268
+ # `of(Class)`/`of(JavaClass)`-style siblings.
1269
+ ("org.springframework.aop.support.AopUtils", "getTargetClass"): "Class",
1270
+ }
1271
+ # Batch 24: declared types of a small set of external (JDK) static
1272
+ # constants, FQN-keyed exactly like `JAVA_STATIC_METHOD_STUB_RETURNS` so a
1273
+ # source class that shares the simple name is never stubbed. Used only by
1274
+ # overload-argument narrowing (`_external_static_field_arg_type`):
1275
+ # `assertEquals(Integer.MAX_VALUE, parseFastForwardLimit(...))` fanned out
1276
+ # across every numeric `assertEquals` overload, and `LogRecordListener.stream
1277
+ # (Level.WARNING)` between `stream(Level)`/`stream(Class)`.
1278
+ # Simple names of the JDK `Throwable` family: a source exception class
1279
+ # extending any of these inherits `Throwable`'s members (JLS 8.4.8), so the
1280
+ # `("Throwable", member)` facts above apply to it when its own hierarchy
1281
+ # declares no member of that name.
1282
+ JAVA_THROWABLE_SIMPLE_TYPES: FrozenSet[str] = frozenset({
1283
+ "Throwable", "Exception", "RuntimeException", "Error", "AssertionError",
1284
+ "IllegalArgumentException", "IllegalStateException",
1285
+ "UnsupportedOperationException", "NullPointerException",
1286
+ "IndexOutOfBoundsException", "ArithmeticException", "ClassCastException",
1287
+ "NumberFormatException", "SecurityException", "IOException",
1288
+ "UncheckedIOException", "InterruptedException", "ExecutionException",
1289
+ "TimeoutException", "ReflectiveOperationException",
1290
+ "ClassNotFoundException", "NoSuchMethodException", "NoSuchFieldException",
1291
+ "IllegalAccessException", "InstantiationException",
1292
+ "InvocationTargetException", "CloneNotSupportedException", "LinkageError",
1293
+ })
1294
+ JAVA_STATIC_FIELD_STUB_TYPES: Dict[Tuple[str, str], str] = {
1295
+ ("java.lang.Double", "MIN_NORMAL"): "double",
1296
+ ("java.lang.Float", "MIN_NORMAL"): "float",
1297
+ ("java.lang.Double", "MAX_EXPONENT"): "int",
1298
+ ("java.lang.Double", "MIN_EXPONENT"): "int",
1299
+ ("java.lang.Float", "MAX_EXPONENT"): "int",
1300
+ ("java.lang.Float", "MIN_EXPONENT"): "int",
1301
+ ("java.lang.Integer", "SIZE"): "int",
1302
+ ("java.lang.Integer", "BYTES"): "int",
1303
+ ("java.lang.Long", "SIZE"): "int",
1304
+ ("java.lang.Long", "BYTES"): "int",
1305
+ ("java.lang.Double", "SIZE"): "int",
1306
+ ("java.lang.Double", "BYTES"): "int",
1307
+ ("java.lang.Float", "SIZE"): "int",
1308
+ ("java.lang.Float", "BYTES"): "int",
1309
+ ("java.lang.Short", "SIZE"): "int",
1310
+ ("java.lang.Byte", "SIZE"): "int",
1311
+ ("java.lang.Character", "SIZE"): "int",
1312
+ **{("java.lang.Integer", name): "int" for name in ("MAX_VALUE", "MIN_VALUE", "SIZE", "BYTES")},
1313
+ **{("java.lang.Long", name): "long" for name in ("MAX_VALUE", "MIN_VALUE")},
1314
+ **{("java.lang.Short", name): "short" for name in ("MAX_VALUE", "MIN_VALUE")},
1315
+ **{("java.lang.Byte", name): "byte" for name in ("MAX_VALUE", "MIN_VALUE")},
1316
+ **{("java.lang.Character", name): "char" for name in ("MAX_VALUE", "MIN_VALUE")},
1317
+ **{("java.lang.Double", name): "double" for name in (
1318
+ "MAX_VALUE", "MIN_VALUE", "NaN", "POSITIVE_INFINITY", "NEGATIVE_INFINITY",
1319
+ )},
1320
+ **{("java.lang.Float", name): "float" for name in (
1321
+ "MAX_VALUE", "MIN_VALUE", "NaN", "POSITIVE_INFINITY", "NEGATIVE_INFINITY",
1322
+ )},
1323
+ ("java.lang.Boolean", "TRUE"): "Boolean",
1324
+ ("java.lang.Boolean", "FALSE"): "Boolean",
1325
+ **{("java.util.logging.Level", name): "java.util.logging.Level" for name in (
1326
+ "OFF", "SEVERE", "WARNING", "INFO", "CONFIG", "FINE", "FINER", "FINEST", "ALL",
1327
+ )},
1328
+ **{("java.nio.charset.StandardCharsets", name): "java.nio.charset.Charset" for name in (
1329
+ "UTF_8", "UTF_16", "UTF_16BE", "UTF_16LE", "US_ASCII", "ISO_8859_1",
1330
+ )},
1331
+ **{("java.util.concurrent.TimeUnit", name): "java.util.concurrent.TimeUnit" for name in (
1332
+ "NANOSECONDS", "MICROSECONDS", "MILLISECONDS", "SECONDS", "MINUTES", "HOURS", "DAYS",
1333
+ )},
1334
+ **{("java.util.Locale", name): "java.util.Locale" for name in (
1335
+ "ROOT", "ENGLISH", "US", "UK", "GERMAN", "GERMANY", "FRENCH", "FRANCE",
1336
+ )},
1337
+ }
1338
+ # JLS 4.3.2: every class inherits these zero-argument `Object` members and an
1339
+ # override cannot change their erased return type, so the invocation result is
1340
+ # known for any receiver, even an unresolvable one.
1341
+ JAVA_OBJECT_MEMBER_RETURNS: Dict[str, str] = {
1342
+ "toString": "String",
1343
+ "hashCode": "int",
1344
+ "getClass": "Class",
1345
+ }
1346
+ # `java.time`'s immutable value types are fluent builders: every `plus*`/
1347
+ # `minus*`/`with*` mutator and `truncatedTo` is JavaDoc-guaranteed to return
1348
+ # the exact same type as the receiver — not a guess, a fixed JDK contract —
1349
+ # yet none of it lives in analyzed source, so a chain like
1350
+ # `now().minusHours(1)` used to go unknown past the first hop and force an
1351
+ # otherwise-resolvable sibling-overload call into P4.4's ambiguous fan-out
1352
+ # (`Moments.everyHour()`'s `emitEventsFor(now().minusHours(1))` fanning out
1353
+ # to both `emitEventsFor(LocalDateTime)` and `emitEventsFor(LocalDate)`,
1354
+ # wrongly reaching `DayHasPassed.of` — spring-modulith benchmark finding).
1355
+ JAVA_TIME_TEMPORAL_TYPES: FrozenSet[str] = frozenset({
1356
+ "LocalDate", "LocalDateTime", "LocalTime", "Instant", "ZonedDateTime",
1357
+ "OffsetDateTime", "OffsetTime", "Duration", "Period", "Year", "YearMonth",
1358
+ "MonthDay",
1359
+ })
1360
+ JAVA_TIME_SELF_RETURNING_PREFIXES: Tuple[str, ...] = ("plus", "minus", "with")
1361
+ JAVA_TIME_SELF_RETURNING_EXACT: FrozenSet[str] = frozenset({"truncatedTo"})
1362
+ # A handful of zero-arg, single-overload cross-type conversions with one
1363
+ # fixed JDK-declared return type. Deliberately excludes any method with a
1364
+ # same-name overload whose return type differs by arity or argument type
1365
+ # (`LocalDate.atStartOfDay()` -> LocalDateTime vs.
1366
+ # `atStartOfDay(ZoneId)` -> ZonedDateTime stays unresolved rather than
1367
+ # guessed at the wrong overload).
1368
+ JAVA_TIME_CONVERSION_RETURNS: Dict[Tuple[str, str], str] = {
1369
+ ("LocalDateTime", "toLocalDate"): "LocalDate",
1370
+ ("LocalDateTime", "toLocalTime"): "LocalTime",
1371
+ ("ZonedDateTime", "toLocalDate"): "LocalDate",
1372
+ ("ZonedDateTime", "toLocalDateTime"): "LocalDateTime",
1373
+ ("ZonedDateTime", "toLocalTime"): "LocalTime",
1374
+ ("ZonedDateTime", "toInstant"): "Instant",
1375
+ ("OffsetDateTime", "toLocalDate"): "LocalDate",
1376
+ ("OffsetDateTime", "toLocalDateTime"): "LocalDateTime",
1377
+ ("OffsetDateTime", "toLocalTime"): "LocalTime",
1378
+ ("OffsetDateTime", "toInstant"): "Instant",
1379
+ }
1380
+ # `BeanFactory.getBean(Class<T>)` (and its context/test-runner subtypes)
1381
+ # returns exactly the argument's own `Class<T>` literal type — documented
1382
+ # Spring Framework contract, not a guess — but none of these interfaces have
1383
+ # source in an analyzed project, so `ctxt.getBean(Foo.class)` used to leave
1384
+ # the call's result unknown even when `Foo` itself is a source class
1385
+ # (spring-modulith benchmark finding: `KafkaEventExternalizerConfiguration
1386
+ # IntegrationTests`'s `ctxt.getBean(DelegatingEventExternalizer.class)`
1387
+ # never resolved, hiding 3 real transitive callers behind it). Deliberately
1388
+ # excludes `getBean(String)`/`getBean(String, Class<T>)` string-named lookup
1389
+ # and multi-arg constructor-arg forms; only the exact single-`Class<T>`-
1390
+ # literal overload has one unambiguous, name-independent target.
1391
+ SPRING_BEAN_CONTAINER_TYPES: FrozenSet[str] = frozenset({
1392
+ "BeanFactory", "ListableBeanFactory", "ConfigurableListableBeanFactory",
1393
+ "ApplicationContext", "ConfigurableApplicationContext",
1394
+ "WebApplicationContext", "ConfigurableWebApplicationContext",
1395
+ "AssertableApplicationContext", "AssertableWebApplicationContext",
1396
+ "AssertableReactiveWebApplicationContext",
1397
+ })
1398
+ # Spring Boot Test's `AbstractApplicationContextRunner.run(ContextConsumer<?
1399
+ # super A>)` target-types its lambda/method-reference argument's single
1400
+ # parameter as the runner's own fixed assertable-context type `A` — a
1401
+ # documented, non-generic-inferred contract per runner subclass (each
1402
+ # subclass hard-codes its own `A`), not a project fact. None of these
1403
+ # classes have source in an analyzed project, so a lambda passed to `.run`
1404
+ # used to leave its parameter's type completely unknown (see
1405
+ # `SPRING_BEAN_CONTAINER_TYPES` above for why that mattered here).
1406
+ SPRING_CONTEXT_RUNNER_ASSERTABLE_CONTEXT: Dict[str, str] = {
1407
+ "ApplicationContextRunner": "AssertableApplicationContext",
1408
+ "WebApplicationContextRunner": "AssertableWebApplicationContext",
1409
+ "ReactiveWebApplicationContextRunner": "AssertableReactiveWebApplicationContext",
1410
+ }
1411
+ # SAM parameter tables for a small set of well-known *external*
1412
+ # (non-JDK) functional interfaces, kept deliberately separate from
1413
+ # `_JDK_SAM_PARAMS`/`_JDK_SAM_RETURNS` above so that table's "derivable
1414
+ # without indexing JDK sources" invariant stays exactly JDK-only.
1415
+ # `ContextConsumer<C>`'s single abstract method is
1416
+ # `void accept(C context) throws Throwable` (Spring Boot Test).
1417
+ EXTERNAL_SAM_PARAMS: Dict[str, Tuple[Union[int, str], ...]] = {
1418
+ "ContextConsumer": (0,),
1419
+ }
1420
+ EXTERNAL_SAM_RETURNS: Dict[str, Union[int, str]] = {
1421
+ "ContextConsumer": "void",
1422
+ }
1423
+ # Small, exact JDK hierarchy facts used only when both sides resolve through
1424
+ # their file's explicit import context. This is not a simple-name fallback:
1425
+ # a project-local `HashMap` never matches `java.util.HashMap` here.
1426
+ JAVA_EXTERNAL_SUBTYPE_STUBS: FrozenSet[Tuple[str, str]] = frozenset({
1427
+ # Batch 25: the JDK types the new fixed-return facts and static
1428
+ # factories can now put *into* an argument position (`Arrays.asList(..)`
1429
+ # -> `List`, `EnumSet.of(..)`, `Stack`, reflection/concurrency
1430
+ # receivers, `Throwable.getCause()`), each with the supertypes a
1431
+ # declared parameter is commonly spelled as. A newly typed argument
1432
+ # that cannot reach its parameter here is a dropped candidate (FN),
1433
+ # not an unknown, so every producible type needs its ancestry listed.
1434
+ ("java.util.List", "java.util.SequencedCollection"),
1435
+ ("java.util.ArrayList", "java.lang.Iterable"),
1436
+ ("java.util.LinkedList", "java.util.List"),
1437
+ ("java.util.LinkedList", "java.util.Collection"),
1438
+ ("java.util.LinkedList", "java.lang.Iterable"),
1439
+ ("java.util.LinkedList", "java.util.Deque"),
1440
+ ("java.util.LinkedList", "java.util.Queue"),
1441
+ ("java.util.Stack", "java.util.Vector"),
1442
+ ("java.util.Stack", "java.util.List"),
1443
+ ("java.util.Stack", "java.util.Collection"),
1444
+ ("java.util.Stack", "java.lang.Iterable"),
1445
+ ("java.util.Vector", "java.util.List"),
1446
+ ("java.util.Vector", "java.util.Collection"),
1447
+ ("java.util.Vector", "java.lang.Iterable"),
1448
+ ("java.util.HashSet", "java.util.Collection"),
1449
+ ("java.util.HashSet", "java.lang.Iterable"),
1450
+ ("java.util.LinkedHashSet", "java.util.Set"),
1451
+ ("java.util.LinkedHashSet", "java.util.Collection"),
1452
+ ("java.util.LinkedHashSet", "java.lang.Iterable"),
1453
+ ("java.util.TreeSet", "java.util.Set"),
1454
+ ("java.util.TreeSet", "java.util.SortedSet"),
1455
+ ("java.util.TreeSet", "java.util.NavigableSet"),
1456
+ ("java.util.TreeSet", "java.util.Collection"),
1457
+ ("java.util.TreeSet", "java.lang.Iterable"),
1458
+ ("java.util.SortedSet", "java.util.Set"),
1459
+ ("java.util.SortedSet", "java.util.Collection"),
1460
+ ("java.util.SortedSet", "java.lang.Iterable"),
1461
+ ("java.util.EnumSet", "java.util.Set"),
1462
+ ("java.util.EnumSet", "java.util.Collection"),
1463
+ ("java.util.EnumSet", "java.lang.Iterable"),
1464
+ ("java.util.ArrayDeque", "java.util.Collection"),
1465
+ ("java.util.ArrayDeque", "java.lang.Iterable"),
1466
+ ("java.util.Deque", "java.util.Queue"),
1467
+ ("java.util.Deque", "java.util.Collection"),
1468
+ ("java.util.Deque", "java.lang.Iterable"),
1469
+ ("java.util.Queue", "java.util.Collection"),
1470
+ ("java.util.Queue", "java.lang.Iterable"),
1471
+ ("java.util.concurrent.ConcurrentHashMap", "java.util.Map"),
1472
+ ("java.util.concurrent.ConcurrentHashMap", "java.util.concurrent.ConcurrentMap"),
1473
+ ("java.util.concurrent.ConcurrentMap", "java.util.Map"),
1474
+ ("java.util.regex.Matcher", "java.util.regex.MatchResult"),
1475
+ ("java.util.concurrent.locks.ReentrantLock", "java.util.concurrent.locks.Lock"),
1476
+ ("java.util.concurrent.ForkJoinPool", "java.util.concurrent.ExecutorService"),
1477
+ ("java.util.concurrent.ForkJoinPool", "java.util.concurrent.Executor"),
1478
+ ("java.util.concurrent.ThreadPoolExecutor", "java.util.concurrent.ExecutorService"),
1479
+ ("java.util.concurrent.ThreadPoolExecutor", "java.util.concurrent.Executor"),
1480
+ ("java.util.concurrent.ScheduledExecutorService", "java.util.concurrent.ExecutorService"),
1481
+ ("java.util.concurrent.ScheduledExecutorService", "java.util.concurrent.Executor"),
1482
+ ("java.util.concurrent.ExecutorService", "java.util.concurrent.Executor"),
1483
+ ("java.util.concurrent.CompletableFuture", "java.util.concurrent.Future"),
1484
+ ("java.util.concurrent.CompletableFuture", "java.util.concurrent.CompletionStage"),
1485
+ ("java.lang.Thread", "java.lang.Runnable"),
1486
+ ("java.lang.reflect.Method", "java.lang.reflect.Executable"),
1487
+ ("java.lang.reflect.Method", "java.lang.reflect.AccessibleObject"),
1488
+ ("java.lang.reflect.Method", "java.lang.reflect.Member"),
1489
+ ("java.lang.reflect.Method", "java.lang.reflect.AnnotatedElement"),
1490
+ ("java.lang.reflect.Constructor", "java.lang.reflect.Executable"),
1491
+ ("java.lang.reflect.Constructor", "java.lang.reflect.AccessibleObject"),
1492
+ ("java.lang.reflect.Constructor", "java.lang.reflect.Member"),
1493
+ ("java.lang.reflect.Constructor", "java.lang.reflect.AnnotatedElement"),
1494
+ ("java.lang.reflect.Executable", "java.lang.reflect.AccessibleObject"),
1495
+ ("java.lang.reflect.Executable", "java.lang.reflect.Member"),
1496
+ ("java.lang.reflect.Executable", "java.lang.reflect.AnnotatedElement"),
1497
+ ("java.lang.reflect.Field", "java.lang.reflect.AccessibleObject"),
1498
+ ("java.lang.reflect.Field", "java.lang.reflect.Member"),
1499
+ ("java.lang.reflect.Field", "java.lang.reflect.AnnotatedElement"),
1500
+ ("java.lang.reflect.Parameter", "java.lang.reflect.AnnotatedElement"),
1501
+ ("java.lang.Class", "java.lang.reflect.AnnotatedElement"),
1502
+ ("java.lang.Class", "java.lang.reflect.Type"),
1503
+ ("java.lang.Class", "java.lang.reflect.GenericDeclaration"),
1504
+ ("java.lang.Module", "java.lang.reflect.AnnotatedElement"),
1505
+ ("java.io.File", "java.lang.Comparable"),
1506
+ ("java.io.File", "java.io.Serializable"),
1507
+ ("java.nio.file.Path", "java.lang.Comparable"),
1508
+ ("java.nio.file.Path", "java.lang.Iterable"),
1509
+ ("java.nio.file.Path", "java.nio.file.Watchable"),
1510
+ ("java.lang.IllegalArgumentException", "java.lang.RuntimeException"),
1511
+ ("java.lang.IllegalArgumentException", "java.lang.Exception"),
1512
+ ("java.lang.IllegalArgumentException", "java.lang.Throwable"),
1513
+ ("java.lang.IllegalStateException", "java.lang.RuntimeException"),
1514
+ ("java.lang.IllegalStateException", "java.lang.Exception"),
1515
+ ("java.lang.IllegalStateException", "java.lang.Throwable"),
1516
+ ("java.lang.UnsupportedOperationException", "java.lang.RuntimeException"),
1517
+ ("java.lang.UnsupportedOperationException", "java.lang.Exception"),
1518
+ ("java.lang.UnsupportedOperationException", "java.lang.Throwable"),
1519
+ ("java.io.IOException", "java.lang.Exception"),
1520
+ ("java.io.UncheckedIOException", "java.lang.RuntimeException"),
1521
+ ("java.io.UncheckedIOException", "java.lang.Exception"),
1522
+ ("java.io.UncheckedIOException", "java.lang.Throwable"),
1523
+ ("java.lang.InterruptedException", "java.lang.Exception"),
1524
+ ("java.lang.InterruptedException", "java.lang.Throwable"),
1525
+ ("java.util.concurrent.ExecutionException", "java.lang.Exception"),
1526
+ ("java.util.concurrent.ExecutionException", "java.lang.Throwable"),
1527
+ ("java.util.concurrent.TimeoutException", "java.lang.Exception"),
1528
+ ("java.util.concurrent.TimeoutException", "java.lang.Throwable"),
1529
+ ("java.lang.ReflectiveOperationException", "java.lang.Exception"),
1530
+ ("java.lang.ReflectiveOperationException", "java.lang.Throwable"),
1531
+ ("java.lang.ClassNotFoundException", "java.lang.ReflectiveOperationException"),
1532
+ ("java.lang.ClassNotFoundException", "java.lang.Exception"),
1533
+ ("java.lang.ClassNotFoundException", "java.lang.Throwable"),
1534
+ ("java.lang.NoSuchMethodException", "java.lang.ReflectiveOperationException"),
1535
+ ("java.lang.NoSuchMethodException", "java.lang.Exception"),
1536
+ ("java.lang.NoSuchMethodException", "java.lang.Throwable"),
1537
+ ("java.lang.reflect.InvocationTargetException", "java.lang.ReflectiveOperationException"),
1538
+ ("java.lang.reflect.InvocationTargetException", "java.lang.Exception"),
1539
+ ("java.lang.reflect.InvocationTargetException", "java.lang.Throwable"),
1540
+ ("java.io.StringReader", "java.io.Reader"),
1541
+ ("java.lang.String", "java.lang.CharSequence"),
1542
+ ("java.util.ArrayList", "java.util.List"),
1543
+ ("java.util.ArrayList", "java.util.Collection"),
1544
+ ("java.util.ArrayDeque", "java.util.Deque"),
1545
+ ("java.util.ArrayDeque", "java.util.Queue"),
1546
+ ("java.util.HashMap", "java.util.Map"),
1547
+ ("java.util.LinkedHashMap", "java.util.Map"),
1548
+ ("java.util.TreeMap", "java.util.Map"),
1549
+ ("java.util.HashSet", "java.util.Set"),
1550
+ ("java.util.List", "java.util.Collection"),
1551
+ ("java.util.List", "java.lang.Iterable"),
1552
+ ("java.util.Set", "java.util.Collection"),
1553
+ ("java.util.Set", "java.lang.Iterable"),
1554
+ ("java.util.Collection", "java.lang.Iterable"),
1555
+ ("java.util.concurrent.LinkedBlockingQueue", "java.util.concurrent.BlockingQueue"),
1556
+ ("java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy", "java.util.concurrent.RejectedExecutionHandler"),
1557
+ ("java.lang.Exception", "java.lang.Throwable"),
1558
+ ("java.lang.RuntimeException", "java.lang.Throwable"),
1559
+ ("java.lang.RuntimeException", "java.lang.Exception"),
1560
+ ("java.lang.Error", "java.lang.Throwable"),
1561
+ ("java.lang.LinkageError", "java.lang.Error"),
1562
+ ("java.lang.LinkageError", "java.lang.Throwable"),
1563
+ ("java.lang.AssertionError", "java.lang.Error"),
1564
+ ("java.lang.AssertionError", "java.lang.Throwable"),
1565
+ ("java.lang.NullPointerException", "java.lang.RuntimeException"),
1566
+ ("java.lang.NullPointerException", "java.lang.Exception"),
1567
+ ("java.lang.NullPointerException", "java.lang.Throwable"),
1568
+ ("java.io.IOException", "java.lang.Throwable"),
1569
+ ("java.io.UnsupportedEncodingException", "java.io.IOException"),
1570
+ ("java.io.UnsupportedEncodingException", "java.lang.Throwable"),
1571
+ ("java.lang.CloneNotSupportedException", "java.lang.Exception"),
1572
+ ("java.lang.CloneNotSupportedException", "java.lang.Throwable"),
1573
+ ("java.lang.IllegalAccessException", "java.lang.Throwable"),
1574
+ ("java.lang.InterruptedException", "java.lang.Throwable"),
1575
+ ("java.net.URISyntaxException", "java.lang.Throwable"),
1576
+ ("java.lang.Class", "java.lang.reflect.AnnotatedElement"),
1577
+ ("java.lang.reflect.Constructor", "java.lang.reflect.AnnotatedElement"),
1578
+ ("java.lang.reflect.Constructor", "java.lang.reflect.Member"),
1579
+ # JDK reflection hierarchy: both Method and Constructor extend
1580
+ # Executable. This lets an exactly typed Method select a source overload
1581
+ # declared against Executable without treating unrelated Member values as
1582
+ # executable.
1583
+ ("java.lang.reflect.Constructor", "java.lang.reflect.Executable"),
1584
+ ("java.lang.reflect.Executable", "java.lang.reflect.AnnotatedElement"),
1585
+ ("java.lang.reflect.Field", "java.lang.reflect.AnnotatedElement"),
1586
+ ("java.lang.reflect.Field", "java.lang.reflect.Member"),
1587
+ ("java.lang.reflect.Method", "java.lang.reflect.AnnotatedElement"),
1588
+ ("java.lang.reflect.Method", "java.lang.reflect.Member"),
1589
+ ("java.lang.reflect.Method", "java.lang.reflect.Executable"),
1590
+ })
1591
+ JAVA_EXTERNAL_TYPES = frozenset(name for relation in JAVA_EXTERNAL_SUBTYPE_STUBS for name in relation)
1592
+ # Concrete `java_type_kind`s that can carry a runtime method body — the only
1593
+ # ones eligible to be a fan-out/DI target (an `interface`/`abstract class`
1594
+ # receiver is the *source* of the ambiguity, never a resolved target).
1595
+ _CONCRETE_TYPE_KINDS: FrozenSet[str] = frozenset({"class", "record", "enum"})
1596
+
1597
+ # Precision↔recall dial (deep-dive §6.3), mirroring P2.21's `DRIFT_RECALL_MODE`.
1598
+ # `strict` keeps only the interface-declaration edge (P4.4's own result);
1599
+ # `balanced` (default, precision-first) fans out to concrete implementors but
1600
+ # caps emission above `_FANOUT_CAP` to avoid flooding; `recall` fans out to
1601
+ # every implementor as a low-confidence candidate regardless of count.
1602
+ _FANOUT_MODES = ("strict", "balanced", "recall")
1603
+ _DEFAULT_FANOUT_MODE = "balanced"
1604
+ _DEFAULT_FANOUT_CAP = 5
1605
+
1606
+ # Per-edge confidence for each fan-out/DI strategy — the evidence table in
1607
+ # codegraph-java-deep-dive.md §6.3 / PROBLEMS.md P4.12. A single implementor is
1608
+ # effectively concrete (High band); many implementors are honest ambiguity
1609
+ # (Low band, <0.6). DI evidence tightens the score toward concrete.
1610
+ _CONF_SINGLE_IMPLEMENTOR = 0.85
1611
+ _CONF_MULTI_IMPLEMENTOR = 0.45
1612
+ _CONF_DI_SINGLE_COMPONENT = 0.85
1613
+ _CONF_SPRING_QUALIFIER = 0.95
1614
+ _CONF_SPRING_PRIMARY = 0.90
1615
+
1616
+
1617
+ # Matches the first string literal argument of an annotation, e.g.
1618
+ # `@Qualifier("stripe")` -> `stripe`, `@Service("fastPayment")` -> `fastPayment`.
1619
+ _ANNOTATION_STRING_ARG = re.compile(r'"((?:[^"\\]|\\.)*)"')