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 11103x 11103x 11103x 11103x 11103x 11103x 33133x 33133x 33133x 33133x 33133x 33133x 33133x 33133x 33133x 11103x 11103x 10568x 10568x 11103x 11103x 244x 244x 11103x 11103x 261x 261x 261x 11103x 11103x 349x 143x 143x 206x 349x 14x 14x 192x 192x 192x 192x 192x 192x 192x 192x 192x 11103x 11103x 206x 206x     206x 206x     206x 206x 206x 206x 1063x 1063x 4x 4x 2x 2x 2x 2x 2x 4x 4x 1063x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 1063x 1024x 1024x 1063x 2x 2x 19x 19x 19x 19x 19x 13x 13x 13x 13x 13x 13x 13x 13x 19x 1063x 206x 11103x  
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));
        }
      }
    }
  }
}