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 9369x 9369x 9369x 9369x 27925x 27925x 27925x 27925x 27925x 27925x 27925x 9369x 9369x 8898x 8898x 9369x 9369x 216x 216x 9369x 9369x 240x 240x 240x 240x 12x 12x 228x 228x 228x 240x 7x 3x 3x 3x 3x 1x 1x 1x 1x 3x 7x 228x 240x 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 228x 228x 228x 228x 228x 9369x
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;
  }
}