All files / src/rules many_parenthesis.ts

93.22% Statements 55/59
83.33% Branches 25/30
100% Functions 7/7
93.22% Lines 55/59

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 1261x 1x 1x 1x 1x 1x     1x     1x   7270x     21683x                                         7013x       114x       144x   144x 144x 12x     132x 16x     132x 16x 16x     16x 4x 4x 4x       132x           16x 16x   16x 31x 31x 8x 23x 15x 3x   12x 12x     12x   28x 20x 8x 4x       9x 5x 5x 5x     9x       12x   12x 12x 30x 9x 9x 9x             12x      
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";
 
export class ManyParenthesisConf extends BasicRuleConfig {
}
 
export class ManyParenthesis extends ABAPRule {
 
  private conf = new ManyParenthesisConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "many_parenthesis",
      title: "Too many parenthesis",
      shortDescription: `Searches for expressions where extra parenthesis can safely be removed`,
      tags: [RuleTag.SingleFile],
      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: ManyParenthesisConf) {
    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);
      Iif (cond.length !== 1) {
        continue;
      }
      if (cond[0].getChildren().length === 1) {
        const message = "Too many parenthesis, simple";
        const issue = Issue.atToken(file, sub.getFirstToken(), message, this.getMetadata().key, this.conf.severity);
        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);
        Iif (i === undefined) {
          return [];
        }
        current = this.findComparator(i);
      }
      if (comparator === "") {
        comparator = current;
      } else if (comparator !== current) {
        return [];
      }
    }
 
    if (comparator !== "" && comparator !== "MIXED") {
      const message = "Too many parenthesis, 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();
        Eif (comparator === "") {
          comparator = current;
        } else if (current !== comparator) {
          return "MIXED";
        }
      }
    }
 
    return comparator;
  }
 
}