All files / src/rules class_attribute_names.ts

97.61% Statements 123/126
93.33% Branches 28/30
100% Functions 9/9
97.61% Lines 123/126

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 122 123 124 125 126 1271x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 22238x 22238x 22238x 22238x 22238x 22238x 22238x 22238x 22238x 22238x 22238x 22238x 22238x 22238x 1x 11136x 11136x 11136x 11136x 11136x 128272x 128272x 128272x 128272x 128272x 128272x 128272x 128272x 128272x 128272x 128272x 128272x 128272x 128272x 128272x 11136x 11136x 40x 40x 40x 40x 11136x 11136x 10568x 10568x 11136x 11136x 292x 292x 11136x 11136x 313x 313x 16x 16x 313x 313x 313x 151x 151x 82x 82x 69x 69x 313x 313x 310x 74x 40x 40x 34x 34x 310x 313x 313x 313x 313x 11136x 11136x 313x     313x 313x 313x 70x 70x 36x 36x 70x 10x 10x 70x 24x 24x 70x   70x 70x 313x 313x 11136x 11136x 70x 70x 70x 70x 40x 40x 40x 70x 70x 70x 11136x 11136x  
import {Issue} from "../issue";
import {NamingRuleConfig} from "./_naming_rule_config";
import {NameValidator} from "../utils/name_validator";
import {ABAPRule} from "./_abap_rule";
import {Identifier} from "../abap/4_file_information/_identifier";
import {InfoAttribute, AttributeLevel} from "../abap/4_file_information/_abap_file_information";
import {RuleTag} from "./_irule";
import {ABAPObject} from "../objects/_abap_object";
import {DDIC} from "../ddic";
import {ABAPFile} from "../abap/abap_file";
 
export class ClassAttributeNamesConf extends NamingRuleConfig {
  /** Ignore global exception classes */
  public ignoreExceptions: boolean = true;
  /** Ignore local classes */
  public ignoreLocal: boolean = true;
  /** Ignore interfaces */
  public ignoreInterfaces: boolean = false;
  /** The pattern for static variable names */
  public statics: string = "^G._.+$";
  /** The pattern for instance variable names */
  public instance: string = "^M._.+$";
  /** The pattern for constant variable names */
  public constants: string = "";
}
 
export class ClassAttributeNames extends ABAPRule {
 
  private conf = new ClassAttributeNamesConf();
 
  public getMetadata() {
    return {
      key: "class_attribute_names",
      title: "Class attributes naming",
      shortDescription: `Allows you to enforce a pattern, such as a prefix, for class variable names.`,
      tags: [RuleTag.Naming, RuleTag.SingleFile],
      badExample: `CLASS zcl_foo DEFINITION PUBLIC.
  PUBLIC SECTION.
    DATA counter TYPE i.
ENDCLASS.`,
      goodExample: `CLASS zcl_foo DEFINITION PUBLIC.
  PUBLIC SECTION.
    DATA mv_counter TYPE i.
ENDCLASS.`,
    };
  }
 
  private getDescription(actual: string, expected: string): string {
    return this.conf.patternKind === "required" ?
      "Class attribute name \"" + actual + "\" does not match pattern " + expected :
      "Class attribute name \"" + actual + "\" must not match pattern " + expected;
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: ClassAttributeNamesConf) {
    this.conf = conf;
  }
 
  public runParsed(file: ABAPFile, obj: ABAPObject): Issue[] {
    let issues: Issue[] = [];
    if (this.conf.patternKind === undefined) {
      this.conf.patternKind = "required";
    }
    let attributes: InfoAttribute[] = [];
    const ddic = new DDIC(this.reg);
    for (const classDef of file.getInfo().listClassDefinitions()) {
      if ((classDef.isLocal && this.conf.ignoreLocal)
        || (ddic.isException(classDef, obj) && this.conf.ignoreExceptions)) {
        continue;
      }
      attributes = attributes.concat(classDef.attributes);
    }
 
    if (this.conf.ignoreInterfaces === false) {
      for (const intfDef of file.getInfo().listInterfaceDefinitions()) {
        if (intfDef.isLocal && this.conf.ignoreLocal) {
          continue;
        }
        attributes = attributes.concat(intfDef.attributes);
      }
    }
 
    issues = this.checkAttributes(attributes);
    return issues;
  }
 
  private checkAttributes(attr: InfoAttribute[] | undefined): Issue[] {
    if (attr === undefined) {
      return [];
    }
 
    let ret: Issue[] = [];
    for (const a of attr) {
      switch (a.level) {
        case AttributeLevel.Instance:
          ret = ret.concat(this.checkName(a.identifier, this.conf.instance));
          break;
        case AttributeLevel.Static:
          ret = ret.concat(this.checkName(a.identifier, this.conf.statics));
          break;
        case AttributeLevel.Constant:
          ret = ret.concat(this.checkName(a.identifier, this.conf.constants));
          break;
        default:
          break;
      }
    }
    return ret;
  }
 
  private checkName(attr: Identifier, expected: string): Issue[] {
    const ret: Issue[] = [];
    const regex = new RegExp(expected, "i");
    const name = attr.getName();
    if (NameValidator.violatesRule(name, regex, this.conf)) {
      const issue = Issue.atIdentifier(attr, this.getDescription(name, expected), this.getMetadata().key, this.conf.severity);
      ret.push(issue);
    }
 
    return ret;
  }
 
}