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 11468x 11468x 11468x 11468x 11468x 11468x 34225x 34225x 34225x 34225x 34225x 34225x 34225x 34225x 34225x 11468x 11468x 10927x 10927x 11468x 11468x 247x 247x 11468x 11468x 264x 264x 264x 11468x 11468x 352x 143x 143x 209x 352x 14x 14x 195x 195x 195x 195x 195x 195x 195x 195x 195x 11468x 11468x 209x 209x     209x 209x     209x 209x 209x 209x 1111x 1111x 4x 4x 2x 2x 2x 2x 2x 4x 4x 1111x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 1111x 1072x 1072x 1111x 2x 2x 19x 19x 19x 19x 19x 13x 13x 13x 13x 13x 13x 13x 13x 19x 1111x 209x 11468x  
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));
        }
      }
    }
  }
}