All files / src/rules selection_screen_texts_missing.ts

96.63% Statements 115/119
89.65% Branches 26/29
100% Functions 7/7
96.63% Lines 115/119

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 1201x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 10794x 10794x 10794x 10794x 10794x 10794x 32192x 32192x 32192x 32192x 32192x 32192x 32192x 32192x 32192x 10794x 10794x 10267x 10267x 10794x 10794x 240x 240x 10794x 10794x 257x 257x 257x 10794x 10794x 336x 143x 143x 193x 336x 6x 6x 187x 187x 187x 187x 187x 187x 187x 187x 187x 10794x 10794x 193x 193x     193x 193x     193x 193x 193x 193x 1015x 1015x 4x 4x 2x 2x 2x 2x 2x 4x 4x 1015x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 1015x 984x 984x 1015x 2x 2x 19x 19x 19x 19x 19x 13x 13x 13x 13x 13x 13x 13x 13x 19x 1015x 193x 10794x  
import {IRule, IRuleMetadata} from "./_irule";
import {Issue} from "../issue";
import {BasicRuleConfig} from "./_basic_rule_config";
import {ITextElements} from "../objects/_abap_object";
import {IObject} from "../objects/_iobject";
import {IRegistry} from "../_iregistry";
import {Parameter, SelectOption, Include, SelectionScreen} from "../abap/2_statements/statements";
import {FieldSub, IncludeName} from "../abap/2_statements/expressions";
import {Program} from "../objects";
import {ABAPFile} from "../abap/abap_file";
 
export class SelectionScreenTextsMissingConf extends BasicRuleConfig {
}
 
export class SelectionScreenTextsMissing implements IRule {
 
  private reg: IRegistry;
  private conf = new SelectionScreenTextsMissingConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "selection_screen_texts_missing",
      title: "Selection screen texts missing",
      shortDescription: `Checks that selection screen parameters and select-options have selection texts`,
      extendedInformation: `Excludes parameters and select-options that:
* are inside a "SELECTION-SCREEN BEGIN OF LINE" block
* have the addition "NO-DISPLAY"`,
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: SelectionScreenTextsMissingConf) {
    this.conf = conf;
  }
 
  public initialize(reg: IRegistry) {
    this.reg = reg;
    return this;
  }
 
  public run(obj: IObject): Issue[] {
    if (!(obj instanceof Program)) {
      return [];
    }
 
    if (obj.isInclude()) {
      return [];
    }
 
    const selTexts = obj.getSelectionTexts();
    const output: Issue[] = [];
    const checked = new Set<string>();
 
    this.checkFile(obj.getMainABAPFile(), selTexts, output, checked);
 
    return output;
  }
 
  private checkFile(file: ABAPFile | undefined, selTexts: ITextElements,
                    output: Issue[], checked: Set<string>, mainProgName: string | undefined = undefined) {
    if (file === undefined) {
      return;
    }
 
    if (checked.has(file.getFilename())) {
      return;
    }
    checked.add(file.getFilename());
 
    let inLine = false;
    for (const stat of file.getStatements()) {
      const s = stat.get();
      if (s instanceof SelectionScreen) {
        const tokens = stat.concatTokens().toUpperCase();
        if (tokens.includes("BEGIN OF LINE")) {
          inLine = true;
        } else if (tokens.includes("END OF LINE")) {
          // known issue: doesn't support BEGIN and END OF LINE span across several includes (not seen a lot in the wild)
          inLine = false;
        }
        continue;
      }
      if (s instanceof Include) {
        const nameNode = stat.findFirstExpression(IncludeName);
        if (nameNode) {
          const inclName = nameNode.getFirstToken().getStr().toUpperCase();
          const inclObj = this.reg.getObject("PROG", inclName) as Program | undefined;
          if (inclObj) {
            this.checkFile(inclObj.getMainABAPFile(), selTexts, output, checked, mainProgName ?? file.getFilename());
          }
        }
        continue;
      }
      if (inLine || !(s instanceof Parameter || s instanceof SelectOption)) {
        continue;
      }
      if (stat.concatTokens().toUpperCase().includes("NO-DISPLAY")) {
        continue;
      }
 
      const fieldNode = stat.findFirstExpression(FieldSub);
      if (fieldNode) {
        const fieldName = fieldNode.getFirstToken().getStr().toUpperCase();
        if (selTexts[fieldName] === undefined) {
          const suffix = mainProgName ? `, ${mainProgName}` : ``;
          output.push(Issue.atToken(
            file,
            fieldNode.getFirstToken(),
            `Selection text missing for "${fieldName}"${suffix}`,
            this.getMetadata().key,
            this.conf.severity));
        }
      }
    }
  }
}