extractCall — the C# branch (tree-sitter.ts:4502) + shared tail.
(&mut self, node: Node<'t>)
| 1007 | |
| 1008 | /// extractCall — the C# branch (tree-sitter.ts:4502) + shared tail. |
| 1009 | fn extract_call(&mut self, node: Node<'t>) { |
| 1010 | if self.stack.is_empty() { |
| 1011 | return; |
| 1012 | } |
| 1013 | let caller = self.top_row(); |
| 1014 | let func = node |
| 1015 | .child_by_field_name("function") |
| 1016 | .or_else(|| node.named_child(0)); |
| 1017 | let Some(func) = func else { return }; |
| 1018 | |
| 1019 | let mut callee_name: String; |
| 1020 | if func.kind() == "member_access_expression" { |
| 1021 | let recv = func.child_by_field_name("expression"); |
| 1022 | let name_node = func.child_by_field_name("name"); |
| 1023 | let method_name = name_node.map(|n| self.text(n)).unwrap_or(""); |
| 1024 | let chained = recv |
| 1025 | .map(|r| r.kind() == "invocation_expression" && !method_name.is_empty()) |
| 1026 | .unwrap_or(false); |
| 1027 | if chained { |
| 1028 | // Chained factory `Foo.Create(args).Bar()` → `Foo.Create().Bar` |
| 1029 | // (inner whitespace stripped, EVERY call-receiver re-encodes — |
| 1030 | // no capitalization gate, unlike kotlin/scala). |
| 1031 | let inner_func = recv.unwrap().child_by_field_name("function"); |
| 1032 | let inner_callee = inner_func.map(|f| strip_js_ws(self.text(f))).unwrap_or_default(); |
| 1033 | callee_name = if inner_callee.is_empty() { |
| 1034 | method_name.to_string() |
| 1035 | } else { |
| 1036 | format!("{inner_callee}().{method_name}") |
| 1037 | }; |
| 1038 | } else { |
| 1039 | // RAW full member-access text: `this.Run`, `base.Method`, |
| 1040 | // `"lit".ToUpper`, multi-line fluent chains with their |
| 1041 | // newlines — no SKIP_RECEIVERS, no literal filter (preserve). |
| 1042 | callee_name = self.text(func).to_string(); |
| 1043 | } |
| 1044 | } else { |
| 1045 | // Bare `Helper()`, generic `Generic<int>` kept verbatim, |
| 1046 | // `nameof(...)` → a calls ref named `nameof`, `?.` chains raw, |
| 1047 | // `(myDel)(x)` → parenthesized text (normalized below). |
| 1048 | callee_name = self.text(func).to_string(); |
| 1049 | } |
| 1050 | |
| 1051 | // Shared parenthesized-conversion normalization — the one shared |
| 1052 | // normalization C# actually hits: `(myDel)(x)` → `myDel`. |
| 1053 | if !callee_name.is_empty() { |
| 1054 | if let Some(c) = util::paren_conversion().captures(&callee_name) { |
| 1055 | callee_name = c[1].to_string(); |
| 1056 | } |
| 1057 | } |
| 1058 | // (template strip + fn-ptr fan-out are c/cpp-gated — not C#.) |
| 1059 | |
| 1060 | if !callee_name.is_empty() { |
| 1061 | self.push_ref_at(caller, &callee_name.clone(), edge_kind_index("calls").unwrap(), node); |
| 1062 | } |
| 1063 | } |
| 1064 | |
| 1065 | fn extract_instantiation(&mut self, node: Node<'t>) { |
| 1066 | if self.stack.is_empty() { |
no test coverage detected