| 455 | } |
| 456 | |
| 457 | static void build_def_props(char *buf, size_t bufsize, const CBMDefinition *def) { |
| 458 | /* Complexity/loop/recursion metrics are meaningful only for Function/Method. |
| 459 | * Gate the block so the millions of Macro/Field/Variable/Class/Enum nodes |
| 460 | * keep a lean properties blob (lossless — those fields are always zero for |
| 461 | * non-functions). Cuts RAM, gbuf-merge copy and dump volume. Mirrors |
| 462 | * pass_definitions.c::build_def_props — keep both in sync. */ |
| 463 | const bool is_fn = |
| 464 | def->label && (strcmp(def->label, "Function") == 0 || strcmp(def->label, "Method") == 0); |
| 465 | int n; |
| 466 | if (is_fn) { |
| 467 | n = snprintf(buf, bufsize, |
| 468 | "{\"complexity\":%d,\"cognitive\":%d,\"loop_count\":%d,\"loop_depth\":%d," |
| 469 | "\"self_recursive\":%s,\"param_count\":%d,\"max_access_depth\":%d," |
| 470 | "\"linear_scan_in_loop\":%d,\"alloc_in_loop\":%d,\"recursion_in_loop\":%s," |
| 471 | "\"unguarded_recursion\":%s," |
| 472 | "\"lines\":%d,\"is_exported\":%s,\"is_test\":%s,\"is_entry_point\":%s", |
| 473 | def->complexity, def->cognitive, def->loop_count, def->loop_depth, |
| 474 | def->is_recursive ? "true" : "false", def->param_count, def->max_access_depth, |
| 475 | def->linear_scan_in_loop, def->alloc_in_loop, |
| 476 | def->recursion_in_loop ? "true" : "false", |
| 477 | def->unguarded_recursion ? "true" : "false", def->lines, |
| 478 | def->is_exported ? "true" : "false", def->is_test ? "true" : "false", |
| 479 | def->is_entry_point ? "true" : "false"); |
| 480 | } else { |
| 481 | n = snprintf(buf, bufsize, |
| 482 | "{\"complexity\":%d,\"lines\":%d,\"is_exported\":%s,\"is_test\":%s," |
| 483 | "\"is_entry_point\":%s", |
| 484 | def->complexity, def->lines, def->is_exported ? "true" : "false", |
| 485 | def->is_test ? "true" : "false", def->is_entry_point ? "true" : "false"); |
| 486 | } |
| 487 | if (n <= 0 || (size_t)n >= bufsize) { |
| 488 | buf[0] = '\0'; |
| 489 | return; |
| 490 | } |
| 491 | size_t pos = (size_t)n; |
| 492 | append_json_string(buf, bufsize, &pos, "docstring", def->docstring); |
| 493 | append_json_string(buf, bufsize, &pos, "signature", def->signature); |
| 494 | append_json_string(buf, bufsize, &pos, "return_type", def->return_type); |
| 495 | append_json_string(buf, bufsize, &pos, "parent_class", def->parent_class); |
| 496 | append_json_str_array(buf, bufsize, &pos, "decorators", def->decorators); |
| 497 | append_json_str_array(buf, bufsize, &pos, "base_classes", def->base_classes); |
| 498 | append_json_str_array(buf, bufsize, &pos, "param_names", def->param_names); |
| 499 | append_json_str_array(buf, bufsize, &pos, "param_types", def->param_types); |
| 500 | append_json_string(buf, bufsize, &pos, "route_path", def->route_path); |
| 501 | append_json_string(buf, bufsize, &pos, "route_method", def->route_method); |
| 502 | |
| 503 | /* MinHash fingerprint — append if present and buffer has room. |
| 504 | * Hex-encoded K=64 uint32 = 512 chars + key/quotes ≈ 520 chars. */ |
| 505 | if (def->fingerprint && def->fingerprint_k > 0 && |
| 506 | pos + CBM_MINHASH_HEX_LEN + CBM_MINHASH_JSON_OVERHEAD < bufsize) { |
| 507 | char fp_hex[CBM_MINHASH_HEX_BUF]; |
| 508 | cbm_minhash_to_hex((const cbm_minhash_t *)def->fingerprint, fp_hex, sizeof(fp_hex)); |
| 509 | append_json_string(buf, bufsize, &pos, "fp", fp_hex); |
| 510 | } |
| 511 | |
| 512 | /* AST structural profile — append if present and buffer has room. */ |
| 513 | if (def->structural_profile && pos + CBM_AST_PROFILE_BUF < bufsize) { |
| 514 | append_json_string(buf, bufsize, &pos, "sp", def->structural_profile); |
no test coverage detected