* Extract a class field declaration (e.g. Java field_declaration, C# field_declaration). * Extracts each declarator as a 'field' kind node inside the owning class.
(node: SyntaxNode)
| 2044 | * Extracts each declarator as a 'field' kind node inside the owning class. |
| 2045 | */ |
| 2046 | private extractField(node: SyntaxNode): void { |
| 2047 | if (!this.extractor) return; |
| 2048 | |
| 2049 | const docstring = getPrecedingDocstring(node, this.source); |
| 2050 | const visibility = this.extractor.getVisibility?.(node); |
| 2051 | const isStatic = this.extractor.isStatic?.(node) ?? false; |
| 2052 | |
| 2053 | // A class field that is actually a CONSTANT (Java `static final`, C# `const` |
| 2054 | // / `static readonly`) is extracted as `constant` kind, not `field`, so |
| 2055 | // value-reference edges treat it as a target (the gate accepts |
| 2056 | // constant/variable, not field). Scoped to languages whose `isConst` |
| 2057 | // predicate is field-shaped — other languages' fields stay `field`. |
| 2058 | const fieldKind: NodeKind = |
| 2059 | (this.language === 'java' || this.language === 'csharp') && |
| 2060 | (this.extractor.isConst?.(node) ?? false) |
| 2061 | ? 'constant' |
| 2062 | : 'field'; |
| 2063 | |
| 2064 | // Java field_declaration: "private final String name = value;" → variable_declarator(s) are direct children |
| 2065 | // C# field_declaration: wraps in variable_declaration → variable_declarator(s) |
| 2066 | let declarators = node.namedChildren.filter( |
| 2067 | c => c.type === 'variable_declarator' |
| 2068 | ); |
| 2069 | // C#: look inside variable_declaration wrapper |
| 2070 | if (declarators.length === 0) { |
| 2071 | const varDecl = node.namedChildren.find(c => c.type === 'variable_declaration'); |
| 2072 | if (varDecl) { |
| 2073 | declarators = varDecl.namedChildren.filter(c => c.type === 'variable_declarator'); |
| 2074 | } |
| 2075 | } |
| 2076 | |
| 2077 | // PHP property_declaration: property_element → variable_name → name |
| 2078 | if (declarators.length === 0) { |
| 2079 | const propElements = node.namedChildren.filter(c => c.type === 'property_element'); |
| 2080 | if (propElements.length > 0) { |
| 2081 | // Get type annotation if present (e.g. "string", "int", "?Foo") |
| 2082 | const typeNode = node.namedChildren.find( |
| 2083 | c => c.type !== 'visibility_modifier' && c.type !== 'static_modifier' |
| 2084 | && c.type !== 'readonly_modifier' && c.type !== 'property_element' |
| 2085 | && c.type !== 'var_modifier' |
| 2086 | ); |
| 2087 | const typeText = typeNode ? getNodeText(typeNode, this.source) : undefined; |
| 2088 | |
| 2089 | for (const elem of propElements) { |
| 2090 | const varName = elem.namedChildren.find(c => c.type === 'variable_name'); |
| 2091 | const nameNode = varName?.namedChildren.find(c => c.type === 'name'); |
| 2092 | if (!nameNode) continue; |
| 2093 | const name = getNodeText(nameNode, this.source); |
| 2094 | const signature = typeText ? `${typeText} $${name}` : `$${name}`; |
| 2095 | this.createNode('field', name, elem, { |
| 2096 | docstring, |
| 2097 | signature, |
| 2098 | visibility, |
| 2099 | isStatic, |
| 2100 | }); |
| 2101 | } |
| 2102 | return; |
| 2103 | } |
no test coverage detected