@@ -327,13 +327,21 @@ static void calls_emit_edge(cbm_gbuf_t *gbuf, int64_t src, int64_t tgt, const ch
327327
328328static void emit_http_async_edge (cbm_pipeline_ctx_t * ctx , const CBMCall * call ,
329329 const cbm_gbuf_node_t * source , const cbm_gbuf_node_t * target ,
330- const cbm_resolution_t * res , cbm_svc_kind_t svc ) {
330+ const cbm_resolution_t * res , cbm_svc_kind_t svc ,
331+ bool suppress_plain_calls ) {
331332 const char * url_or_topic = call -> first_string_arg ;
332333 bool is_url = (url_or_topic && url_or_topic [0 ] != '\0' &&
333334 (url_or_topic [0 ] == '/' || strstr (url_or_topic , "://" ) != NULL ));
334335 bool is_topic = (url_or_topic && url_or_topic [0 ] != '\0' && svc == CBM_SVC_ASYNC &&
335336 strlen (url_or_topic ) > PAIR_LEN );
336337 if (!is_url && !is_topic ) {
338+ /* No URL/topic → this is not a real service call; the svc kind was a
339+ * substring coincidence in the resolved QN (e.g. "SalesforceRestClient"
340+ * matches the "RestClient" HTTP lib). Emit a plain CALLS edge — unless a
341+ * weak TS/JS member-call match should be suppressed (#592/#606). */
342+ if (suppress_plain_calls ) {
343+ return ;
344+ }
337345 char esc_callee [CBM_SZ_256 ];
338346 cbm_json_escape (esc_callee , sizeof (esc_callee ), call -> callee_name );
339347 char props [CBM_SZ_512 ];
@@ -368,17 +376,22 @@ static void emit_http_async_edge(cbm_pipeline_ctx_t *ctx, const CBMCall *call,
368376}
369377
370378/* Classify a resolved call and emit the appropriate edge. */
379+ /* When suppress_plain_calls is true (a TS/JS/TSX weak short-name member-call
380+ * match, #592/#606), the route/HTTP/ASYNC/CONFIG service classifications below
381+ * still run — only the plain CALLS fall-through is skipped, so a fabricated
382+ * project edge is dropped while every service edge stays main-identical. */
371383static void emit_classified_edge (cbm_pipeline_ctx_t * ctx , const CBMCall * call ,
372384 const cbm_gbuf_node_t * source , const cbm_gbuf_node_t * target ,
373385 const cbm_resolution_t * res , const char * module_qn ,
374- const char * * imp_keys , const char * * imp_vals , int imp_count ) {
386+ const char * * imp_keys , const char * * imp_vals , int imp_count ,
387+ bool suppress_plain_calls ) {
375388 cbm_svc_kind_t svc = cbm_service_pattern_match (res -> qualified_name );
376389 if (svc == CBM_SVC_ROUTE_REG && call -> first_string_arg && call -> first_string_arg [0 ] == '/' ) {
377390 handle_route_registration (ctx , call , source , module_qn , imp_keys , imp_vals , imp_count );
378391 return ;
379392 }
380393 if (svc == CBM_SVC_HTTP || svc == CBM_SVC_ASYNC ) {
381- emit_http_async_edge (ctx , call , source , target , res , svc );
394+ emit_http_async_edge (ctx , call , source , target , res , svc , suppress_plain_calls );
382395 return ;
383396 }
384397 if (svc == CBM_SVC_CONFIG ) {
@@ -393,6 +406,9 @@ static void emit_classified_edge(cbm_pipeline_ctx_t *ctx, const CBMCall *call,
393406 call );
394407 return ;
395408 }
409+ if (suppress_plain_calls ) {
410+ return ; /* weak TS/JS member-call match with an unresolved receiver (#606) */
411+ }
396412 char esc_c2 [CBM_SZ_256 ];
397413 cbm_json_escape (esc_c2 , sizeof (esc_c2 ), call -> callee_name );
398414 char props [CBM_SZ_512 ];
@@ -450,7 +466,7 @@ static int resolve_single_call(cbm_pipeline_ctx_t *ctx, CBMCall *call,
450466 res .strategy = lsp -> strategy ;
451467 res .candidate_count = 1 ;
452468 emit_classified_edge (ctx , call , source_node , target_node , & res , module_qn , imp_keys ,
453- imp_vals , imp_count );
469+ imp_vals , imp_count , false );
454470 return SKIP_ONE ;
455471 }
456472 }
@@ -474,7 +490,7 @@ static int resolve_single_call(cbm_pipeline_ctx_t *ctx, CBMCall *call,
474490 .confidence = PC_SVC_PATTERN_CONF ,
475491 .strategy = "service_pattern" ,
476492 .candidate_count = 0 };
477- emit_http_async_edge (ctx , call , source_node , NULL , & svc_res , csvc );
493+ emit_http_async_edge (ctx , call , source_node , NULL , & svc_res , csvc , false );
478494 return SKIP_ONE ;
479495 }
480496 }
@@ -502,7 +518,7 @@ static int resolve_single_call(cbm_pipeline_ctx_t *ctx, CBMCall *call,
502518 .confidence = PC_SVC_PATTERN_CONF ,
503519 .strategy = "service_pattern" ,
504520 .candidate_count = 0 };
505- emit_http_async_edge (ctx , call , source_node , NULL , & svc_res , esvc );
521+ emit_http_async_edge (ctx , call , source_node , NULL , & svc_res , esvc , false );
506522 return SKIP_ONE ;
507523 }
508524 }
@@ -522,6 +538,21 @@ static int resolve_single_call(cbm_pipeline_ctx_t *ctx, CBMCall *call,
522538 return 0 ;
523539 }
524540
541+ /* TS/JS/TSX weak-method suppression (#592/#606). A member call x.foo() only
542+ * reaches the registry when the TS-LSP could not resolve the receiver type
543+ * (the LSP block above already returned for type-resolved calls, including
544+ * the "resolved but target out of gbuf" fall-through). Binding such a call
545+ * by a weak short-name strategy fabricates an edge (`re.test()` -> a project
546+ * `test`). Rather than drop it here — which would also skip the service
547+ * bypasses below and emit_classified_edge's route/HTTP/CONFIG branches —
548+ * defer to emit_classified_edge and suppress ONLY the plain-CALLS
549+ * fall-through, so every service edge stays main-identical. res.strategy may
550+ * be lsp_* here; the helper's explicit drop-list leaves lsp_* untouched. */
551+ bool is_tsjs =
552+ lang == CBM_LANG_JAVASCRIPT || lang == CBM_LANG_TYPESCRIPT || lang == CBM_LANG_TSX ;
553+ bool tsjs_drop_plain_call =
554+ cbm_tsjs_suppress_weak_method_match (is_tsjs , call -> is_method , res .strategy );
555+
525556 /* Service-pattern HTTP/ASYNC calls to an EXTERNAL client library (e.g.
526557 * `requests.get("/api/orders/{id}")`) resolve to a QN containing the library
527558 * name ("requests"), but that library is not in the indexed tree so
@@ -537,7 +568,7 @@ static int resolve_single_call(cbm_pipeline_ctx_t *ctx, CBMCall *call,
537568 (u [0 ] == '/' || strstr (u , "://" ) != NULL ||
538569 (svc == CBM_SVC_ASYNC && strlen (u ) > PAIR_LEN ));
539570 if (has_url_or_topic ) {
540- emit_http_async_edge (ctx , call , source_node , NULL , & res , svc );
571+ emit_http_async_edge (ctx , call , source_node , NULL , & res , svc , false );
541572 return SKIP_ONE ;
542573 }
543574 }
@@ -547,7 +578,7 @@ static int resolve_single_call(cbm_pipeline_ctx_t *ctx, CBMCall *call,
547578 return 0 ;
548579 }
549580 emit_classified_edge (ctx , call , source_node , target_node , & res , module_qn , imp_keys , imp_vals ,
550- imp_count );
581+ imp_count , tsjs_drop_plain_call );
551582 return SKIP_ONE ;
552583}
553584
0 commit comments