This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch edje-vector-intergration
in repository efl.
View the commit online.
commit b2194d02874642904b61fcdb9c4d5127c4c2df60
Author: [email protected] <[email protected]>
AuthorDate: Thu Apr 30 09:29:05 2026 -0600
edje: add cross-description vector diagnostics (Task 7.4)
Emit VEC007 (info) and VEC009 (warning) diagnostics at end-of-compile to
catch type inconsistencies across vector descriptions. VEC007 notes that
Lottie descriptions skip deep introspection (VEC004/009/010 checks).
VEC009 pairs descriptions of the same VECTOR part, comparing named node
types via DFS — warns if a node has different type across descriptions
(e.g. shape vs container).
Consolidate _node_type_str into shared edje_vg_node_type_str (header +
edje_cc_handlers.c) to eliminate duplication between handlers and
diagnostics reporting.
Defer VEC008 (override target unresolvability) — Phase 4.3 VEC002 errors
already subsume this case. Defer VEC010 (parent-path mismatch) — requires
ancestor-stack walker; low priority for Phase 7.
Add tests: VEC007 positive, VEC009 trigger, VEC009 negative control.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
src/bin/edje/edje_cc.h | 4 +
src/bin/edje/edje_cc_handlers.c | 12 +-
src/bin/edje/edje_cc_out.c | 163 +++++++++++++++++++++++++++
src/tests/edje/edje_test_vector_states.c | 186 +++++++++++++++++++++++++++++++
4 files changed, 359 insertions(+), 6 deletions(-)
diff --git a/src/bin/edje/edje_cc.h b/src/bin/edje/edje_cc.h
index ea8a376a76..24e0b6a87d 100644
--- a/src/bin/edje/edje_cc.h
+++ b/src/bin/edje/edje_cc.h
@@ -328,6 +328,10 @@ Eina_List *_edje_cc_vg_all_trees_get(void);
Eina_Hash *_edje_cc_vg_desc_to_tree_get(void);
void _edje_cc_vg_cleanup(void);
+/* Shared node-type label used by VEC diagnostics in both edje_cc_handlers.c
+ * and edje_cc_out.c. Returns "?" for unrecognised values. */
+const char *edje_vg_node_type_str(Edje_Vg_Node_Type type);
+
void script_rewrite(Code *code);
/* global vars */
diff --git a/src/bin/edje/edje_cc_handlers.c b/src/bin/edje/edje_cc_handlers.c
index f80a843e04..79df0860d3 100644
--- a/src/bin/edje/edje_cc_handlers.c
+++ b/src/bin/edje/edje_cc_handlers.c
@@ -661,7 +661,6 @@ static void _vg_inline_block_end(void);
static void _vg_open_override(Edje_Vg_Node_Type type);
static void _vg_close_block(void);
static void st_vg_override_target(void);
-static const char *_node_type_str(Edje_Vg_Node_Type t);
static void _vg_finalize_description_overrides(void);
static void ob_collections_group_programs_program(void);
@@ -2039,9 +2038,10 @@ _vg_close_block(void)
}
}
-/* Task 4.3: Map a node type to a printable string for VEC002 diagnostics. */
-static const char *
-_node_type_str(Edje_Vg_Node_Type t)
+/* Map a node type to a printable string for VEC diagnostics.
+ * Shared across edje_cc_handlers.c and edje_cc_out.c (declared in edje_cc.h). */
+const char *
+edje_vg_node_type_str(Edje_Vg_Node_Type t)
{
switch (t)
{
@@ -2093,8 +2093,8 @@ _vg_finalize_description_overrides(void)
error_and_abort(NULL,
"vector override target '%s' is type %s but override expects %s (VEC002)",
ovr->target_name,
- _node_type_str(node->type),
- _node_type_str(ovr->expected_type));
+ edje_vg_node_type_str(node->type),
+ edje_vg_node_type_str(ovr->expected_type));
}
}
diff --git a/src/bin/edje/edje_cc_out.c b/src/bin/edje/edje_cc_out.c
index 0396d821ad..3945e3d586 100644
--- a/src/bin/edje/edje_cc_out.c
+++ b/src/bin/edje/edje_cc_out.c
@@ -3022,6 +3022,166 @@ data_write_color_class_register(Eet_File *ef)
free(cc_info);
}
+/* =========================================================================
+ * Task 7.4: Cross-description vector diagnostics (VEC007, VEC009)
+ *
+ * VEC007 (INFO) — Lottie-source description; topology checks skipped.
+ * VEC009 (WARN) — same-named nodes have different types across descriptions.
+ * VEC010 — deferred: parent-path mismatch; rare in practice, helper
+ * to compute parent-path strings is non-trivial. Noted for
+ * Phase 8 polish.
+ * VEC008 — deferred: subsumed by Phase 4.3's VEC002 error.
+ * ========================================================================= */
+
+
+/* Recursive walk over tree A's nodes; for each named node also present in
+ * tree B, check whether the types match. Emit VEC009 on mismatch. */
+static void
+_vec009_walk(const Edje_Vg_Node *na, const Edje_Vg_Tree *tb,
+ const char *part_name,
+ const char *state_a, const char *state_b)
+{
+ if (!na) return;
+
+ if (na->name)
+ {
+ Edje_Vg_Node *nb = _edje_vg_tree_node_by_name(tb, na->name);
+ if (nb && nb->type != na->type)
+ {
+ edje_diag_emit("VEC009", EDJE_DIAG_WARN, NULL, 0,
+ part_name, NULL,
+ "node \"%s\" has different types across descriptions "
+ "\"%s\" (%s) and \"%s\" (%s) — transition will step "
+ "instead of interpolating",
+ na->name,
+ state_a, edje_vg_node_type_str(na->type),
+ state_b, edje_vg_node_type_str(nb->type));
+ }
+ }
+
+ if (na->type == EDJE_VG_NODE_CONTAINER)
+ {
+ Eina_List *l;
+ Edje_Vg_Node *child;
+ EINA_LIST_FOREACH(na->container.children, l, child)
+ _vec009_walk(child, tb, part_name, state_a, state_b);
+ }
+}
+
+/* Check one pair of descriptions for VEC009 type mismatches.
+ * tree_a/tree_b are the base trees (overrides not applied — documented
+ * simplification: applying overrides at compile time would require
+ * duplication; base-tree comparison is the pragmatic v1 approach). */
+static void
+_vec009_check_pair(const Edje_Vg_Tree *tree_a, const Edje_Vg_Tree *tree_b,
+ const char *part_name,
+ const char *state_a, const char *state_b)
+{
+ /* Walk A→B only: type mismatch is symmetric, so one direction catches
+ * all pairs where the name exists in both trees. */
+ _vec009_walk(tree_a->root, tree_b, part_name, state_a, state_b);
+}
+
+/* Validate all VECTOR descriptions in a single part. */
+static void
+_vec_check_part_descriptions(const Edje_Part_Collection *pc,
+ const Edje_Part *ep)
+{
+ unsigned int i, j;
+ char state_a[128], state_b[128];
+
+ /* Collect pointers to all descriptions (default + other[]). */
+ unsigned int n = 1 + ep->other.desc_count;
+ Edje_Part_Description_Vector **descs =
+ alloca(n * sizeof(Edje_Part_Description_Vector *));
+
+ descs[0] = (Edje_Part_Description_Vector *)ep->default_desc;
+ for (i = 0; i < ep->other.desc_count; i++)
+ descs[i + 1] = (Edje_Part_Description_Vector *)ep->other.desc[i];
+
+ /* VEC007: info for every Lottie-source description when the part has
+ * more than one description (single-description parts have no
+ * cross-description concern). */
+ if (n > 1)
+ {
+ for (i = 0; i < n; i++)
+ {
+ const Edje_Part_Description_Vector *d = descs[i];
+ if (d->vg.set && d->vg.type == EDJE_VECTOR_FILE_TYPE_LOTTIE)
+ {
+ snprintf(state_a, sizeof(state_a), "%s %.2f",
+ d->common.state.name ? d->common.state.name : "default",
+ d->common.state.value);
+ edje_diag_emit("VEC007", EDJE_DIAG_INFO, NULL, 0,
+ ep->name, state_a,
+ "Lottie source — topology / type / parent-path "
+ "checks (VEC004/009/010) are skipped for this "
+ "description; use SVG inline trees for full "
+ "cross-description validation");
+ }
+ }
+ }
+
+ /* VEC009: walk all pairs of tree-based descriptions. */
+ if (!edje_file->vector_dir) return;
+
+ for (i = 0; i < n; i++)
+ {
+ const Edje_Part_Description_Vector *da = descs[i];
+ if (da->vg.tree_id < 0) continue;
+ if ((unsigned int)da->vg.tree_id >= edje_file->vector_dir->trees_count)
+ continue;
+
+ snprintf(state_a, sizeof(state_a), "%s %.2f",
+ da->common.state.name ? da->common.state.name : "default",
+ da->common.state.value);
+
+ for (j = i + 1; j < n; j++)
+ {
+ const Edje_Part_Description_Vector *db = descs[j];
+ if (db->vg.tree_id < 0) continue;
+ if ((unsigned int)db->vg.tree_id >= edje_file->vector_dir->trees_count)
+ continue;
+
+ snprintf(state_b, sizeof(state_b), "%s %.2f",
+ db->common.state.name ? db->common.state.name : "default",
+ db->common.state.value);
+
+ const Edje_Vg_Tree *ta =
+ &edje_file->vector_dir->trees[da->vg.tree_id];
+ const Edje_Vg_Tree *tb =
+ &edje_file->vector_dir->trees[db->vg.tree_id];
+
+ _vec009_check_pair(ta, tb, ep->name, state_a, state_b);
+ }
+ }
+
+ (void)pc; /* pc available if future checks need collection context */
+}
+
+/* Orchestrator: walk all collections and all VECTOR parts. Called from
+ * data_write() after data_write_vectors_trees() has patched tree_id fields
+ * into descriptions and edje_file->vector_dir is populated. */
+static void
+data_validate_vectors(void)
+{
+ Edje_Part_Collection *pc;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(edje_collections, l, pc)
+ {
+ unsigned int i;
+ for (i = 0; i < pc->parts_count; i++)
+ {
+ Edje_Part *ep = pc->parts[i];
+ if (ep->type != EDJE_PART_TYPE_VECTOR) continue;
+ /* Skip parts with no non-default descriptions — nothing to compare. */
+ if (!ep->default_desc) continue;
+ _vec_check_part_descriptions(pc, ep);
+ }
+ }
+}
+
void
data_write(void)
{
@@ -3093,6 +3253,9 @@ data_write(void)
data_write_vectors_trees(ef, &tree_num);
INF("Wrote %i vector trees", tree_num);
}
+ /* Task 7.4: cross-description diagnostics — runs after tree_id fields
+ * are patched but before eet_close / vg_cleanup. */
+ data_validate_vectors();
data_write_groups(ef, &collection_num);
INF("groups: %3.5f", ecore_time_get() - t); t = ecore_time_get();
data_write_scripts(ef);
diff --git a/src/tests/edje/edje_test_vector_states.c b/src/tests/edje/edje_test_vector_states.c
index fc25a558a2..a313a9e3f2 100644
--- a/src/tests/edje/edje_test_vector_states.c
+++ b/src/tests/edje/edje_test_vector_states.c
@@ -2081,6 +2081,188 @@ EFL_START_TEST(edje_vg_diag_vec014_inherit_allowed)
}
EFL_END_TEST
+/* =========================================================================
+ * Phase 7.4: VEC007 and VEC009 cross-description diagnostic tests
+ * ========================================================================= */
+
+/* VEC007 — Lottie-source description on a multi-description part: info fires,
+ * compilation succeeds (zero exit).
+ *
+ * The test creates a fake .json file in /tmp (Lottie files are embedded as
+ * raw bytes so no parsing is required), then compiles an EDC with two
+ * descriptions referencing it and checks VEC007 appears in the output. */
+EFL_START_TEST(edje_vg_diag_vec007_lottie_info)
+{
+ /* Create a minimal fake Lottie JSON in /tmp. Lottie files are stored as
+ * raw bytes by edje_cc so their content is irrelevant for compilation. */
+ const char *json_name = "edje_diag_vec007_fake.json";
+ char json_path[PATH_MAX];
+ snprintf(json_path, sizeof(json_path), "/tmp/%s", json_name);
+
+ FILE *jf = fopen(json_path, "w");
+ fail_if(!jf);
+ fputs("{\"v\":\"5.6\",\"fr\":30,\"ip\":0,\"op\":30,\"w\":100,\"h\":100,"
+ "\"nm\":\"test\",\"ddd\":0,\"assets\":[],\"layers\":[]}", jf);
+ fclose(jf);
+
+ static const char edc_tmpl[] =
+ "images { vector: \"edje_diag_vec007_fake.json\"; }\n"
+ "collections {\n"
+ " group { name: \"g\";\n"
+ " parts {\n"
+ " part { name: \"v\"; type: VECTOR;\n"
+ " description { state: \"default\" 0.0;\n"
+ " image { normal: \"edje_diag_vec007_fake.json\"; }\n"
+ " }\n"
+ " description { state: \"alt\" 0.0;\n"
+ " image { normal: \"edje_diag_vec007_fake.json\"; }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ char edc_path[PATH_MAX];
+ snprintf(edc_path, sizeof(edc_path), "/tmp/edje_diag_vec007_%d.edc",
+ (int)getpid());
+ FILE *f = fopen(edc_path, "w");
+ fail_if(!f);
+ fputs(edc_tmpl, f);
+ fclose(f);
+
+ char cmd[PATH_MAX * 4];
+ snprintf(cmd, sizeof(cmd),
+ "EFL_RUN_IN_TREE=1 %s -beta -fd /tmp -id /tmp '%s' /dev/null 2>&1",
+ _EDJE_CC_BIN, edc_path);
+
+ FILE *p = popen(cmd, "r");
+ fail_if(!p);
+
+ char buf[4096];
+ Eina_Bool found = EINA_FALSE;
+ while (fgets(buf, sizeof(buf), p))
+ {
+ if (strstr(buf, "VEC007"))
+ found = EINA_TRUE;
+ }
+
+ int exit_status = pclose(p);
+ unlink(edc_path);
+ unlink(json_path);
+
+ ck_assert_msg(found,
+ "VEC007 info should be emitted for Lottie-source descriptions");
+ /* VEC007 is INFO — compilation must succeed. */
+ ck_assert_msg(exit_status == 0,
+ "Compilation should succeed (VEC007 is INFO severity)");
+}
+EFL_END_TEST
+
+/* VEC009 — two descriptions where the same node name has different types:
+ * warning fires, compilation succeeds (zero exit). */
+EFL_START_TEST(edje_vg_diag_vec009_type_mismatch)
+{
+ /* Desc "default" has a shape named "body"; desc "alt" has a container
+ * named "body". VEC009 must fire as a warning (exit 0). */
+ static const char edc[] =
+ "collections {\n"
+ " vectors {\n"
+ " vector { name: \"t1\";\n"
+ " shape { name: \"body\"; path: \"M0,0 L10,10\";\n"
+ " fill.color: 200 0 0 255; }\n"
+ " }\n"
+ " vector { name: \"t2\";\n"
+ " container { name: \"body\"; }\n"
+ " }\n"
+ " }\n"
+ " group { name: \"g\";\n"
+ " parts {\n"
+ " part { name: \"v\"; type: VECTOR;\n"
+ " description { state: \"default\" 0.0;\n"
+ " vector.use: \"t1\";\n"
+ " }\n"
+ " description { state: \"alt\" 0.0;\n"
+ " vector.use: \"t2\";\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ ck_assert_msg(_compile_with_diag(edc, "VEC009", EINA_FALSE),
+ "VEC009 (node type mismatch across descriptions) should warn "
+ "(zero exit)");
+}
+EFL_END_TEST
+
+/* VEC009 positive control — two descriptions with the same-named node of the
+ * same type: no VEC009 fires, compilation clean (zero exit). */
+EFL_START_TEST(edje_vg_diag_vec009_no_false_positive)
+{
+ /* Both "default" and "alt" have a shape named "body" — types match,
+ * no VEC009 should fire. */
+ static const char edc[] =
+ "collections {\n"
+ " vectors {\n"
+ " vector { name: \"ta\";\n"
+ " shape { name: \"body\"; path: \"M0,0 L10,10\";\n"
+ " fill.color: 200 0 0 255; }\n"
+ " }\n"
+ " vector { name: \"tb\";\n"
+ " shape { name: \"body\"; path: \"M5,5 L15,15\";\n"
+ " fill.color: 0 0 200 255; }\n"
+ " }\n"
+ " }\n"
+ " group { name: \"g\";\n"
+ " parts {\n"
+ " part { name: \"v\"; type: VECTOR;\n"
+ " description { state: \"default\" 0.0;\n"
+ " vector.use: \"ta\";\n"
+ " }\n"
+ " description { state: \"alt\" 0.0;\n"
+ " vector.use: \"tb\";\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ /* Write and compile; expect zero exit and NO VEC009 in output. */
+ char edc_path[PATH_MAX];
+ snprintf(edc_path, sizeof(edc_path),
+ "/tmp/edje_diag_vec009_ok_%d.edc", (int)getpid());
+ FILE *f = fopen(edc_path, "w");
+ fail_if(!f);
+ fputs(edc, f);
+ fclose(f);
+
+ char cmd[PATH_MAX * 2];
+ snprintf(cmd, sizeof(cmd),
+ "EFL_RUN_IN_TREE=1 %s -beta -fd /tmp '%s' /dev/null 2>&1",
+ _EDJE_CC_BIN, edc_path);
+
+ FILE *p = popen(cmd, "r");
+ fail_if(!p);
+
+ char buf[4096];
+ Eina_Bool spurious = EINA_FALSE;
+ while (fgets(buf, sizeof(buf), p))
+ {
+ if (strstr(buf, "VEC009"))
+ spurious = EINA_TRUE;
+ }
+
+ int exit_status = pclose(p);
+ unlink(edc_path);
+
+ ck_assert_msg(exit_status == 0,
+ "VEC009 positive control: compilation should succeed");
+ ck_assert_msg(!spurious,
+ "VEC009 positive control: no VEC009 should fire when node "
+ "types match across descriptions");
+}
+EFL_END_TEST
+
void
edje_test_vector_states(TCase *tc)
{
@@ -2126,4 +2308,8 @@ edje_test_vector_states(TCase *tc)
/* Phase 7.3 VEC014 transform-form mutual exclusion */
tcase_add_test(tc, edje_vg_diag_vec014_transform_conflict);
tcase_add_test(tc, edje_vg_diag_vec014_inherit_allowed);
+ /* Phase 7.4 cross-description diagnostics */
+ tcase_add_test(tc, edje_vg_diag_vec007_lottie_info);
+ tcase_add_test(tc, edje_vg_diag_vec009_type_mismatch);
+ tcase_add_test(tc, edje_vg_diag_vec009_no_false_positive);
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.