All files / src/rules function_module_recommendations.ts

97.84% Statements 91/93
94.44% Branches 17/18
100% Functions 7/7
97.84% Lines 91/93

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 931x 1x 1x 1x 1x 1x 1x 1x 1x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 17846x 1x 8925x 8925x 8925x 8925x 8925x 35095x 35095x 35095x 35095x 35095x 35095x 35095x 35095x 8925x 8925x 8488x 8488x 8925x 8925x 205x 205x 8925x 8925x 220x 220x     220x 220x 220x 6x 6x 6x 6x 2x 2x 4x 4x 4x 4x 6x 2x 2x 6x 220x 220x 8925x 8925x 8925x 2x 2x 8925x 8925x
import {ABAPFile, Issue, Version} from "..";
import {FunctionName} from "../abap/2_statements/expressions";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {IRuleMetadata, RuleTag} from "./_irule";
 
 
export type Recommendations = {name: string, replace: string, from?: Version;};
 
export class FunctionModuleRecommendationsConf extends BasicRuleConfig {
 
  /** Tuple of Function Module Name to be replaced, the recommended alternative and
   * the version from which the recommendation is valid.*/
  public recommendations: Recommendations[] = [
    {name: "CALCULATE_HASH_FOR_RAW", replace: "use CL_ABAP_HMAC"},
    {name: "ECATT_CONV_XSTRING_TO_STRING", replace: "use CL_BINARY_CONVERT"},
    {name: "F4_FILENAME", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "FUNCTION_EXISTS", replace: "surround with try-catch CX_SY_DYN_CALL_ILLEGAL_METHOD instead"},
    {name: "GUI_DOWNLOAD", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_UPLOAD", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUID_CREATE", replace: "use CL_SYSTEM_UUID"},
    {name: "IGN_TIMESTAMP_DIFFERENCE", replace: "use CL_ABAP_TSTMP"},
    {name: "IGN_TIMESTAMP_PLUSMINUS", replace: "use CL_ABAP_TSTMP"},
    {name: "JOB_CREATE", replace: "use CL_BP_ABAP_JOB"},
    {name: "JOB_SUBMIT", replace: "use CL_BP_ABAP_JOB"},
    {name: "POPUP_TO_DECIDE", replace: "use POPUP_TO_CONFIRM"},
    {name: "POPUP_TO_GET_VALUE", replace: "use POPUP_GET_VALUES"},
    {name: "REUSE_ALV_GRID_DISPLAY", replace: "use CL_SALV_TABLE=>FACTORY or CL_GUI_ALV_GRID"},
    {name: "ROUND", replace: "use built in function: round()"},
    {name: "SAPGUI_PROGRESS_INDICATOR", replace: "use CL_PROGRESS_INDICATOR"},
    {name: "SCMS_BASE64_DECODE_STR", replace: "use class CL_HTTP_UTILITY methods"},
    {name: "SCMS_STRING_TO_XSTRING", replace: "use CL_BINARY_CONVERT"},
    {name: "SO_NEW_DOCUMENT_ATT_SEND_API1", replace: "use CL_BCS"},
    {name: "SSFC_BASE64_DECODE", replace: "use class CL_HTTP_UTILITY methods"},
    {name: "SSFC_BASE64_ENCODE", replace: "use class CL_HTTP_UTILITY methods"},
    {name: "SUBST_GET_FILE_LIST", replace: "see note 1686357"},
    {name: "WS_FILENAME_GET", replace: "use CL_GUI_FRONTEND_SERVICES"},
  ];
}
 
export class FunctionModuleRecommendations extends ABAPRule {
 
  private conf = new FunctionModuleRecommendationsConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "function_module_recommendations",
      title: "Function Module Recommendations",
      shortDescription: `Suggests replacements for various function modules`,
      extendedInformation: `https://docs.abapopenchecks.org/checks/53/`,
      tags: [RuleTag.SingleFile],
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: FunctionModuleRecommendationsConf) {
    this.conf = conf;
  }
 
  public runParsed(file: ABAPFile): Issue[] {
    const issues: Issue[] = [];
    if (!this.conf.recommendations) {
      return issues;
    }
    const configVersion = this.reg.getConfig().getVersion();
 
    for (const exNode of file.getStructure()?.findAllExpressions(FunctionName) || []) {
      const token = exNode.getFirstToken();
      let funcName = token.getStr().toUpperCase();
      // only check constant FM names
      if (!funcName.startsWith("'")) {
        continue;
      }
      // remove leading and trailing single quote
      funcName = funcName.slice(1, funcName.length - 1);
      const index = this.conf.recommendations.findIndex(
        i => i.name.toUpperCase() === funcName && (i.from === undefined || configVersion >= i.from));
      if (index >= 0) {
        issues.push(Issue.atToken(file, token, this.getMessage(index), this.getMetadata().key, this.conf.severity));
      }
    }
    return issues;
  }
 
 
  private getMessage(index: number) {
    return `Recommendation: Replace Function ${this.conf.recommendations[index].name} with: ${this.conf.recommendations[index].replace}`;
  }
 
}