@abaplint/core
Version:
abaplint - Core API
164 lines (162 loc) • 5.91 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CyclicOO = exports.CyclicOOConf = void 0;
const issue_1 = require("../issue");
const _basic_rule_config_1 = require("./_basic_rule_config");
const objects_1 = require("../objects");
const syntax_1 = require("../abap/5_syntax/syntax");
const _reference_1 = require("../abap/5_syntax/_reference");
const _builtin_1 = require("../abap/5_syntax/_builtin");
const _abap_object_1 = require("../objects/_abap_object");
class CyclicOOConf extends _basic_rule_config_1.BasicRuleConfig {
constructor() {
super(...arguments);
/** List of object names to skip, must be full upper case name
* @uniqueItems true
*/
this.skip = [];
/** Skips shared memory enabled classes */
this.skipSharedMemory = true;
/** Skip testclass inclues */
this.skipTestclasses = true;
}
}
exports.CyclicOOConf = CyclicOOConf;
class CyclicOO {
constructor() {
this.conf = new CyclicOOConf();
this.edges = {};
}
getMetadata() {
return {
key: "cyclic_oo",
title: "Cyclic OO",
shortDescription: `Finds cyclic/circular OO references`,
extendedInformation: `Runs for global INTF + CLAS objects
Objects must be without syntax errors for this rule to take effect
References in testclass includes are ignored`,
};
}
getConfig() {
return this.conf;
}
setConfig(conf) {
this.conf = conf;
if (this.conf.skip === undefined) {
this.conf.skip = [];
}
}
initialize(reg) {
var _a;
this.reg = reg;
this.edges = {};
for (const obj of this.reg.getObjectsByType("CLAS")) {
if (this.reg.isDependency(obj)) {
continue;
}
const name = obj.getName().toUpperCase();
if (!(obj instanceof objects_1.Class)) {
continue;
}
else if (this.conf.skip.indexOf(name) >= 0) {
continue;
}
else if (this.conf.skipSharedMemory === true && ((_a = obj.getClassDefinition()) === null || _a === void 0 ? void 0 : _a.isSharedMemory) === true) {
continue;
}
const run = new syntax_1.SyntaxLogic(this.reg, obj).run();
if (run.issues.length > 0) {
continue;
}
this.buildEdges(name, run.spaghetti.getTop());
}
for (const obj of this.reg.getObjectsByType("INTF")) {
if (this.reg.isDependency(obj)) {
continue;
}
const name = obj.getName().toUpperCase();
if (!(obj instanceof _abap_object_1.ABAPObject)) {
continue;
}
else if (this.conf.skip.indexOf(name) >= 0) {
continue;
}
const run = new syntax_1.SyntaxLogic(this.reg, obj).run();
if (run.issues.length > 0) {
continue;
}
this.buildEdges(name, run.spaghetti.getTop());
}
return this;
}
run(obj) {
if (!(obj instanceof objects_1.Interface) && !(obj instanceof objects_1.Class)) {
return [];
}
const id = obj.getIdentifier();
if (id === undefined) {
return [];
}
const previous = {};
previous[obj.getName()] = true;
const path = this.findCycle(obj.getName(), obj.getName(), previous);
if (path) {
const message = "Cyclic definition/usage: " + obj.getName() + " -> " + path;
return [issue_1.Issue.atIdentifier(id, message, this.getMetadata().key, this.conf.severity)];
}
return [];
}
/////////////////////////////
findCycle(source, current, previous) {
if (this.edges[current] === undefined) {
return undefined;
}
for (const e of this.edges[current]) {
if (e === source) {
return e;
}
if (previous[e] === undefined) { // dont revisit vertices
previous[e] = true;
const found = this.findCycle(source, e, previous);
if (found) {
return e + " -> " + found;
}
}
}
return undefined;
}
buildEdges(from, node) {
var _a;
for (const r of node.getData().references) {
if (r.resolved === undefined
|| node.getIdentifier().filename === r.resolved.getFilename()
|| r.resolved.getFilename() === _builtin_1.BuiltIn.filename) {
continue;
}
if (this.conf.skipTestclasses === true
&& (r.position.getFilename().includes(".testclasses.")
|| r.resolved.getFilename().includes(".testclasses."))) {
continue;
}
if (r.referenceType === _reference_1.ReferenceType.ObjectOrientedReference
&& ((_a = r.extra) === null || _a === void 0 ? void 0 : _a.ooName)) {
if (this.edges[from] === undefined) {
this.edges[from] = [];
}
const name = r.extra.ooName.toUpperCase();
if (name !== from && this.edges[from].indexOf(name) < 0) {
const obj = this.reg.getObject("INTF", name) || this.reg.getObject("CLAS", name);
if (obj && this.reg.isDependency(obj)) {
continue;
}
this.edges[from].push(name);
}
}
}
for (const c of node.getChildren()) {
this.buildEdges(from, c);
}
}
}
exports.CyclicOO = CyclicOO;
//# sourceMappingURL=cyclic_oo.js.map