mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-22 01:07:18 +08:00
fix(ts): a class extends the class after its type parameters (#2648)
fix(ts): a class extends the class after its type parameters A TS/JS class head was read up to its first `extends`, which for `class EditorPane<MementoType extends object = object> extends Composite<MementoType>` is a type parameter's constraint: EditorPane read as extending `object` (so nothing), and `MultiWindowParts<T extends IMultiWindowPart, …>` as extending `IMultiWindowPart`. Fields, wrapper methods and static methods inherited through such a head were missed or found on the wrong class. The head is now read from its `class` keyword to its body, over several lines, with type parameter lists and type arguments left out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13467,12 +13467,37 @@ function readDeclaredSupertypes(cls: Node, context: ResolutionContext): string[]
|
||||
case 'pascal': return names(/=\s*class\s*\(([^)]*)\)/i.exec(head)?.[1]);
|
||||
case 'python': return names(/\bclass\s+\w+\s*\(([^)]*)\)/.exec(head)?.[1]?.replace(/\w+\s*=\s*[\w.]+/g, ''));
|
||||
case 'ruby': return names(/\bclass\s+[\w:]+\s*<\s*([\w:]+)/.exec(head)?.[1]);
|
||||
case 'php': case 'typescript': case 'tsx': case 'javascript': case 'jsx':
|
||||
return names(/\bextends\s+([\w.\\]+)/.exec(head)?.[1]);
|
||||
case 'php': return names(/\bextends\s+([\w.\\]+)/.exec(head)?.[1]);
|
||||
case 'typescript': case 'tsx': case 'javascript': case 'jsx':
|
||||
return names(/\bextends\s+([\w.\\]+)/.exec(tsClassHead(lines, cls))?.[1]);
|
||||
default: return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A TS/JS class's head, from its `class` keyword to its body, with type
|
||||
* parameter lists and type arguments left out: the first `extends` in
|
||||
* `class EditorPane<M extends object = object> extends Composite<M>` is a
|
||||
* constraint, not the class it extends. Decorators above the keyword are
|
||||
* skipped, and the head may run over several lines.
|
||||
*/
|
||||
function tsClassHead(lines: readonly string[], cls: Node): string {
|
||||
const text = lines.slice(cls.startLine - 1, cls.startLine + 7).join(' ');
|
||||
const named = new RegExp(`\\bclass\\s+${cls.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).exec(text);
|
||||
let head = '';
|
||||
let angles = 0;
|
||||
for (let i = named?.index ?? /\bclass\b/.exec(text)?.index ?? 0; i < text.length; i++) {
|
||||
const c = text[i]!;
|
||||
if (c === '<') angles++;
|
||||
else if (c === '>' && angles > 0 && text[i - 1] !== '=') angles--;
|
||||
else if (angles === 0) {
|
||||
if (c === '{') break;
|
||||
head += c;
|
||||
}
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
/** A method named `name` on a supertype of the given classes, nearest first. */
|
||||
function inheritedClassMethod(classes: Node[], name: string, context: ResolutionContext): Node | null {
|
||||
if (classes.length === 0) return null;
|
||||
|
||||
Reference in New Issue
Block a user