All files / src/rules local_variable_names.ts

100% Statements 164/164
96.87% Branches 31/32
100% Functions 9/9
100% Lines 164/164

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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 1661x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 21826x 21826x 21826x 21826x 21826x 21826x 21826x 21826x 1x 10923x 10923x 10923x 10923x 10923x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 10923x 10923x 32x 32x 32x 32x 10923x 10923x 10345x 10345x 10923x 10923x 284x 284x 10923x 10923x 300x 300x 2x 2x 300x 300x 300x 12x 12x 288x 288x 300x 60x 60x 300x 53x 53x 300x 3x 3x 288x 288x 288x 10923x 10923x 116x 116x 116x 116x 65x 65x 41x 41x 24x 24x 24x 24x 24x 65x 116x 116x 116x 3x 3x 3x 3x 3x 3x 116x 116x 116x 116x 9x 9x 9x 9x 9x 9x 9x 9x 116x 116x 5x 5x 5x 5x 5x 5x 116x 116x 116x 2x 2x 2x 2x 2x 2x 116x 116x 116x 6x 6x 2x 2x 4x 4x 4x 4x 4x 6x 116x 116x 116x 10923x 10923x 47x 47x 47x 47x 32x 32x 32x 32x 47x 47x 10923x 10923x    
import {Issue} from "../issue";
import {ABAPRule} from "./_abap_rule";
import * as Structures from "../abap/3_structures/structures";
import * as Statements from "../abap/2_statements/statements";
import * as Expressions from "../abap/2_statements/expressions";
import {StructureNode} from "../abap/nodes";
import {AbstractToken} from "../abap/1_lexer/tokens/abstract_token";
import {NamingRuleConfig} from "./_naming_rule_config";
import {NameValidator} from "../utils/name_validator";
import {IRuleMetadata, RuleTag} from "./_irule";
import {ABAPFile} from "../abap/abap_file";
 
export class LocalVariableNamesConf extends NamingRuleConfig {
  /** The pattern for local variable names */
  public expectedData: string = "^L._.+$";
  /** The pattern for local constant names */
  public expectedConstant: string = "^LC_.+$";
  /** The pattern for field symbol names */
  public expectedFS: string = "^<L._.+>$";
}
 
export class LocalVariableNames extends ABAPRule {
 
  private conf = new LocalVariableNamesConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "local_variable_names",
      title: "Local variable naming conventions",
      shortDescription: `
Allows you to enforce a pattern, such as a prefix, for local variables, constants and field symbols.
Regexes are case-insensitive.`,
      tags: [RuleTag.Naming, RuleTag.SingleFile],
      badExample: `FORM bar.
  DATA foo.
ENDFORM.`,
      goodExample: `FORM bar.
  DATA lv_foo.
ENDFORM.`,
    };
  }
 
  private getDescription(expected: string, actual: string): string {
    return this.conf.patternKind === "required" ?
      "Local variable name does not match pattern " + expected + ": " + actual :
      "Local variable name must not match pattern " + expected + ": " + actual;
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: LocalVariableNamesConf) {
    this.conf = conf;
  }
 
  public runParsed(file: ABAPFile): Issue[] {
    const ret: Issue[] = [];
    if (this.conf.patternKind === undefined) {
      this.conf.patternKind = "required";
    }
    const stru = file.getStructure();
 
    if (stru === undefined) {
      return [];
    }
 
    // inside METHOD, FORM, FUNCTION MODULE
    for (const node of stru.findAllStructures(Structures.Form)) {
      ret.push(...this.checkLocals(node, file));
    }
    for (const node of stru.findAllStructures(Structures.Method)) {
      ret.push(...this.checkLocals(node, file));
    }
    for (const node of stru.findAllStructures(Structures.FunctionModule)) {
      ret.push(...this.checkLocals(node, file));
    }
 
    return ret;
  }
 
  private checkLocals(structure: StructureNode, file: ABAPFile): Issue[] {
    let ret: Issue[] = [];
 
    // data, field symbols
    for (const dat of structure.findAllStatements(Statements.Data)) {
      const parent = structure.findParent(dat);
      if (parent && parent.get() instanceof Structures.Data) {
        continue; // inside DATA BEGIN OF
      }
      const found = dat.findFirstExpression(Expressions.DefinitionName);
      if (found) {
        const token = found.getFirstToken();
        ret = ret.concat(this.checkName(token, file, this.conf.expectedData));
      }
    }
 
    // inline data
    for (const dat of structure.findAllExpressions(Expressions.InlineData)) {
      const found = dat.findFirstExpression(Expressions.TargetField);
      if (found) {
        const token = found.getFirstToken();
        ret = ret.concat(this.checkName(token, file, this.conf.expectedData));
      }
    }
 
    // data structures, data begin of, first level
    const dataStructures = structure.findAllStructures(Structures.Data);
    for (const struc of dataStructures) {
      // ignore nested DATA BEGIN
      const stat = struc.findFirstStatement(Statements.DataBegin);
      const found = stat?.findFirstExpression(Expressions.DefinitionName);
      if (found) {
        const token = found.getFirstToken();
        ret = ret.concat(this.checkName(token, file, this.conf.expectedData));
      }
    }
 
    for (const fieldsymbol of structure.findAllStatements(Statements.FieldSymbol)) {
      const found = fieldsymbol.findFirstExpression(Expressions.FieldSymbol);
      if (found) {
        const token = found.getFirstToken();
        ret = ret.concat(this.checkName(token, file, this.conf.expectedFS));
      }
    }
 
    // inline field symbols
    for (const fieldsymbol of structure.findAllExpressions(Expressions.InlineFS)) {
      const found = fieldsymbol.findFirstExpression(Expressions.TargetFieldSymbol);
      if (found) {
        const token = found.getFirstToken();
        ret = ret.concat(this.checkName(token, file, this.conf.expectedFS));
      }
    }
 
    const constants = structure.findAllStatements(Statements.Constant);
    for (const constant of constants) {
      const parent = structure.findParent(constant);
      if (parent && parent.get() instanceof Structures.Constants) {
        continue; // inside DATA BEGIN OF
      }
      const found = constant.findFirstExpression(Expressions.DefinitionName);
      if (found) {
        const token = found.getFirstToken();
        ret = ret.concat(this.checkName(token, file, this.conf.expectedConstant));
      }
    }
 
    return ret;
  }
 
  private checkName(token: AbstractToken, file: ABAPFile, expected: string): Issue[] {
    const ret: Issue[] = [];
    const regex = new RegExp(expected, "i");
    const name = token.getStr();
    if (NameValidator.violatesRule(name, regex, this.conf)) {
      const message = this.getDescription(expected, name);
      const issue = Issue.atToken(file, token, message, this.getMetadata().key, this.conf.severity);
      ret.push(issue);
    }
    return ret;
  }
 
}