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 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 21784x 1x 10893x 10893x 10893x 10893x 10893x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 32519x 10893x 10893x 30x 30x 10893x 10893x 10345x 10345x 10893x 10893x 259x 259x 10893x 10893x 297x 297x 297x 297x 297x 1563x 1563x 1563x 1563x 1563x 1563x 3x 1563x 6x 6x 6x 6x 6x 1560x 2x 2x 1554x 2x 1552x 1x 1550x 2x 1549x 2x 2x 1x 1x 1x 1547x 1x 1545x 5x 5x 5x 1562x 1563x 21x 21x 1562x 1562x 1562x 1562x 1563x 222x 222x 222x 10x 10x 1x 1x 9x 9x 9x 222x 1563x 297x 297x 297x 10893x 10893x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 10893x  
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;
  }
}