All files / src/rules method_length.ts

98.36% Statements 120/122
97.05% Branches 33/34
100% Functions 10/10
98.36% Lines 120/122

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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 1221x 1x 1x 1x 1x 1x 1x 1x 1x 21022x 21022x 21022x 21022x 21022x 21022x 21022x 21022x 21022x 21022x 1x 1x 1x 1x 1x 1x 10516x 10516x 10516x 10516x 10516x 31361x 31361x 31361x 31361x 31361x 31361x 31361x 31361x 31361x 31361x 10516x 10516x 32x 32x 27x 27x 32x 5x 5x 32x     32x 32x 10516x 10516x 9970x 9970x 10516x 10516x 264x 264x 10516x 10516x 270x 270x 10516x 10516x 336x 336x 336x 336x 1x 1x 335x 335x 335x 335x 335x 336x 333x 333x 333x 335x 335x 335x 10516x 10516x 10516x 10516x 668x 668x 668x 95x 95x 2x 2x 93x 95x 28x 1x 1x 27x 27x 27x 27x 95x 5x 5x 5x 5x 95x 668x 668x 668x 10516x 10516x 28x 28x 28x 10516x 10516x
import {Issue} from "../issue";
import {IObject} from "../objects/_iobject";
import {IRegistry} from "../_iregistry";
import * as Objects from "../objects";
import {IMethodLengthResult, MethodLengthStats} from "../utils/method_length_stats";
import {IRule, IRuleMetadata, RuleTag} from "./_irule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {FormLengthStats} from "../utils/form_length_stats";
 
export class MethodLengthConf extends BasicRuleConfig {
  /** Maximum method/form length in statements. */
  public statements: number = 100;
  /** Checks for empty methods/forms. */
  public errorWhenEmpty: boolean = true;
  /** Option to ignore test classes for this check. */
  public ignoreTestClasses: boolean = false;
  /** Option to check forms. */
  public checkForms: boolean = true;
}
 
enum IssueType {
  EmptyMethod,
  MaxStatements,
}
 
export class MethodLength implements IRule {
 
  private conf = new MethodLengthConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "method_length",
      title: "Method/Form Length",
      shortDescription: `Checks relating to method/form length.`,
      extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#keep-methods-small
 
Abstract methods without statements are considered okay.`,
      tags: [RuleTag.Styleguide, RuleTag.SingleFile],
    };
  }
 
  private getDescription(issueType: IssueType, actual: string, type: string): string {
    switch (issueType) {
      case IssueType.EmptyMethod: {
        return "Empty " + type;
      }
      case IssueType.MaxStatements: {
        return "Reduce " + type + " length to max " + this.conf.statements + " statements, currently " + actual;
      }
      default: {
        return "";
      }
    }
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: MethodLengthConf) {
    this.conf = conf;
  }
 
  public initialize(_reg: IRegistry) {
    return this;
  }
 
  public run(obj: IObject): Issue[] {
 
    if (this.conf.ignoreTestClasses === true
        && obj instanceof Objects.Class
        && obj.getClassDefinition()?.isForTesting === true) {
      return [];
    }
 
    const methodStats = MethodLengthStats.run(obj);
    const methodIssues = this.check(methodStats, "METHOD");
 
    let formIssues: Issue[] = [];
    if (this.conf.checkForms) {
      const formStats = FormLengthStats.run(obj);
      formIssues = this.check(formStats, "FORM");
    }
 
    return methodIssues.concat(formIssues);
  }
 
// ***********************
 
  private check(stats: IMethodLengthResult[], type: string) {
    const issues: Issue[] = [];
 
    for (const s of stats) {
      if ((this.conf.ignoreTestClasses === true)
        && s.file.getFilename().includes(".testclasses.")) {
        continue;
      }
 
      if (s.count === 0 && this.conf.errorWhenEmpty === true) {
        if (this.isAbstract(s)) {
          continue;
        }
        const issue = Issue.atPosition(s.file, s.pos, this.getDescription(IssueType.EmptyMethod, "0", type), this.getMetadata().key, this.conf.severity);
        issues.push(issue);
        continue;
      }
      if (s.count > this.conf.statements) {
        const message = this.getDescription(IssueType.MaxStatements, s.count.toString(), type);
        const issue = Issue.atPosition(s.file, s.pos, message, this.getMetadata().key, this.conf.severity);
        issues.push(issue);
      }
    }
 
    return issues;
  }
 
  private isAbstract(result: IMethodLengthResult): boolean {
    const cdef = result.file.getInfo().getClassDefinitionByName(result.className);
    return cdef?.isAbstract === true;
  }
 
}