`) is an intrinsic element, not a symbol in scope,
- # so it must not bind by name to a same-named function. For a
- # member tag the last segment decides (`
` is not a
- # local `div`). Fragments and namespaced tags (``) are
- # skipped.
+ # so it must not bind by name to a same-named function.
+ # Only bare identifiers are handled. Member tags (``,
+ # ``, ``) are skipped: with a lowercase
+ # receiver the member path falls back to the bare property name and
+ # binds to an unrelated local function. Fragments and namespaced
+ # tags (``) have no identifier name either.
func_node = node.child_by_field_name("name")
- tag = func_node
- if tag is not None and tag.type == "member_expression":
- tag = tag.child_by_field_name("property")
if (func_node is None
- or func_node.type not in ("identifier", "member_expression")
- or tag is None
- or _read_text(tag, source)[:1].islower()):
+ or func_node.type != "identifier"
+ or _read_text(func_node, source)[:1].islower()):
func_node = None
if func_node:
if func_node.type == "identifier":
diff --git a/tests/test_jsx_component_usage_calls.py b/tests/test_jsx_component_usage_calls.py
index a6c563ba2..9d8083c28 100644
--- a/tests/test_jsx_component_usage_calls.py
+++ b/tests/test_jsx_component_usage_calls.py
@@ -5,8 +5,9 @@ A JSX element renders its component, but tree-sitter models it as
field, not as a `call_expression`. Those node types were never in `call_types`,
so a component that is only ever rendered (the normal case in React) had no
incoming edge and looked unused. A lowercase bare tag (``) is an intrinsic
-DOM element and must not bind to a same-named function; for a member tag the last
-segment decides (`
`).
+DOM element and must not bind to a same-named function. Member tags
+(``, ``) are not handled: resolving them by the property
+name binds to unrelated local functions.
"""
from __future__ import annotations
@@ -120,3 +121,21 @@ def test_member_tag_does_not_bind_to_local_function_by_last_segment(tmp_path: Pa
),
})
assert ("App()", "Provider()") not in calls
+
+
+
+def test_member_tag_with_lowercase_receiver_does_not_bind_to_local_function(tmp_path: Path):
+ # `` / `` are members, not the local Close() / Comp().
+ calls, _ = _calls(tmp_path, {
+ "icons.tsx": "export function X() { return null; }\n",
+ "app.tsx": (
+ "import * as icons from './icons';\n"
+ "function Close() { return null; }\n"
+ "function Comp() { return null; }\n"
+ "export function App(props: any) {\n"
+ " return ;\n"
+ "}\n"
+ ),
+ })
+ assert ("App()", "Close()") not in calls
+ assert ("App()", "Comp()") not in calls