All files / src/rules avoid_use.ts

100% Statements 118/118
96.77% Branches 30/31
100% Functions 8/8
100% Lines 118/118

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 1191x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 15306x 15306x 15306x 15306x 15306x 15306x 15306x 15306x 15306x 15306x 15306x 15306x 1x 7654x 7654x 7654x 7654x 7654x 22836x 22836x 22836x 22836x 22836x 22836x 22836x 22836x 22836x 22836x 22836x 22836x 22836x 7654x 7654x 21x 21x 7654x 7654x 7346x 7346x 7654x 7654x 140x 140x 7654x 7654x 173x 173x 173x 173x 868x 868x 868x 868x 1x 868x 6x 6x 6x 6x 6x 867x 1x 1x 861x 1x 860x 1x 859x 5x 5x 868x 868x 14x 14x 868x 868x 868x 868x 868x 117x 117x 117x 7x 7x 7x 117x 868x 173x 173x 173x 7654x 7654x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 7654x  
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 {
  /** 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 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
 
DESCRIBE TABLE LINES: use lines() instead (quickfix exists)`,
      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.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.statics && statement instanceof Statements.Static && isStaticsBlock === false) {
        message = "STATICS";
      } else if (this.conf.break && statement instanceof Statements.Break) {
        message = "BREAK/BREAK-POINT";
      }
 
      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;
  }
}