All files / src/rules function_module_recommendations.ts

98.11% Statements 156/159
90.9% Branches 20/22
100% Functions 8/8
98.11% Lines 156/159

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 151 152 153 154 155 156 157 158 159 1601x 1x 1x 1x 1x 1x 1x 1x 2x 2x   2x 1x 1x 1x 1x 1x 1x 1x 1x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 22176x 1x 11090x 11090x 11090x 11090x 11090x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 33127x 11090x 11090x 10571x 10571x 11090x 11090x 247x 247x 11090x 11090x 274x 274x     274x 274x 274x 10x 10x 10x 10x 2x 2x 8x 8x 8x 269x 269x 2x 269x 8x 10x 6x 6x 10x 274x 274x 11090x 11090x 11090x 6x 6x 11090x 11090x  
import {FunctionName} from "../abap/2_statements/expressions";
import {ABAPFile} from "../abap/abap_file";
import {Issue} from "../issue";
import {ABAPRelease, Release, releaseAtLeast, Version, versionToABAPRelease} from "../version";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {IRuleMetadata, RuleTag} from "./_irule";
 
function resolveRelease(from: string): ABAPRelease | undefined {
  if (Release[from] !== undefined) { return Release[from]; }
  try { return versionToABAPRelease(from as Version); } catch { return undefined; }
}
 
export type Recommendations = {
  /** @pattern ^\w+$ */
  name: string,
  replace: string,
  from?: string,
};
 
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.
   * @uniqueItems true
   */
  public recommendations: Recommendations[] = [
    {name: "CALCULATE_HASH_FOR_CHAR", replace: "use CL_ABAP_MESSAGE_DIGEST"},
    {name: "CALCULATE_HASH_FOR_RAW", replace: "use CL_ABAP_HMAC or CL_ABAP_MESSAGE_DIGEST"},
    {name: "CCU_TIMESTAMP_DIFFERENCE", replace: "use CL_ABAP_TSTMP"},
    {name: "CLPB_EXPORT", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "CLPB_IMPORT", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "CONVERT_DATE_TO_EXTERNAL", replace: "use CL_ABAP_DATFM"},
    {name: "CONVERT_TIME_INPUT", replace: "use CL_ABAP_TIMEFM"},
    {name: "DOWNLOAD", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {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_CREATE_DIRECTORY", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_DELETE_FILE", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_DOWNLOAD", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_EXEC", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_FB_USAGE", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_FILE_LOAD_DIALOG", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_FILE_SAVE_DIALOG", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_GET_DESKTOP_INFO", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_GET_FILE_INFO", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_GET_REGVALUE", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_MULTIPLE_FILE_LOAD_DIALOG", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_REMOVE_DIRECTORY", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_RUN", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_SET_REGVALUE", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_UPLOAD", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_VSS_DOWNLOAD", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "GUI_VSS_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: "ISM_SD_GET_PRICING_CONDITIONS", replace: "use CL_PRC_RESULT_FACTORY as per note 2220005"},
    {name: "JOB_CREATE", replace: "use CL_BP_ABAP_JOB"},
    {name: "JOB_SUBMIT", replace: "use CL_BP_ABAP_JOB"},
    {name: "MD5_CALCULATE_HASH_FOR_CHAR", replace: "use CL_ABAP_MESSAGE_DIGEST"},
    {name: "MD5_CALCULATE_HASH_FOR_RAW", replace: "use CL_ABAP_MESSAGE_DIGEST"},
    {name: "POPUP_TO_CONFIRM_LOSS_OF_DATA", replace: "use POPUP_TO_CONFIRM"},
    {name: "POPUP_TO_CONFIRM_STEP", replace: "use POPUP_TO_CONFIRM"},
    {name: "POPUP_TO_CONFIRM_WITH_MESSAGE", replace: "use POPUP_TO_CONFIRM"},
    {name: "POPUP_TO_CONFIRM_WITH_VALUE", replace: "use POPUP_TO_CONFIRM"},
    {name: "POPUP_TO_CONFIRM_WITH_VALUE_2", replace: "use POPUP_TO_CONFIRM"},
    {name: "POPUP_TO_DECIDE", replace: "use POPUP_TO_CONFIRM"},
    {name: "POPUP_TO_DECIDE_INFO", replace: "use POPUP_TO_CONFIRM"},
    {name: "POPUP_TO_DECIDE_WITH_MESSAGE", replace: "use POPUP_TO_CONFIRM"},
    {name: "POPUP_TO_GET_VALUE", replace: "use POPUP_GET_VALUES"},
    {name: "QF05_RANDOM_INTEGER", replace: "use CL_ABAP_RANDOM_INT"},
    {name: "REGISTRY_GET", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "REGISTRY_SET", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {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: "SYSTEM_GET_UNIQUE_ID", replace: "use CL_SYSTEM_UUID"},
    {name: "SYSTEM_UUID_C_CREATE", replace: "use CL_SYSTEM_UUID"},
    {name: "UPLOAD", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "WS_DOWNLOAD", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "WS_EXECUTE", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "WS_FILENAME_GET", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "WS_FILE_ATTRIB", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "WS_FILE_COPY", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "WS_FILE_DELETE", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "WS_QUERY", replace: "use CL_GUI_FRONTEND_SERVICES"},
    {name: "WS_UPLOAD", 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],
      badExample: `CALL FUNCTION 'GUID_CREATE'
  IMPORTING
    ev_guid_32 = guid.`,
      goodExample: `guid = cl_system_uuid=>create_uuid_c32_static( ).`,
    };
  }
 
  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 configRelease = this.reg.getConfig().getRelease();
 
    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 => {
        if (i.name.toUpperCase() !== funcName) { return false; }
        if (i.from === undefined) { return true; }
        const rel = resolveRelease(i.from);
        return rel !== undefined && releaseAtLeast(configRelease, rel);
      });
      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}`;
  }
 
}