| 1875 | static int parse_narrowing_one(PHPLSPContext *ctx, TSNode cond, php_narrowing_t *out); |
| 1876 | |
| 1877 | static int parse_narrowing_collect(PHPLSPContext *ctx, TSNode cond, php_narrowing_t *out, |
| 1878 | int max_out) { |
| 1879 | if (ts_node_is_null(cond) || max_out <= 0) |
| 1880 | return 0; |
| 1881 | const char *kind = ts_node_type(cond); |
| 1882 | |
| 1883 | /* Parenthesized: unwrap. */ |
| 1884 | if (strcmp(kind, "parenthesized_expression") == 0) { |
| 1885 | uint32_t nc = ts_node_child_count(cond); |
| 1886 | for (uint32_t i = 0; i < nc; i++) { |
| 1887 | TSNode c = ts_node_child(cond, i); |
| 1888 | if (!ts_node_is_null(c) && ts_node_is_named(c)) { |
| 1889 | return parse_narrowing_collect(ctx, c, out, max_out); |
| 1890 | } |
| 1891 | } |
| 1892 | return 0; |
| 1893 | } |
| 1894 | |
| 1895 | /* Conjunction `A && B` (or `A and B`) → recurse into both sides. */ |
| 1896 | if (strcmp(kind, "binary_expression") == 0) { |
| 1897 | TSNode op = ts_node_child_by_field_name(cond, "operator", 8); |
| 1898 | char *opt = !ts_node_is_null(op) ? php_node_text(ctx, op) : NULL; |
| 1899 | if (opt && (strcmp(opt, "&&") == 0 || strcmp(opt, "and") == 0)) { |
| 1900 | TSNode left = ts_node_child_by_field_name(cond, "left", 4); |
| 1901 | TSNode right = ts_node_child_by_field_name(cond, "right", 5); |
| 1902 | int got = parse_narrowing_collect(ctx, left, out, max_out); |
| 1903 | got += parse_narrowing_collect(ctx, right, out + got, max_out - got); |
| 1904 | return got; |
| 1905 | } |
| 1906 | } |
| 1907 | |
| 1908 | /* Single-predicate path. */ |
| 1909 | php_narrowing_t one = {0}; |
| 1910 | if (parse_narrowing_one(ctx, cond, &one)) { |
| 1911 | out[0] = one; |
| 1912 | return 1; |
| 1913 | } |
| 1914 | return 0; |
| 1915 | } |
| 1916 | |
| 1917 | static int parse_narrowing_one(PHPLSPContext *ctx, TSNode cond, php_narrowing_t *out) { |
| 1918 | if (ts_node_is_null(cond)) |
no test coverage detected