All files / src/rules avoid_use.ts

100% Statements 128/128
91.42% Branches 32/35
100% Functions 8/8
100% Lines 128/128

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 1291x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 18722x 18722x 18722x 18722x 18722x 18722x 18722x 18722x 18722x 18722x 18722x 18722x 18722x 18722x 18722x 18722x 1x 9362x 9362x 9362x 9362x 9362x 27949x 27949x 27949x 27949x 27949x 27949x 27949x 27949x 27949x 27949x 27949x 27949x 27949x 27949x 27949x 27949x 9362x 9362x 25x 25x 9362x 9362x 8898x 8898x 9362x 9362x 217x 217x 9362x 9362x 256x 256x 256x 256x 1376x 1376x 1376x 1376x 2x 1376x 6x 6x 6x 6x 6x 1374x 1x 1x 1368x 1x 1367x 1x 1366x 1x 1365x 5x 5x 5x 1376x 1376x 16x 16x 1376x 1376x 1376x 1376x 1376x 194x 194x 194x 9x 9x 9x 194x 1376x 256x 256x 256x 9362x 9362x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 9362x  
import * as Statements from "../abap/2_statements/statements";
import {Issue} from "../issue";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {TypeTable, TypeTableKey} from "../abap/2_statements/expressions";
import {IRuleMetadata, RuleTag} from "./_irule";
import {ABAPFile} from "../abap/abap_file";
import {Version} from "../version";
import {StatementNode} from "../abap/nodes/statement_node";
import {EditHelper, IEdit} from "../edit_helper";
 
export class AvoidUseConf extends BasicRuleConfig {
  /** Do not emit quick fix suggestion */
  public skipQuickFix?: boolean = false;
  /** Detects DEFINE (macro definitions) */
  public define: boolean = true;
  /** Detects statics */
  public statics: boolean = true;
  /** Detects DEFAULT KEY definitions, from version v740sp02 and up */
  public defaultKey: boolean = true;
  /** Detects BREAK and BREAK-POINTS */
  public break: boolean = true;
  /** Detects TEST SEAMS */
  public testSeams: boolean = true;
  /** Detects DESCRIBE TABLE LINES, use lines() instead */
  public describeLines: boolean = true;
}
 
export class AvoidUse extends ABAPRule {
 
  private conf = new AvoidUseConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "avoid_use",
      title: "Avoid use of certain statements",
      shortDescription: `Detects usage of certain statements.`,
      extendedInformation: `DEFAULT KEY: https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#avoid-default-key
 
Macros: https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-US/abenmacros_guidl.htm
 
STATICS: use CLASS-DATA instead
 
DESCRIBE TABLE LINES: use lines() instead (quickfix exists)
 
TEST-SEAMS: https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#use-test-seams-as-temporary-workaround`,
      tags: [RuleTag.Styleguide, RuleTag.SingleFile],
    };
  }
 
  private getDescription(statement: string): string {
    return "Avoid use of " + statement;
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: AvoidUseConf) {
    this.conf = conf;
  }
 
  public runParsed(file: ABAPFile) {
    const issues: Issue[] = [];
    let isStaticsBlock: boolean = false;
 
    for (const statementNode of file.getStatements()) {
      const statement = statementNode.get();
      let message: string | undefined = undefined;
      let fix: IEdit | undefined = undefined;
      if (this.conf.define && statement instanceof Statements.Define) {
        message = "DEFINE";
      } else if (this.conf.describeLines && statement instanceof Statements.Describe) {
        const children = statementNode.getChildren();
        if (children.length === 6 && children[3].getFirstToken().getStr().toUpperCase() === "LINES") {
          message = "DESCRIBE LINES, use lines() instead";
          fix = this.conf.skipQuickFix === true ? undefined : this.getDescribeLinesFix(file, statementNode);
        }
      } else if (this.conf.statics && statement instanceof Statements.StaticBegin) {
        isStaticsBlock = true;
        message = "STATICS";
      } else if (this.conf.statics && statement instanceof Statements.StaticEnd) {
        isStaticsBlock = false;
      } else if (this.conf.testSeams && statement instanceof Statements.TestSeam) {
        message = "TEST-SEAM";
      } else if (this.conf.statics && statement instanceof Statements.Static && isStaticsBlock === false) {
        message = "STATICS";
      } else if (this.conf.break && statement instanceof Statements.Break) {
        message = "BREAK/BREAK-POINT";
        fix = this.conf.skipQuickFix === true ? undefined : EditHelper.deleteStatement(file, statementNode);
      }
 
      if (message) {
        issues.push(Issue.atStatement(file, statementNode, this.getDescription(message), this.getMetadata().key, this.conf.severity, fix));
      }
 
      if (this.conf.defaultKey
          && (this.reg.getConfig().getVersion() >= Version.v740sp02
          || this.reg.getConfig().getVersion() === Version.Cloud)
          && (statement instanceof Statements.Data || statement instanceof Statements.Type)) {
        const tt = statementNode.findFirstExpression(TypeTable)?.findDirectExpression(TypeTableKey);
        const token = tt?.findDirectTokenByText("DEFAULT");
        if (tt && token) {
          message = "DEFAULT KEY";
          issues.push(Issue.atToken(file, token, this.getDescription(message), this.getMetadata().key, this.conf.severity));
        }
      }
    }
 
    return issues;
  }
 
  private getDescribeLinesFix(file: ABAPFile, statementNode: StatementNode): IEdit|undefined {
    const children = statementNode.getChildren();
    const target = children[4].concatTokens();
    const source = children[2].concatTokens();
 
    const startPosition = children[0].getFirstToken().getStart();
    const insertText = target + " = lines( " + source + " ).";
 
    const deleteFix = EditHelper.deleteStatement(file, statementNode);
    const insertFix = EditHelper.insertAt(file, startPosition, insertText);
 
    const finalFix = EditHelper.merge(deleteFix, insertFix);
 
    return finalFix;
  }
}