All files / src/rules many_parentheses.ts

95.38% Statements 124/130
91.18% Branches 31/34
100% Functions 7/7
95.38% Lines 124/130

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 120 121 122 123 124 125 126 127 128 129 1301x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7674x 7674x 7674x 7674x 7674x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 22886x 7674x 7674x 7364x 7364x 7674x 7674x 140x 140x 7674x 7674x 172x 172x 172x 172x 12x 12x 160x 172x 30x 30x 160x 172x 21x 21x     21x 7x 7x 7x 7x 7x 7x 7x 21x 160x 160x 160x 7674x 7674x 7674x 7674x 30x 30x 30x 30x 53x 53x 12x 53x 20x 3x 3x 17x 20x     17x 17x 53x 38x 53x 8x 8x 53x 19x 30x 5x 5x 5x 5x 19x 19x 19x 7674x 7674x 17x 17x 17x 17x 39x 11x 11x 11x 11x     11x 39x 17x 17x 17x 7674x 7674x
import * as Expressions from "../abap/2_statements/expressions";
import {Issue} from "../issue";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {IRuleMetadata, RuleTag} from "./_irule";
import {ExpressionNode, TokenNode} from "../abap/nodes";
import {ABAPFile} from "../abap/abap_file";
import {EditHelper} from "../edit_helper";
 
export class ManyParenthesesConf extends BasicRuleConfig {
}
 
export class ManyParentheses extends ABAPRule {
 
  private conf = new ManyParenthesesConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "many_parentheses",
      title: "Too many parentheses",
      shortDescription: `Searches for expressions where extra parentheses can safely be removed`,
      tags: [RuleTag.SingleFile, RuleTag.Quickfix],
      badExample: `
IF ( destination IS INITIAL ).
ENDIF.
IF foo = boo AND ( bar = lar AND moo = loo ).
ENDIF.
`,
      goodExample: `
IF destination IS INITIAL.
ENDIF.
IF foo = boo AND bar = lar AND moo = loo.
ENDIF.
`,
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: ManyParenthesesConf) {
    this.conf = conf;
  }
 
  public runParsed(file: ABAPFile) {
    const issues: Issue[] = [];
 
    const structure = file.getStructure();
    if (structure === undefined) {
      return [];
    }
 
    for (const cond of structure.findAllExpressions(Expressions.Cond)) {
      issues.push(...this.analyze(file, cond));
    }
 
    for (const sub of structure.findAllExpressions(Expressions.CondSub)) {
      const cond = sub.findDirectExpressions(Expressions.Cond);
      if (cond.length !== 1) {
        continue;
      }
      if (cond[0].getChildren().length === 1) {
        const message = "Too many parentheses, simple";
        const fixText = sub.getChildren()[1].concatTokens();
        const fix = EditHelper.replaceRange(file, sub.getFirstToken().getStart(), sub.getLastToken().getEnd(), fixText);
 
        const issue = Issue.atToken(file, sub.getFirstToken(), message, this.getMetadata().key, this.conf.severity, fix);
        issues.push(issue);
      }
    }
 
    return issues;
  }
 
////////////////////
 
  private analyze(file: ABAPFile, cond: ExpressionNode): Issue[] {
    const issues: Issue[] = [];
    let comparator = "";
 
    for (const c of cond.getChildren()) {
      let current = "";
      if (c instanceof TokenNode) {
        current = c.get().getStr().toUpperCase();
      } else if (c instanceof ExpressionNode && c.get() instanceof Expressions.CondSub) {
        if (c.getFirstToken().getStr() === "NOT") {
          return [];
        }
        const i = c.findDirectExpression(Expressions.Cond);
        if (i === undefined) {
          return [];
        }
        current = this.findComparator(i);
      }
      if (comparator === "") {
        comparator = current;
      } else if (comparator !== current) {
        return [];
      }
    }
 
    if (comparator !== "" && comparator !== "MIXED") {
      const message = "Too many parentheses, complex";
      const issue = Issue.atToken(file, cond.getFirstToken(), message, this.getMetadata().key, this.conf.severity);
      issues.push(issue);
    }
 
    return issues;
  }
 
  private findComparator(cond: ExpressionNode): string {
    let comparator = "";
 
    const children = cond.getChildren();
    for (const c of children) {
      if (c instanceof TokenNode) {
        const current = c.get().getStr().toUpperCase();
        if (comparator === "") {
          comparator = current;
        } else if (current !== comparator) {
          return "MIXED";
        }
      }
    }
 
    return comparator;
  }
 
}