Recover a queue/topic identity from a Go composite-literal input struct, e.g. &sqs.SendMessageInput{QueueUrl: queueUrl, MessageBody: body} sns.PublishInput{TopicArn: "arn:aws:sns:..."} The dispatch target is carried by a struct field (QueueUrl/TopicArn/...), not a bare string arg, so the async edge would otherwise degrade to a plain CALLS. Returns the field's value: the string-literal content when
| 1929 | // Returns the field's value: the string-literal content when present, else the |
| 1930 | // referenced identifier text (which still names the queue/topic for edge formation). |
| 1931 | static const char *extract_composite_queue_field(CBMExtractCtx *ctx, TSNode node) { |
| 1932 | // Unwrap a pointer-of-composite: `&Type{...}` is a unary_expression whose |
| 1933 | // operand is the composite_literal. |
| 1934 | if (strcmp(ts_node_type(node), "unary_expression") == 0) { |
| 1935 | TSNode operand = ts_node_child_by_field_name(node, TS_FIELD("operand")); |
| 1936 | if (ts_node_is_null(operand)) { |
| 1937 | return NULL; |
| 1938 | } |
| 1939 | node = operand; |
| 1940 | } |
| 1941 | if (strcmp(ts_node_type(node), "composite_literal") != 0) { |
| 1942 | return NULL; |
| 1943 | } |
| 1944 | TSNode body = ts_node_child_by_field_name(node, TS_FIELD("body")); |
| 1945 | if (ts_node_is_null(body)) { |
| 1946 | body = cbm_find_child_by_kind(node, "literal_value"); |
| 1947 | } |
| 1948 | if (ts_node_is_null(body)) { |
| 1949 | return NULL; |
| 1950 | } |
| 1951 | uint32_t nc = ts_node_named_child_count(body); |
| 1952 | for (uint32_t i = 0; i < nc; i++) { |
| 1953 | TSNode el = ts_node_named_child(body, i); |
| 1954 | if (strcmp(ts_node_type(el), "keyed_element") != 0) { |
| 1955 | continue; |
| 1956 | } |
| 1957 | // keyed_element children: key then value. Each side may be wrapped in a |
| 1958 | // literal_element; unwrap to the underlying identifier/literal. |
| 1959 | uint32_t ec = ts_node_named_child_count(el); |
| 1960 | if (ec < PAIR_LEN) { |
| 1961 | continue; |
| 1962 | } |
| 1963 | TSNode key_n = ts_node_named_child(el, 0); |
| 1964 | TSNode val_n = ts_node_named_child(el, 1); |
| 1965 | if (strcmp(ts_node_type(key_n), "literal_element") == 0 && |
| 1966 | ts_node_named_child_count(key_n) > 0) { |
| 1967 | key_n = ts_node_named_child(key_n, 0); |
| 1968 | } |
| 1969 | if (strcmp(ts_node_type(val_n), "literal_element") == 0 && |
| 1970 | ts_node_named_child_count(val_n) > 0) { |
| 1971 | val_n = ts_node_named_child(val_n, 0); |
| 1972 | } |
| 1973 | char *key = cbm_node_text(ctx->arena, key_n, ctx->source); |
| 1974 | if (!is_queue_topic_field(key)) { |
| 1975 | continue; |
| 1976 | } |
| 1977 | const char *resolved = extract_string_value(ctx, val_n); |
| 1978 | if (resolved && resolved[0]) { |
| 1979 | return resolved; |
| 1980 | } |
| 1981 | // Value is a variable/expression (no constant value); use its source text |
| 1982 | // as the queue/topic identity so the async edge still forms. |
| 1983 | char *raw = cbm_node_text(ctx->arena, val_n, ctx->source); |
| 1984 | if (raw && raw[0]) { |
| 1985 | return raw; |
| 1986 | } |
| 1987 | } |
| 1988 | return NULL; |
no test coverage detected