(node: SyntaxNode, source: string, extractor: LanguageExtractor)
| 71 | } |
| 72 | |
| 73 | function extractNameRaw(node: SyntaxNode, source: string, extractor: LanguageExtractor): string { |
| 74 | const hookName = extractor.resolveName?.(node, source); |
| 75 | if (hookName) return hookName; |
| 76 | |
| 77 | // Try field name first |
| 78 | const nameNode = getChildByField(node, extractor.nameField); |
| 79 | if (nameNode) { |
| 80 | // Unwrap pointer_declarator / reference_declarator for C/C++ pointer and |
| 81 | // reference return types (`int* f()`, `int& f()`, `int&& f()`). Without |
| 82 | // unwrapping the reference wrapper an inline reference-returning method is |
| 83 | // named "& f() const" instead of "f" — common in Unreal Engine gameplay |
| 84 | // headers (`const FGameplayTagContainer& GetActiveTags() const`). Out-of-line |
| 85 | // defs (`T& C::f()`) already resolve via the qualified-name hook. A |
| 86 | // pointer_declarator exposes its inner through a `declarator` field; a |
| 87 | // reference_declarator has none, so it's reached via namedChild(0). |
| 88 | let resolved = nameNode; |
| 89 | while (resolved.type === 'pointer_declarator' || resolved.type === 'reference_declarator') { |
| 90 | const inner = getChildByField(resolved, 'declarator') || resolved.namedChild(0); |
| 91 | if (!inner) break; |
| 92 | resolved = inner; |
| 93 | } |
| 94 | // C++ user-defined conversion operator: the declarator is an `operator_cast` |
| 95 | // whose first child is the target type and second is the `() const` tail. Name |
| 96 | // it `operator <type>` (the conventional spelling) rather than the whole |
| 97 | // `operator EALSMovementState() const` declarator, so it matches symbolic |
| 98 | // overloads (`operator+`) and is findable by the type name. |
| 99 | if (resolved.type === 'operator_cast') { |
| 100 | const typeNode = resolved.namedChild(0); |
| 101 | return typeNode ? `operator ${getNodeText(typeNode, source).trim()}` : getNodeText(resolved, source); |
| 102 | } |
| 103 | // Handle complex declarators (C/C++) |
| 104 | if (resolved.type === 'function_declarator' || resolved.type === 'declarator') { |
| 105 | const innerName = getChildByField(resolved, 'declarator') || resolved.namedChild(0); |
| 106 | return innerName ? getNodeText(innerName, source) : getNodeText(resolved, source); |
| 107 | } |
| 108 | // Lua: `function t.f()` / `function t:m()` — the name node is a dot/method |
| 109 | // index expression; the simple name is the trailing field/method (the table |
| 110 | // receiver is captured separately via getReceiverType). |
| 111 | if (resolved.type === 'dot_index_expression') { |
| 112 | const field = getChildByField(resolved, 'field'); |
| 113 | if (field) return getNodeText(field, source); |
| 114 | } |
| 115 | if (resolved.type === 'method_index_expression') { |
| 116 | const method = getChildByField(resolved, 'method'); |
| 117 | if (method) return getNodeText(method, source); |
| 118 | } |
| 119 | return getNodeText(resolved, source); |
| 120 | } |
| 121 | |
| 122 | // For Dart method_signature, look inside inner signature types |
| 123 | if (node.type === 'method_signature') { |
| 124 | for (let i = 0; i < node.namedChildCount; i++) { |
| 125 | const child = node.namedChild(i); |
| 126 | if (child && ( |
| 127 | child.type === 'function_signature' || |
| 128 | child.type === 'getter_signature' || |
| 129 | child.type === 'setter_signature' || |
| 130 | child.type === 'constructor_signature' || |
no test coverage detected