All files / src/rules avoid_use.ts

100% Statements 150/150
92.3% Branches 48/52
100% Functions 8/8
100% Lines 150/150

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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 1511x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 21006x 1x 10504x 10504x 10504x 10504x 10504x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 31357x 10504x 10504x 28x 28x 10504x 10504x 9970x 9970x 10504x 10504x 252x 252x 10504x 10504x 291x 291x 291x 291x 291x 1530x 1530x 1530x 1530x 1530x 1530x 2x 1530x 6x 6x 6x 6x 6x 1528x 1x 1x 1522x 1x 1521x 1x 1520x 2x 1519x 2x 2x 1x 1x 1x 1517x 1x 1515x 5x 5x 5x 1529x 1530x 19x 19x 1529x 1529x 1529x 1529x 1530x 217x 217x 217x 10x 10x 1x 1x 9x 9x 9x 217x 1530x 291x 291x 291x 10504x 10504x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 10504x  
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";
import {Comment} from "../abap/2_statements/statements/_statement";
 
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. Use pseudo comment DEFAULT_KEY to ignore */
  public defaultKey: boolean = true;
  /** Detects BREAK and BREAK-POINTS */
  public break: boolean = true;
  /** Detects TEST SEAMS. Use pseudo comment TEST_SEAM_USAGE to ignore */
  public testSeams: boolean = true;
  /** Detects DESCRIBE TABLE LINES, use lines() instead */
  public describeLines: boolean = true;
  /** Detects EXPORT TO MEMORY */
  public exportToMemory: boolean = true;
  /** Detects EXPORT TO DATABASE */
  public exportToDatabase: 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
 
BREAK points`,
      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;
 
    const statements = file.getStatements();
    for (let i = 0; i < statements.length; i++) {
      const statementNode = statements[i];
      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.exportToMemory && statement instanceof Statements.Export && statementNode.concatTokens().includes("TO MEMORY ")) {
        message = "EXPORT TO MEMORY";
      } else if (this.conf.exportToDatabase && statement instanceof Statements.Export && statementNode.concatTokens().includes("TO DATABASE ")) {
        message = "EXPORT TO DATABASE";
      } else if (this.conf.testSeams && statement instanceof Statements.TestSeam) {
        const next = statements[i + 1];
        if (next?.get() instanceof Comment && next.concatTokens().includes("EC TEST_SEAM_USAGE")) {
          continue;
        }
        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) {
          const next = statements[i + 1];
          if (next?.get() instanceof Comment && next.concatTokens().includes("EC DEFAULT_KEY")) {
            continue;
          }
          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;
  }
}