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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/main/java/com/ibm/cldk/neo4j/V2GraphProjector.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,14 @@ public static GraphRows project(Analysis analysis, String appName) {
for (JIdEdge e : analysis.getApplication().getParamIn()) {
b.edgeIfBothResolved("J_PARAM_IN",
new NodeRef("JBodyNode", "id", e.getSrc()),
new NodeRef("JBodyNode", "id", e.getDst()), RowBuilder.props());
new NodeRef("JBodyNode", "id", e.getDst()), paramEdgeProps(e));
}
}
if (analysis.getApplication().getParamOut() != null) {
for (JIdEdge e : analysis.getApplication().getParamOut()) {
b.edgeIfBothResolved("J_PARAM_OUT",
new NodeRef("JBodyNode", "id", e.getSrc()),
new NodeRef("JBodyNode", "id", e.getDst()), RowBuilder.props());
new NodeRef("JBodyNode", "id", e.getDst()), paramEdgeProps(e));
}
}

Expand Down Expand Up @@ -731,4 +731,14 @@ private static Map<String, Object> mapOf(String k, Object v) {
m.put(k, v);
return m;
}

/**
* {@code var} on a param edge: the callee-side formal's variable, which the catalog declares
* and every {@code param_in}/{@code param_out} edge carries (codeanalyzer-python#195).
*/
private static Map<String, Object> paramEdgeProps(JIdEdge e) {
Map<String, Object> p = RowBuilder.props();
p.put("var", e.getVar());
return RowBuilder.prune(p);
}
}
11 changes: 9 additions & 2 deletions src/main/java/com/ibm/cldk/schema/JIdEdge.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@
import lombok.Data;

/**
* An id-to-id edge with no payload — the shape of {@code param_in}/{@code param_out} (application
* scope, global-ordinal endpoints) and {@code summary} (callable scope, local endpoints).
* An id-to-id edge — the shape of {@code param_in}/{@code param_out} (application scope,
* global-ordinal endpoints) and {@code summary} (callable scope, local endpoints).
*
* <p>{@code var} is the callee-side formal's variable on a {@code param_in}/{@code param_out}
* edge — the parameter name, or {@code $ret} for the return port — set on every such edge. The
* Neo4j catalog declared it on {@code J_PARAM_IN}/{@code J_PARAM_OUT} while nothing wrote it, so
* a Cypher predicate on {@code r.var} went three-valued across every call boundary
* (codeanalyzer-python#195). Absent (never null-serialized) on {@code summary} edges.
*/
@Data
public class JIdEdge {
private String src;
private String dst;
private String var;
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,14 @@ private static void addActualsAndEdges(
for (int i = 0; i < nArgs; i++) {
int formalIndex = Math.min(i, calleeParams - 1);
paramIn.add(edge(
global(c, local + "/actual_in:" + i), global(callee, "@formal_in:" + formalIndex)));
global(c, local + "/actual_in:" + i), global(callee, "@formal_in:" + formalIndex),
callee.getParameters().get(formalIndex).getName()));
}
}
// Recomputed rather than read off callee.getBody(): traversal order is not call order, so
// the callee's own addFormals may not have run yet when the caller's site is processed.
if (hasActualOut && returnsValue(callee)) {
paramOut.add(edge(global(callee, "@formal_out"), global(c, local + "/actual_out")));
paramOut.add(edge(global(callee, "@formal_out"), global(c, local + "/actual_out"), "$ret"));
}
}
}
Expand All @@ -143,6 +144,13 @@ private static JBodyNode vertex(String kind, String of, String parent) {
return n;
}

/** A param edge names the callee-side formal it binds: the parameter, or {@code $ret}. */
private static JIdEdge edge(String src, String dst, String var) {
JIdEdge e = edge(src, dst);
e.setVar(var);
return e;
}

private static JIdEdge edge(String src, String dst) {
JIdEdge e = new JIdEdge();
e.setSrc(src);
Expand Down
25 changes: 25 additions & 0 deletions src/test/java/com/ibm/cldk/neo4j/V2Neo4jSchemaConformanceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -165,6 +166,30 @@ public void everyEmittedNodeLabelAndPropertyIsDeclared() {
}
}

/**
* codeanalyzer-python#195, same defect here: the catalog declared {@code var} on
* J_PARAM_IN/J_PARAM_OUT and the projection wrote no properties, so a predicate on
* {@code r.var} went three-valued across every call boundary. Every emitted edge of those
* types carries every declared property — on all of them, not some.
*/
@Test
public void paramEdgesCarryEveryDeclaredProperty() {
for (String rel : Arrays.asList("J_PARAM_IN", "J_PARAM_OUT")) {
int seen = 0;
for (EdgeRow e : rows.edges) {
if (!e.type.equals(rel)) {
continue;
}
seen++;
for (String key : REL_BY_TYPE.get(rel).properties.keySet()) {
assertTrue(e.props.containsKey(key) && e.props.get(key) != null,
rel + " " + e.from.value + " -> " + e.to.value + " lacks " + key);
}
}
assertTrue(seen > 0, "precondition: the L4 fixture must emit " + rel);
}
}

@Test
public void everyEmittedRelationshipIsDeclared() {
assertTrue(rows.edges.size() > 0, "fixture produced no edges");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,11 @@ void buildsFormalActualVerticesAndParamEdges() {
JIdEdge in = r.paramIn.get(0);
assertEquals(CanId.ordinalId(A_ID, "3:16/actual_in:0"), in.getSrc());
assertEquals(CanId.ordinalId(B_ID, "formal_in:0"), in.getDst());
assertEquals("y", in.getVar(), "param_in names the callee formal it binds (#195)");
assertEquals(1, r.paramOut.size());
assertEquals(CanId.ordinalId(B_ID, "formal_out"), r.paramOut.get(0).getSrc());
assertEquals(CanId.ordinalId(A_ID, "3:16/actual_out"), r.paramOut.get(0).getDst());
assertEquals("$ret", r.paramOut.get(0).getVar(), "param_out names the return port");
}

@Test
Expand Down