All files / src/rules use_line_exists.ts

94% Statements 47/50
87.5% Branches 21/24
100% Functions 7/7
93.62% Lines 44/47

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 1091x 1x 1x 1x 1x 1x 1x   1x       1x     1x 7270x     21684x                                   7013x       114x       143x   143x 10x     133x       133x 133x 725x 725x 711x   14x 14x       10x       133x           11x 13x 13x 2x   11x 11x 11x 11x                     11x 31x 31x 10x 21x 2x 19x 1x     10x      
import {Issue} from "../issue";
import * as Statements from "../abap/2_statements/statements";
import * as Expressions from "../abap/2_statements/expressions";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {Version} from "../version";
import {IRuleMetadata, RuleTag} from "./_irule";
import {StatementNode} from "../abap/nodes";
import {Comment} from "../abap/2_statements/statements/_statement";
import {ABAPFile} from "../abap/abap_file";
import {ABAPObject} from "../objects/_abap_object";
 
export class UseLineExistsConf extends BasicRuleConfig {
}
 
export class UseLineExists extends ABAPRule {
  private conf = new UseLineExistsConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "use_line_exists",
      title: "Use line_exists",
      shortDescription: `Use line_exists, from 740sp02 and up`,
      extendedInformation: `
https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#prefer-line_exists-to-read-table-or-loop-at
 
Not reported if the READ TABLE statement contains BINARY SEARCH.`,
      tags: [RuleTag.Upport, RuleTag.Styleguide, RuleTag.SingleFile],
      badExample: `READ TABLE my_table TRANSPORTING NO FIELDS WITH KEY key = 'A'.
IF sy-subrc = 0.
ENDIF.`,
      goodExample: `IF line_exists( my_table[ key = 'A' ] ).
ENDIF.`,
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: UseLineExistsConf) {
    this.conf = conf;
  }
 
  public runParsed(file: ABAPFile, obj: ABAPObject) {
    const issues: Issue[] = [];
 
    if (obj.getType() === "INTF") {
      return [];
    }
 
    Iif (this.reg.getConfig().getVersion() < Version.v740sp02 && this.reg.getConfig().getVersion() !== Version.Cloud) {
      return [];
    }
 
    const statements = file.getStatements();
    for (let i = 0; i < statements.length; i++) {
      const statement = statements[i];
      if (!(statement.get() instanceof Statements.ReadTable)) {
        continue;
      }
      const concat = statement.concatTokens().toUpperCase();
      if (concat.includes(" TRANSPORTING NO FIELDS") === true
          && concat.includes(" BINARY SEARCH") === false
          && this.checksSubrc(i, statements) === true
          && this.usesTabix(i, statements) === false) {
        issues.push(Issue.atStatement(file, statement, "Use line_exists", this.getMetadata().key, this.conf.severity));
      }
    }
 
    return issues;
  }
 
///////////////////////
 
  private checksSubrc(index: number, statements: readonly StatementNode[]): boolean {
    for (let i = index + 1; i < statements.length; i++) {
      const statement = statements[i];
      if (statement.get() instanceof Comment) {
        continue;
      }
      for (const c of statement.findAllExpressions(Expressions.Cond)) {
        for (const s of c.findAllExpressions(Expressions.Source)) {
          Eif (s.concatTokens().toUpperCase() === "SY-SUBRC") {
            return true;
          }
        }
      }
      return false;
    }
    return false;
  }
 
  // this is a heuristic, data flow analysis is required to get the correct result
  private usesTabix(index: number, statements: readonly StatementNode[]): boolean {
    for (let i = index + 1; i < index + 5; i++) {
      const statement = statements[i];
      if (statement === undefined) {
        break;
      } else if (statement.get() instanceof Comment) {
        continue;
      } else if (statement.concatTokens().toUpperCase().includes(" SY-TABIX")) {
        return true;
      }
    }
    return false;
  }
 
}