UNPKG

@specs-feup/kadabra

Version:

A Java source-to-source compiler written in Typescript

139 lines (114 loc) 3.87 kB
import Query from "@specs-feup/lara/api/weaver/Query.js"; import Collections from "@specs-feup/lara/api/lara/Collections.js"; import BaseDetector from "./BaseDetector.js"; import { Annotation, Call, Class, FileJp, Joinpoint, Method, Reference, Var, } from "../../../../Joinpoints.js"; export default class MemberIgnoringMethodDetector extends BaseDetector { dups: Map<string, Method[]>; results: Method[] = []; constructor() { super("Member Ignoring Method Detector"); this.dups = new Map(); this.computeSameNameMethods(); } static noOverrideAnnotationFilter(annos: Annotation[]) { return annos.filter((a) => a.type === "Override").length === 0; } computeSameNameMethods() { const methods = Query.search(Method, { isStatic: false, isFinal: false, privacy: (p) => p !== "private", annotations: MemberIgnoringMethodDetector.noOverrideAnnotationFilter, }); for (const m of methods) { const dup = this.dups.get(m.name); if (dup !== undefined) { dup.push(m); } else { this.dups.set(m.name, [m]); } } } analyseClass(jpClass: Class) { super.analyseClass(jpClass); const checkOverride = !( jpClass.interfaces.length == 0 && jpClass.superClassJp == undefined ); const mightBeStatic = Query.childrenFrom(jpClass, Method, { isStatic: false, isFinal: false, privacy: (p) => p !== "private", annotations: MemberIgnoringMethodDetector.noOverrideAnnotationFilter, }); for (const m of mightBeStatic) { if (m.body === undefined || m.body.children.length === 0) continue; if (!this.#methodCanBeStatic(m)) continue; if (checkOverride && this.#isOverride(m)) continue; this.results.push(m); } } print() { console.log(`${this.name}:`); const data = this.results.map((r) => [ r.line.toString(), r.name, (r.getAncestor("file") as FileJp).path, ]); Collections.printTable(["Line", "Method", "File"], data, [10, 30, 100]); console.log(); } save() { return this.results.map((r) => { const node = r; let loc = node.name; const jpClass = node.getAncestor("class") as Class; loc = jpClass.name + "/" + loc; const jpFile = jpClass.getAncestor("file") as FileJp; loc = jpFile.name + "/" + loc; return loc + ":" + r.line.toString(); }); } #isOverride(jpMethod: Method) { const dups = this.dups.get(jpMethod.name); if (dups === undefined || dups.length < 2) return false; for (const dup of dups) { if (dup.declarator === jpMethod.declarator) continue; if (dup.isOverriding(jpMethod) || jpMethod.isOverriding(dup)) { return true; } } return false; } #methodCanBeStatic(jp: Joinpoint) { if (jp instanceof Call && !this.#callIsStatic(jp)) { return false; } if (jp instanceof Var && !this.#varIsStatic(jp)) { return false; } for (const child of jp.children) { if (!this.#methodCanBeStatic(child)) return false; } return true; } #callIsStatic(jpCall: Call) { return jpCall.decl.isStatic; } #varIsStatic(jpVar: Var) { const isParameter = Query.childrenFrom(jpVar, Reference, { type: "Parameter" }).get() .length > 0; return isParameter ? true : jpVar.isStatic; } }