All files / src/rules message_exists.ts

94.56% Statements 87/92
90.47% Branches 19/21
100% Functions 5/5
94.56% Lines 87/92

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 921x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 8930x 8930x 8930x 8930x 26610x 26610x 26610x 26610x 26610x 26610x 26610x 8930x 8930x 8485x 8485x 8930x 8930x 202x 202x 8930x 8930x 222x 222x 222x 222x 12x 12x 210x 210x 210x 222x 7x 3x 3x 3x 3x 1x 1x 1x 1x 3x 7x 210x 222x 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 210x 210x 210x 210x 210x 8930x
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";
import {IRuleMetadata, RuleTag} from "./_irule";
 
export class MessageExistsConf extends BasicRuleConfig {
}
 
export class MessageExistsRule extends ABAPRule {
  private conf = new MessageExistsConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "message_exists",
      title: "Check MESSAGE exists",
      shortDescription: `In message statements, check that the message class + id exist`,
      tags: [RuleTag.Syntax],
    };
  }
 
  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;
  }
}