Commit b3b46bd
authored
feat: L1 emission in canonical schema v2 (opt-in via --schema v2) (#188)
* feat(schema): can:// id + byte-offset span utils for v2 (#180)
* test: add real-world Java app fixtures as git submodules (#180)
* feat(schema): v2 L1 module/type tree — AST-driven modular builders (#180)
* build: exclude test-application fixtures from spotless formatting (#180)
* feat(schema): nested member types via containment; drop nesting field (D4) (#180)
* feat(schema): callable/field/param builders; call sites keyed by local id (#180)
Key body nodes by bare local id (line:col) per the keystone, and emit call nodes for constructor
invocations and this()/super() chaining so L2 can resolve those edges.
* feat(schema): v2 JSON config, module imports/hash/span, field kind, is_variadic (#180)
Add V2Json (snake_case keys, no nulls emitted), module span/imports/
content_hash, field kind discriminator and parameter is_variadic, with a
serialization-contract test covering the emitted key names.
* feat(schema): type/param modifiers, field initializer, declaration, code_start_line, comments (#180)
* feat(schema): local variables on callable (#180)
* feat(schema): resolve types via the JavaParser symbol solver at L1 (#180)
Types, supertypes, error_channel and refs.types are now resolved to
qualified names, and callable signatures use erased resolved parameter
types, matching the v1 symbol table. Tests parse with a symbol solver so
the resolution path is actually exercised; unresolvable types degrade to
their AST spelling.
* feat(schema): enum constants, record components, initializer-block callables (#180)
* feat(schema): rich call-site facts on call nodes (receiver/arg types, callee signature, flags) (#180)
* feat(schema): entrypoint flags, qualified field refs, broader type refs, AstScopes (#180)
* feat(schema): L1 extractor — v2 modules from the project parse loop (#180)
* feat(cli): --schema v2 emits the canonical envelope, with flag validation (#180)
v2 is opt-in (v1 stays the default until the rest of the migration lands).
Unsupported combinations (-a > 1, --emit neo4j, --source-analysis,
--target-files, unknown --schema) fail with a clear non-zero error rather
than silently emitting a different shape. stdout carries compact JSON only.
* test: L1 conformance gate against the canonical v2 JSON Schema (#180)
Adds the strict in-repo schema used as the L1 oracle until the SDK's v2
models exist, a gate over the in-repo fixtures in the default suite, and a
realWorldConformanceTest task for the submodule applications (too slow for
the inner loop, but required).
* fix(schema): resolve library types at L1; omit unknown static-call flag (#180)
L1 now downloads the project's dependencies and puts a JarTypeSolver on the
solver path, so third-party types resolve to qualified names as they do in
v1 (verified on spring-petclinic: Model, Pageable, Page<Owner>, and
callee_signature on 99% of call sites). Reflection is restricted to the JRE
so the analyzer's own dependencies can no longer be resolved as if the
analysed project depended on them. is_static_call becomes a Boolean that is
omitted when the callee is unresolved rather than reported as false.
* test: drop the daytrader-microservices fixture (contains no Java source)
The repo is deployment tooling (Makefile, docker-compose, helm charts) with
zero .java files, so it cannot exercise the analyzer. Also ignores output/,
used for ad-hoc v1/v2 comparison runs.
* feat(schema): model anonymous classes; add callable body_span (#180)
Anonymous class bodies now get their own type node ($anon$N) under the
callable that declares them, like named local classes — v1 mis-attributed
their initializers and locals to the enclosing type and the first v2 attempt
dropped them. Re-measured, initializer blocks and local variables are back
at parity with v1.
body_span delimits the { ... } block so source[body_span.bytes] reproduces
v1's per-callable code byte for byte without duplicating the text; the
callable's own span covers the whole declaration. Pinned by a test that
compares against the v1 emitter directly.
* feat(schema): model anonymous classes in field initializers; add comparison report
Anonymous classes occur in two places: inside a callable body and inside a
field initializer, which belongs to no callable. The latter was missed, so
commons-lang's AnnotationUtils lost the double-brace initializer configuring
its ToStringStyle.
Adds docs/design/notes/l1-v1-v2-comparison.md, generated from twenty runs
(ten applications x both schemas). v2 matches or exceeds v1 on every
structural metric; the two remaining negative deltas are v1 counting bugs
(anonymous-class fields reported as method locals, nested initializer blocks
counted twice) that v2 does not reproduce.
* feat(cli): incremental L1 cache keyed on content_hash (-c/--cache-dir, --eager)
Reuses modules whose files are byte-for-byte unchanged, skipping the parse
as well as the build: commons-lang goes from 130s cold to 4s warm. Caching
is opt-in, and the cache is discarded wholesale when the app name or
analyzer version changes since both are baked into every can:// id.
The extractor now enumerates and hashes files itself instead of parsing
whole source roots up front; module discovery is unchanged (commons-lang
still yields 625 modules, matching v1).
* fix(schema): six correctness fixes from code review
- Constructor callee_signature normalises to <init> so it joins against the
target callable's signature; otherwise L2 drops every constructor edge
(88 of petclinic's call sites).
- Expression-type resolution no longer memoises failures by expression text:
the same text can denote different types in different scopes of one file,
so a failure blanked later resolvable occurrences.
- metrics.cyclomatic is scope-filtered like every other callable fact, so
branches inside a nested or anonymous class are no longer double-counted.
- Call sites with no source range are skipped rather than colliding on 0:0
and silently overwriting one another.
- Module span end position is computed for universal newlines and for files
with no trailing newline.
- Corrected the byteOffsets javadoc: the range is end-exclusive.
* fix(schema): emit dropped constructs; restore v1 call-site facts
Constructs L1 was silently dropping:
- compact constructors (CompactConstructorDeclaration is not a
CallableDeclaration; signature comes from the record components)
- enum-constant class bodies, as $enum$<NAME> types whose overrides are
their own callables
- nested anonymous classes in field initializers were double-emitted
- partial parses are now announced instead of accepted silently
Lapsed v1 parity on call sites: method_name, return_type, comment, and
accessibility as a single enum replacing v1's four booleans (which
conflated unknown with package-private). Initializer error_channel now
reaches throws nested inside catch blocks.
Also: per-parameter signature degradation instead of all-or-nothing,
drop the redundant code_start_line, fix stale javadocs.
* feat(schema): model type parameters on generic types and callables
A generic signature was not reconstructable from the emitted facts: a
parameter declared `T` resolves to the bare spelling `T` (a type variable
has no qualified name), and `declaration` omits the clause because
JavaParser's getDeclarationAsString does. So `type: "T"` appeared with no
record of what T ranges over.
type_parameters on `type` and `callable` carries the name, resolved bounds,
span and annotations, in declaration order (a type argument binds by
position). An unbounded parameter gets no bounds rather than a fabricated
java.lang.Object. Genericity is keyed off NodeWithTypeParameters, so enums,
annotation types, anonymous classes and enum-constant bodies are excluded
by the language rule rather than an instanceof chain.
Purely additive: signatures, ids and call-site joins are untouched.
* fix(schema): fully erase signatures; synthesize implicit constructors
Erasure was one level deep, so a type variable's bound kept its own type
arguments: `<T extends Consumer<?>> copy(T...)` was keyed
`copy(java.util.function.Consumer<?>[])`, and `<T extends Comparable<T>>`
leaked `T` into a durable name. Iterate to a fixpoint. Also repairs v1,
whose WALA join compares these keys against fully-erased ASM descriptors.
55 of 4688 callables change across the fixtures, all in commons-lang.
L1 now emits the constructors the language guarantees but the source
never writes, so a `new Foo()` site names a callable instead of a
dangling id: classes and enums declaring none, a record's canonical
constructor, and an anonymous class's generated one. Identity only.
Enum constant bodies are excluded: which constructor a constant invokes
is not recoverable, and a constant is not a call site.1 parent e2cc4e3 commit b3b46bd
70 files changed
Lines changed: 6106 additions & 40 deletions
File tree
- .claude
- docs/design/notes
- src
- main/java/com/ibm/cldk
- schema
- syntactic_analysis
- test
- java/com/ibm/cldk
- schema
- syntactic_analysis
- resources
- schema
- test-applications
- enum-record-bodies-test/src/main/java/org/example
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
196 | 196 | | |
197 | 197 | | |
198 | 198 | | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
142 | 142 | | |
143 | 143 | | |
144 | 144 | | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
145 | 148 | | |
146 | 149 | | |
147 | 150 | | |
148 | 151 | | |
149 | 152 | | |
150 | 153 | | |
151 | 154 | | |
152 | | - | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
153 | 160 | | |
154 | 161 | | |
155 | 162 | | |
156 | 163 | | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
157 | 181 | | |
158 | 182 | | |
159 | | - | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
160 | 187 | | |
161 | 188 | | |
162 | 189 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
0 commit comments