Land upstream #1686 (maxmilian + bompus kernel/CG-28 follow-ups) onto current main. tree-sitter-typescript interface members (method_signature / property_signature) were never listed in the TS extractor, so platform .d.ts APIs had no declaration nodes for call edges. Mirrors on the Rust kernel path; keeps CG-28 damping for pure-interface declaration files; filters damped files from the explore RWR seed set. Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
This commit is contained in:
co-authored by
Colby McHenry
parent
8c047342cd
commit
ee83636acb
@@ -41,8 +41,15 @@ export function classifyTsClassMember(node: SyntaxNode): 'method' | 'property' {
|
||||
export const typescriptExtractor: LanguageExtractor = {
|
||||
functionTypes: ['function_declaration', 'generator_function_declaration', 'arrow_function', 'function_expression', 'generator_function'],
|
||||
classTypes: ['class_declaration', 'abstract_class_declaration'],
|
||||
methodTypes: ['method_definition', 'public_field_definition'],
|
||||
// `method_signature` is the interface/type-literal form of a method; without it
|
||||
// an interface's members never enter the graph, so a `.d.ts` platform API has
|
||||
// no declaration node for call sites to attach to (#1638). Java/C# don't need
|
||||
// an equivalent — their grammars reuse `method_declaration`.
|
||||
methodTypes: ['method_definition', 'public_field_definition', 'method_signature'],
|
||||
classifyMethodNode: classifyTsClassMember,
|
||||
// The interface counterpart of `public_field_definition`. It carries no value,
|
||||
// so it is always a property and never needs classifyMethodNode.
|
||||
propertyTypes: ['property_signature'],
|
||||
interfaceTypes: ['interface_declaration'],
|
||||
structTypes: [],
|
||||
enumTypes: ['enum_declaration'],
|
||||
|
||||
@@ -52,6 +52,20 @@ const RTK_HOOK_NAME_RE = /^use[A-Z][A-Za-z0-9]*(?:Query|Mutation)$/;
|
||||
* initialized with one of these is a component, not a constant (#841). */
|
||||
const REACT_COMPONENT_HOCS = new Set(['forwardRef', 'memo', 'React.forwardRef', 'React.memo']);
|
||||
|
||||
/**
|
||||
* Method node types that spell a SIGNATURE — a declaration with no body (#1638).
|
||||
*
|
||||
* They are a method of whatever type declares them and nothing on their own, so
|
||||
* they must not take `extractMethod`'s "no class-like parent, so treat it as a
|
||||
* free function" fallback. The other `methodTypes` can: a `method_definition`
|
||||
* outside a class really is a function. This one appears outside a class only
|
||||
* inside a type literal (`type Handle = { stop(): void }`), whose members
|
||||
* `extractTypeAlias` already extracts and attaches to the alias (#359) — take
|
||||
* the fallback and the file gains a phantom top-level `function stop` beside
|
||||
* the real `Handle::stop`.
|
||||
*/
|
||||
const SIGNATURE_METHOD_NODE_TYPES = new Set(['method_signature']);
|
||||
|
||||
/** Vue store collections whose object-literal members are the symbols an agent
|
||||
* looks for. Extracted as function nodes so `actions`/`mutations`/`getters` are
|
||||
* findable + readable (the foundation under any later dispatch-bridge synth). */
|
||||
@@ -1073,8 +1087,13 @@ export class TreeSitterExtractor {
|
||||
this.extractClass(node);
|
||||
skipChildren = true;
|
||||
}
|
||||
// Check for method declarations (only if not already handled by functionTypes)
|
||||
else if (this.extractor.methodTypes.includes(nodeType)) {
|
||||
// Check for method declarations (only if not already handled by functionTypes).
|
||||
// A bodiless SIGNATURE only counts as one where a type declares it — see
|
||||
// SIGNATURE_METHOD_NODE_TYPES for what falling through would otherwise mint.
|
||||
else if (
|
||||
this.extractor.methodTypes.includes(nodeType)
|
||||
&& (!SIGNATURE_METHOD_NODE_TYPES.has(nodeType) || this.isInsideClassLikeNode())
|
||||
) {
|
||||
// TS/JS class fields parse as a methodTypes node; only function-valued
|
||||
// fields are methods — a plain field (`public fonts: Fonts;`) is a
|
||||
// property (#808). C++ lists `field_declaration` so pure-virtual methods
|
||||
@@ -1335,22 +1354,16 @@ export class TreeSitterExtractor {
|
||||
else if (nodeType === 'impl_item') {
|
||||
this.extractRustImplItem(node);
|
||||
}
|
||||
// TypeScript interface members: property_signature (`foo: T`, `foo?: T`)
|
||||
// and method_signature (`foo(arg: A): R`) both carry type annotations the
|
||||
// interface walker would otherwise drop. Extract them as `references`
|
||||
// edges from the interface so resolvers can wire callers/impact for
|
||||
// types that only appear in interface members.
|
||||
else if (
|
||||
(nodeType === 'property_signature' || nodeType === 'method_signature') &&
|
||||
this.isInsideClassLikeNode() &&
|
||||
this.TYPE_ANNOTATION_LANGUAGES.has(this.language)
|
||||
) {
|
||||
const parentId = this.nodeStack[this.nodeStack.length - 1];
|
||||
if (parentId) {
|
||||
this.extractTypeAnnotations(node, parentId);
|
||||
}
|
||||
// don't skipChildren — nested signatures still need traversal
|
||||
}
|
||||
// NOTE: `property_signature` / `method_signature` used to be handled here,
|
||||
// hanging their type annotations off the ENCLOSING INTERFACE — the only
|
||||
// anchor available while the members themselves went unextracted. Since
|
||||
// #1638 they are in the TS extractor's `methodTypes` / `propertyTypes`, so
|
||||
// the branches above claim them first (under the same `isInsideClassLikeNode`
|
||||
// guard this branch had, so nothing it used to reach is now missed) and this
|
||||
// one was dead. The `references` edges survive — `extractMethod` and
|
||||
// `extractProperty` each call `extractTypeAnnotations` — but now hang off
|
||||
// the member, which is the more precise anchor: `Api::fetch → PageId` says
|
||||
// which member wants the type, where `Api → PageId` only said the file did.
|
||||
|
||||
// Visit children (unless the extract method already visited them)
|
||||
if (!skipChildren) {
|
||||
@@ -2096,8 +2109,18 @@ export class TreeSitterExtractor {
|
||||
// and the initializer VALUE, which the generic finder below would
|
||||
// wrongly pick — so fields use the type field only (#808). Other
|
||||
// languages (C# property_declaration) keep the generic scan.
|
||||
//
|
||||
// A `property_signature` (an interface member, #1638) carries a `type`
|
||||
// field and no value, so it reads the type field too. It cannot take the
|
||||
// generic scan: that scan's exclusion list covers `identifier` but not the
|
||||
// `property_identifier` an interface member is named with, so it stops on
|
||||
// the name and `interface Stats { counts: Record<string, number> }` yields
|
||||
// `signature: "counts counts"` instead of the type. Named explicitly
|
||||
// rather than folded into the field test so no other language's
|
||||
// `property_declaration` moves off the generic scan.
|
||||
const isTsJsField =
|
||||
node.type === 'public_field_definition' || node.type === 'field_definition';
|
||||
node.type === 'public_field_definition' || node.type === 'field_definition'
|
||||
|| node.type === 'property_signature';
|
||||
const typeNode = isTsJsField
|
||||
? getChildByField(node, 'type')
|
||||
: node.namedChildren.find(
|
||||
|
||||
Reference in New Issue
Block a user