ast-transpiler
Version:
README.md
112 lines (89 loc) • 3.35 kB
text/typescript
import * as ts from 'typescript';
const source = `
class main {
f() {
console.log('1')
}
main() {
this.f()
}
}
class second extends main {
f() {
console.log('2')
}
}
`;
const sourceFile = ts.createSourceFile('test.ts', source, ts.ScriptTarget.ESNext, true);
const program = ts.createProgram({ rootNames: ['test.ts'], options: { noEmit: true }, host: {
getSourceFile: (fileName) => (fileName === 'test.ts' ? sourceFile : undefined),
getDefaultLibFileName: () => 'lib.d.ts',
writeFile: () => {},
getCurrentDirectory: () => '',
getDirectories: () => [],
getCanonicalFileName: (fileName) => fileName,
useCaseSensitiveFileNames: () => true,
getNewLine: () => '\n',
fileExists: (fileName) => fileName === 'test.ts',
readFile: () => '',
resolveModuleNames: () => []
}});
const checker = program.getTypeChecker();
function isMethodOverridden(callExpression: ts.CallExpression): boolean {
const symbol = checker.getSymbolAtLocation(callExpression.expression);
if (!symbol) return false;
const declarations = symbol.getDeclarations();
if (!declarations) return false;
for (const declaration of declarations) {
const parentClass = declaration.parent;
if (ts.isClassDeclaration(parentClass) && parentClass.name) {
const parentClassName = parentClass.name.getText();
const parentClassSymbol = checker.getSymbolAtLocation(parentClass.name);
if (parentClassSymbol) {
const parentClassType = checker.getDeclaredTypeOfSymbol(parentClassSymbol);
const derivedClasses = getAllDerivedClasses(parentClassType);
for (const derivedClass of derivedClasses) {
const derivedClassMembers = derivedClass.getProperties();
for (const member of derivedClassMembers) {
if (member.name === symbol.getName() && member.declarations[0].parent.kind === ts.SyntaxKind.ClassDeclaration) {
return true;
}
}
}
}
}
}
return false;
}
function getAllDerivedClasses(baseType: ts.Type): ts.Type[] {
const derivedClasses: ts.Type[] = [];
const visitedClasses = new Set<ts.Type>();
function visitDerivedClasses(type: ts.Type) {
if (visitedClasses.has(type)) {
return;
}
visitedClasses.add(type);
const subtypes = (checker as any).getImmediateDerivedClasses(type);
for (const subtype of subtypes) {
derivedClasses.push(subtype);
visitDerivedClasses(subtype);
}
}
visitDerivedClasses(baseType);
return derivedClasses;
}
function findCallExpressions(node: ts.Node) {
if (ts.isCallExpression(node)) {
const overridden = isMethodOverridden(node);
console.log(`Method ${node.expression.getText()} is ${overridden ? '' : 'not '}overridden.`);
}
ts.forEachChild(node, findCallExpressions);
}
function traverseAst(node: ts.Node) {
if (ts.isClassDeclaration(node)) {
ts.forEachChild(node, findCallExpressions);
} else {
ts.forEachChild(node, traverseAst);
}
}
traverseAst(sourceFile);