All files / src/rules parser_702_chaining.ts

93.04% Statements 107/115
87.87% Branches 29/33
100% Functions 7/7
93.04% Lines 107/115

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 1151x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 11225x 11225x 11225x 11225x 33492x 33492x 33492x 33492x 33492x 33492x 33492x 33492x 33492x 33492x 11225x 11225x 10730x 10730x 11225x 11225x 227x 227x 11225x 11225x 248x 248x 248x 248x 237x 237x 11x 11x 248x     11x 248x 10x 10x 7x 7x 10x 6x 6x     6x 6x 3x 3x 3x 6x 6x 3x 3x 3x 6x 3x 11x 11x 11x 11x 11x 8x 8x 3x 11x     3x 3x 248x 7x 6x 6x     6x 6x 7x 11x 11x 11x 11225x 11225x 11225x 9x 9x 2x 2x 2x 2x 2x 2x 2x 9x 11225x 11225x 5x 5x 5x 11225x
import * as Expressions from "../abap/2_statements/expressions";
import {Issue} from "../issue";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {RuleTag, IRuleMetadata} from "./_irule";
import {ABAPFile} from "../abap/abap_file";
import {Version} from "../version";
import {Statements} from "..";
import {ExpressionNode} from "../abap/nodes";
 
export class Parser702ChainingConf extends BasicRuleConfig {
}
 
export class Parser702Chaining extends ABAPRule {
  private conf = new Parser702ChainingConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "parser_702_chaining",
      title: "Parser Error, bad chaining on 702",
      shortDescription:
`ABAP on 702 does not allow for method chaining with IMPORTING/EXPORTING/CHANGING keywords,
this rule finds these and reports errors.
Only active on target version 702 and below.`,
      tags: [RuleTag.Syntax, RuleTag.SingleFile],
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: Parser702ChainingConf) {
    this.conf = conf;
  }
 
  public runParsed(file: ABAPFile) {
    const issues: Issue[] = [];
 
    if (this.reg.getConfig().getVersion() !== Version.v702
        && this.reg.getConfig().getVersion() !== Version.v700) {
      return [];
    }
 
    const stru = file.getStructure();
    if (stru === undefined) {
      return [];
    }
 
    for (const chain of stru.findAllExpressions(Expressions.MethodCallChain)) {
      const calls = chain.findDirectExpressions(Expressions.MethodCall);
      if (calls.length < 2) {
        continue;
      }
      for (const call of calls) {
        const callParam = call.findDirectExpression(Expressions.MethodCallParam);
        if (callParam === undefined) {
          continue;
        }
        const param = callParam.findDirectExpression(Expressions.MethodParameters);
        if (param === undefined) {
          continue;
        }
        if (param.findDirectTokenByText("IMPORTING")
            || param.findDirectTokenByText("CHANGING")
            || param.findDirectTokenByText("EXCEPTIONS")) {
          const message = "This kind of method chaining not possible in 702";
          this.pushIssue(message, file, param, issues);
        }
      }
    }
 
    // after a value assignment (move statement whose source is a method call, or method parameter assignment),
    // there can't be any EXPORTING/IMPORTING/CHANGING/EXCEPTIONS
    for (const statement of file.getStatements()) {
      if (!(statement.get() instanceof Statements.Move)) {
        continue;
      }
      const source = statement.findDirectExpression(Expressions.Source);
      if (source === undefined) {
        continue;
      }
      this.ensureSourceHasNoProceduralKeywords(source, file, issues);
    }
    for (const methodParameters of stru.findAllExpressions(Expressions.MethodParameters)) {
      for (const params of methodParameters.findAllExpressions(Expressions.ParameterS)) {
        const source = params.findDirectExpression(Expressions.Source);
        if (source === undefined) {
          continue;
        }
        this.ensureSourceHasNoProceduralKeywords(source, file, issues);
      }
    }
 
    return issues;
  }
 
 
  private ensureSourceHasNoProceduralKeywords(source: ExpressionNode, file: ABAPFile, issues: Issue[]) {
    const forbiddenTokens = ["EXPORTING", "IMPORTING", "CHANGING", "EXCEPTIONS"];
    for (const param of source.findAllExpressions(Expressions.MethodParameters)) {
      const usedForbiddenToken = forbiddenTokens.find(text => param.findDirectTokenByText(text));
 
      if (usedForbiddenToken) {
        const message = `Unexpected word ${usedForbiddenToken} in functional method call`;
        this.pushIssue(message, file, param, issues);
      }
    }
  }
 
  private pushIssue(message: string, file: ABAPFile, node: ExpressionNode, issues: Issue[]) {
    const issue = Issue.atPosition(file, node.getFirstToken().getStart(), message, this.getMetadata().key, this.conf.severity);
    issues.push(issue);
  }
}