All files / src/rules slow_parameter_passing.ts

98.07% Statements 102/104
88.46% Branches 23/26
100% Functions 8/8
98.07% Lines 102/104

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 1051x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 9365x 9365x 9365x 9365x 9365x 27924x 27924x 27924x 27924x 27924x 27924x 27924x 9365x 9365x 8900x 8900x 9365x 9365x 216x 216x 9365x 9365x 221x 221x 221x 9365x 9365x 280x 280x 280x 51x 51x 229x 229x 229x 229x 280x 45x 45x 35x 35x 32x 32x 3x 35x 2x 2x 2x     2x 2x 2x 35x 45x 229x 229x 229x 9365x 9365x 3x 3x 3x 6x 6x 6x 1x 1x 6x 3x 3x 3x 9365x 9365x 1005x 1005x 1005x 45x 1005x 960x 776x 776x 960x 1005x 1005x 1005x 9365x 9365x  
import {BasicRuleConfig} from "./_basic_rule_config";
import {Issue} from "../issue";
import {IRule, IRuleMetadata, RuleTag} from "./_irule";
import {IObject} from "../objects/_iobject";
import {ABAPObject} from "../objects/_abap_object";
import {IRegistry} from "../_iregistry";
import {SyntaxLogic} from "../abap/5_syntax/syntax";
import {ISpaghettiScopeNode} from "../abap/5_syntax/_spaghetti_scope";
import {ScopeType} from "../abap/5_syntax/_scope_type";
import {IdentifierMeta, TypedIdentifier} from "../abap/types/_typed_identifier";
import {Position} from "../position";
import {ReferenceType} from "../abap/5_syntax/_reference";
 
export class SlowParameterPassingConf extends BasicRuleConfig {
}
 
export class SlowParameterPassing implements IRule {
  private reg: IRegistry;
  private conf = new SlowParameterPassingConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "slow_parameter_passing",
      title: "Slow Parameter Passing",
      shortDescription: `Detects slow pass by value passing for methods where parameter is not changed`,
      tags: [RuleTag.Performance],
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: SlowParameterPassingConf): void {
    this.conf = conf;
  }
 
  public initialize(reg: IRegistry): IRule {
    this.reg = reg;
    return this;
  }
 
  public run(obj: IObject): readonly Issue[] {
    const issues: Issue[] = [];
 
    if (!(obj instanceof ABAPObject)) {
      return [];
    }
 
    const top = new SyntaxLogic(this.reg, obj).run().spaghetti.getTop();
    const methods = this.listMethodNodes(top);
 
    for (const m of methods) {
      const vars = m.getData().vars;
      for (const v in vars) {
        const id = vars[v];
        if (id.getMeta().includes(IdentifierMeta.PassByValue) === false) {
          continue;
        }
        const writes = this.listWritePositions(m, id);
        if (writes.length === 0) {
          const message = "Parameter " + id.getName() + " passed by VALUE but not changed";
 
          if (this.reg.isFileDependency(id.getFilename()) === true) {
            continue;
          }
 
          issues.push(Issue.atIdentifier(id, message, this.getMetadata().key, this.getConfig().severity));
        }
      }
    }
 
    return issues;
  }
 
  private listWritePositions(node: ISpaghettiScopeNode, id: TypedIdentifier): Position[] {
    const ret: Position[] = [];
 
    for (const v of node.getData().references) {
      if (v.referenceType === ReferenceType.DataWriteReference
          && v.resolved?.getFilename() === id.getFilename()
          && v.resolved?.getStart().equals(id.getStart())) {
        ret.push(v.position.getStart());
      }
    }
 
    return ret;
  }
 
  private listMethodNodes(node: ISpaghettiScopeNode): ISpaghettiScopeNode[] {
    const ret: ISpaghettiScopeNode[] = [];
 
    if (node.getIdentifier().stype === ScopeType.Method) {
      ret.push(node);
    } else {
      for (const c of node.getChildren()) {
        ret.push(...this.listMethodNodes(c));
      }
    }
 
    return ret;
  }
 
}