Import subtrees are excluded from ordinary usage emission, but their local * names still participate in lexical lookup. Keep this classifier narrow: it * identifies only the binding side, never a module path or imported source * name. */
| 1942 | * identifies only the binding side, never a module path or imported source |
| 1943 | * name. */ |
| 1944 | static bool is_import_binding_occurrence(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec *spec) { |
| 1945 | TSNode boundary = nearest_import_ancestor(node, spec); |
| 1946 | if (ts_node_is_null(boundary)) { |
| 1947 | return false; |
| 1948 | } |
| 1949 | /* Rust's extern-crate alias is a direct field of the import boundary, |
| 1950 | * unlike the nested alias containers used by Python and ES imports. */ |
| 1951 | if (field_contains_node(boundary, "alias", node)) { |
| 1952 | return true; |
| 1953 | } |
| 1954 | if (import_alias_contains(node, boundary)) { |
| 1955 | return true; |
| 1956 | } |
| 1957 | |
| 1958 | switch (ctx->language) { |
| 1959 | case CBM_LANG_PYTHON: { |
| 1960 | const char *boundary_kind = ts_node_type(boundary); |
| 1961 | if (strcmp(boundary_kind, "future_import_statement") == 0) { |
| 1962 | return false; |
| 1963 | } |
| 1964 | for (TSNode parent = ts_node_parent(node); |
| 1965 | !ts_node_is_null(parent) && !ts_node_eq(parent, boundary); |
| 1966 | parent = ts_node_parent(parent)) { |
| 1967 | /* In an aliased import, only the alias binds. */ |
| 1968 | if (strcmp(ts_node_type(parent), "aliased_import") == 0) { |
| 1969 | return false; |
| 1970 | } |
| 1971 | } |
| 1972 | bool from_import = strcmp(boundary_kind, "import_from_statement") == 0; |
| 1973 | if (from_import) { |
| 1974 | if (field_contains_node(boundary, "module_name", node) || |
| 1975 | field_contains_node(boundary, "module", node)) { |
| 1976 | return false; |
| 1977 | } |
| 1978 | /* Older Python grammar variants omit the module field. Their first |
| 1979 | * named child is still the source module, never a local binding. */ |
| 1980 | if (ts_node_named_child_count(boundary) > 1 && |
| 1981 | node_contains(ts_node_named_child(boundary, 0), node)) { |
| 1982 | return false; |
| 1983 | } |
| 1984 | return strcmp(ts_node_type(node), "identifier") == 0; |
| 1985 | } |
| 1986 | |
| 1987 | TSNode entry = node; |
| 1988 | TSNode parent = ts_node_parent(entry); |
| 1989 | while (!ts_node_is_null(parent) && !ts_node_eq(parent, boundary)) { |
| 1990 | entry = parent; |
| 1991 | parent = ts_node_parent(parent); |
| 1992 | } |
| 1993 | TSNode first = first_named_leaf(entry); |
| 1994 | return !ts_node_is_null(first) && ts_node_eq(first, node); |
| 1995 | } |
| 1996 | case CBM_LANG_JAVASCRIPT: |
| 1997 | case CBM_LANG_TYPESCRIPT: |
| 1998 | case CBM_LANG_TSX: { |
| 1999 | if (strcmp(ts_node_type(boundary), "import_statement") != 0) { |
| 2000 | return false; |
| 2001 | } |
no test coverage detected