All files / src/rules message_exists.ts

94.44% Statements 85/90
90.48% Branches 19/21
100% Functions 5/5
94.44% Lines 85/90

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 901x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7661x 7661x 7661x 7661x 22816x 22816x 22816x 22816x 22816x 22816x 7661x 7661x 7346x 7346x 7661x 7661x 139x 139x 7661x 7661x 158x 158x 158x 158x 12x 12x 146x 146x 146x 158x 7x 3x 3x 3x 3x 1x 1x 1x 1x 3x 7x 146x 158x 7x 4x 4x       4x 4x 4x 4x 1x 1x 1x 1x 1x 1x 1x 3x 4x     3x 3x 4x 1x 1x 1x 1x 4x 7x 146x 146x 146x 146x 146x 7661x
import * as Expressions from "../abap/2_statements/expressions";
import {Issue} from "../issue";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {MessageClass} from "../objects";
import {DDIC} from "../ddic";
import {ABAPFile} from "../abap/abap_file";
 
export class MessageExistsConf extends BasicRuleConfig {
}
 
export class MessageExistsRule extends ABAPRule {
  private conf = new MessageExistsConf();
 
  public getMetadata() {
    return {
      key: "message_exists",
      title: "Check MESSAGE exists",
      shortDescription: `In message statements, check that the message class + id exist`,
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: MessageExistsConf) {
    this.conf = conf;
  }
 
  public runParsed(file: ABAPFile) {
    const issues: Issue[] = [];
 
    const struc = file.getStructure();
    if (struc === undefined) {
      return [];
    }
 
    const expressions = struc.findAllExpressionsMulti([Expressions.MessageClass, Expressions.MessageSource]);
 
    for (const node of expressions) {
      if (node.get() instanceof Expressions.MessageClass) {
        const token = node.getFirstToken();
        const name = token.getStr();
        if (this.reg.getObject("MSAG", name) === undefined
            && new DDIC(this.reg).inErrorNamespace(name) === true) {
          const message = "Message class \"" + name + "\" not found";
          const issue = Issue.atToken(file, token, message, this.getMetadata().key, this.conf.severity);
          issues.push(issue);
        }
      }
    }
 
    for (const node of expressions) {
      if (node.get() instanceof Expressions.MessageSource) {
        const clas = node.findFirstExpression(Expressions.MessageClass);
        if (clas === undefined) {
// todo, handle case where message class is defined on header level instead of in the statement
          continue;
        }
        const token = clas.getFirstToken();
        const name = token.getStr();
        const msag = this.reg.getObject("MSAG", name) as MessageClass | undefined;
        if (msag === undefined) {
          if (new DDIC(this.reg).inErrorNamespace(name) === true) {
            const message = "Message class \"" + token.getStr() + "\" not found";
            const issue = Issue.atToken(file, token, message, this.getMetadata().key, this.conf.severity);
            issues.push(issue);
          }
          continue;
        }
        const typeNumber = node.findFirstExpression(Expressions.MessageTypeAndNumber);
        if (typeNumber === undefined) {
          continue;
        }
        const numberToken = typeNumber.getFirstToken();
        const num = numberToken.getStr().substr(1);
        if (msag.getByNumber(num) === undefined) {
          const message = "Message number \"" + num + "\" not found in class \"" + name + "\"";
          const issue = Issue.atToken(file, numberToken, message, this.getMetadata().key, this.conf.severity);
          issues.push(issue);
        }
      }
    }
 
// todo, check number of placeholders in message vs code matches
 
    return issues;
  }
}