UNPKG

@abaplint/core

Version:
443 lines (419 loc) • 24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ObsoleteStatement = exports.ObsoleteStatementConf = void 0; const Statements = require("../abap/2_statements/statements"); const Expressions = require("../abap/2_statements/expressions"); const issue_1 = require("../issue"); const _abap_rule_1 = require("./_abap_rule"); const _basic_rule_config_1 = require("./_basic_rule_config"); const position_1 = require("../position"); const _irule_1 = require("./_irule"); const version_1 = require("../version"); const edit_helper_1 = require("../edit_helper"); const nodes_1 = require("../abap/nodes"); const expressions_1 = require("../abap/2_statements/expressions"); class ObsoleteStatementConf extends _basic_rule_config_1.BasicRuleConfig { constructor() { super(...arguments); /** Check for REFRESH statement */ this.refresh = true; /** Check for COMPUTE statement */ this.compute = true; /** Check for ADD statement */ this.add = true; /** Check for SUBTRACT statement */ this.subtract = true; /** Check for MULTIPLY statement */ this.multiply = true; /** Check for DIVIDE statement */ this.divide = true; /** Check for MOVE statement */ this.move = true; /** Checks for usages of IS REQUESTED */ this.requested = true; /** Checks for usages of OCCURS */ this.occurs = true; /** Checks for SET EXTENDED CHECK */ this.setExtended = true; /** Checks for WITH HEADER LINE */ this.withHeaderLine = true; /** Checks for FIELD-SYMBOLS ... STRUCTURE */ this.fieldSymbolStructure = true; /** Checks for TYPE-POOLS */ this.typePools = true; /** Checks for addition LOAD */ this.load = true; /** Checks for PARAMETER */ this.parameter = true; /** Checks for RANGES */ this.ranges = true; /** Checks for COMMUNICATION */ this.communication = true; /** Checks for PACK */ this.pack = true; /** Checks for SELECT without INTO */ this.selectWithoutInto = true; /** FREE MEMORY, without ID */ this.freeMemory = true; /** Checks for EXIT FROM SQL */ this.exitFromSQL = true; /** Checks for SORT itab BY <fs> */ this.sortByFS = true; /** Checks for CALL TRANSFORMATION OBJECTS */ this.callTransformation = true; /** Check for POSIX REGEX usage */ this.regex = true; /** Check for OCCURENCES vs OCCURRENCES usage */ this.occurences = true; /** Check for CLIENT SPECIFIED */ this.clientSpecified = true; /** Check for FORM DEFINITION */ this.formDefinition = true; /** Check for FORM IMPLEMENTATION */ this.formImplementation = true; } } exports.ObsoleteStatementConf = ObsoleteStatementConf; class ObsoleteStatement extends _abap_rule_1.ABAPRule { constructor() { super(...arguments); this.conf = new ObsoleteStatementConf(); } getMetadata() { return { key: "obsolete_statement", title: "Obsolete statements", shortDescription: `Checks for usages of certain obsolete statements`, tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Styleguide, _irule_1.RuleTag.Quickfix], extendedInformation: ` https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#prefer-functional-to-procedural-language-constructs https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#avoid-obsolete-language-elements SET EXTENDED CHECK: https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-us/abapset_extended_check.htm IS REQUESTED: https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abenlogexp_requested.htm WITH HEADER LINE: https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abapdata_header_line.htm FIELD-SYMBOLS STRUCTURE: https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-us/abapfield-symbols_obsolete_typing.htm TYPE-POOLS: from 702, https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-US/abennews-71-program_load.htm LOAD addition: from 702, https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-US/abennews-71-program_load.htm COMMUICATION: https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-us/abapcommunication.htm OCCURS: https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-us/abapdata_occurs.htm PARAMETER: https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abapparameter.htm RANGES: https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-US/abapranges.htm PACK: https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-us/abappack.htm MOVE: https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-us/abapmove_obs.htm SELECT without INTO: https://help.sap.com/doc/abapdocu_731_index_htm/7.31/en-US/abapselect_obsolete.htm SELECT COUNT(*) is considered okay FREE MEMORY: https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-us/abapfree_mem_id_obsolete.htm SORT BY FS: https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-US/abapsort_itab_obsolete.htm CALL TRANSFORMATION OBJECTS: https://help.sap.com/doc/abapdocu_752_index_htm/7.52/en-US/abapcall_transformation_objects.htm POSIX REGEX: https://help.sap.com/doc/abapdocu_755_index_htm/7.55/en-US/index.htm OCCURENCES: check for OCCURENCES vs OCCURRENCES CLIENT SPECIFIED, from 754: https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abapselect_client_obsolete.htm`, badExample: `REFRESH itab. COMPUTE foo = 2 + 2. MULTIPLY lv_foo BY 2. INTERFACE intf LOAD. IF foo IS SUPPLIED. ENDIF.`, }; } getConfig() { return this.conf; } setConfig(conf) { this.conf = conf; } runParsed(file) { var _a, _b, _c; const issues = []; const statements = file.getStatements(); let prev = undefined; const configVersion = this.reg.getConfig().getVersion(); for (const staNode of statements) { const sta = staNode.get(); if ((sta instanceof Statements.Refresh && this.conf.refresh) || (sta instanceof Statements.Compute && this.conf.compute) || (sta instanceof Statements.Add && this.conf.add) || (sta instanceof Statements.Subtract && this.conf.subtract) || (sta instanceof Statements.ClassDefinitionLoad && this.conf.load && configVersion >= version_1.Version.v702) || (sta instanceof Statements.InterfaceLoad && this.conf.load && configVersion >= version_1.Version.v702) || (sta instanceof Statements.Multiply && this.conf.multiply) || (sta instanceof Statements.Divide && this.conf.divide) || (sta instanceof Statements.Move && this.conf.move && staNode.getTokens()[0].getStr().toUpperCase() === "MOVE" && staNode.getTokens()[1].getStr() !== "-" && staNode.getTokens()[1].getStr().toUpperCase() !== "EXACT")) { if (prev === undefined || staNode.getStart().getCol() !== prev.getCol() || staNode.getStart().getRow() !== prev.getRow()) { const message = "Statement \"" + staNode.getFirstToken().getStr() + "\" is obsolete"; const fix = this.getFix(file, sta, staNode); const issue = issue_1.Issue.atStatement(file, staNode, message, this.getMetadata().key, this.conf.severity, fix); issues.push(issue); } prev = staNode.getStart(); } if (this.conf.setExtended && sta instanceof Statements.SetExtendedCheck) { const issue = issue_1.Issue.atStatement(file, staNode, "SET EXTENDED CHECK is obsolete", this.getMetadata().key, this.conf.severity); issues.push(issue); } if (this.conf.communication && sta instanceof Statements.Communication) { const issue = issue_1.Issue.atStatement(file, staNode, "COMMUNICATION is obsolete", this.getMetadata().key, this.conf.severity); issues.push(issue); } if (this.conf.pack && sta instanceof Statements.Pack) { const issue = issue_1.Issue.atStatement(file, staNode, "PACK is obsolete", this.getMetadata().key, this.conf.severity); issues.push(issue); } if (this.conf.parameter && sta instanceof Statements.Parameter) { const token = staNode.getFirstToken(); if (token.getStr().toUpperCase() === "PARAMETER") { const fix = edit_helper_1.EditHelper.replaceToken(file, token, "PARAMETERS"); const issue = issue_1.Issue.atStatement(file, staNode, "Use PARAMETERS instead of PARAMETER", this.getMetadata().key, this.conf.severity, fix); issues.push(issue); } } if (this.conf.ranges && sta instanceof Statements.Ranges) { const children = staNode.getChildren(); let fix = undefined; if (children.length === 5) { const simpleNameString = children[1].concatTokens(); const fieldSubString = children[3].concatTokens(); const replacement = "TYPES " + simpleNameString + " LIKE RANGE OF " + fieldSubString + "."; fix = edit_helper_1.EditHelper.replaceRange(file, staNode.getStart(), staNode.getEnd(), replacement); } const issue = issue_1.Issue.atStatement(file, staNode, "Use LIKE RANGE OF instead of RANGES", this.getMetadata().key, this.conf.severity, fix); issues.push(issue); } if (this.conf.selectWithoutInto && (sta instanceof Statements.Select || sta instanceof Statements.SelectLoop) && staNode.findFirstExpression(Expressions.SQLIntoStructure) === undefined && staNode.findFirstExpression(Expressions.SQLIntoList) === undefined && staNode.findFirstExpression(Expressions.SQLIntoTable) === undefined) { const concat = (_a = staNode.findFirstExpression(Expressions.SQLFieldList)) === null || _a === void 0 ? void 0 : _a.concatTokens().toUpperCase(); if (concat !== "COUNT(*)" && concat !== "COUNT( * )") { const issue = issue_1.Issue.atStatement(file, staNode, "SELECT without INTO", this.getMetadata().key, this.conf.severity); issues.push(issue); } } if (this.conf.requested && sta instanceof Statements.If) { for (const compare of staNode.findAllExpressions(Expressions.Compare)) { const token = compare.findDirectTokenByText("REQUESTED"); if (token) { const fix = edit_helper_1.EditHelper.replaceToken(file, token, "SUPPLIED"); const issue = issue_1.Issue.atToken(file, token, "IS REQUESTED is obsolete", this.getMetadata().key, this.conf.severity, fix); issues.push(issue); } } } if (this.conf.occurs) { if ((sta instanceof Statements.Describe) || (sta instanceof Statements.Ranges) || (sta instanceof Statements.DataBegin) || (sta instanceof Statements.TypeBegin)) { const token = staNode.findDirectTokenByText("OCCURS"); if (token) { const issue = issue_1.Issue.atToken(file, token, "OCCURS is obsolete", this.getMetadata().key, this.conf.severity); issues.push(issue); } } for (const dataDef of staNode.findAllExpressions(Expressions.DataDefinition)) { const token = (_b = dataDef.findDirectExpression(Expressions.TypeTable)) === null || _b === void 0 ? void 0 : _b.findDirectTokenByText("OCCURS"); if (token) { const issue = issue_1.Issue.atToken(file, token, "OCCURS is obsolete", this.getMetadata().key, this.conf.severity); issues.push(issue); } } } if (this.conf.withHeaderLine === true && sta instanceof Statements.Data) { if (staNode.concatTokens().toUpperCase().includes("WITH HEADER LINE")) { const token = staNode.getTokens().find(t => t.getStr().toUpperCase() === "WITH"); if (token) { const issue = issue_1.Issue.atToken(file, token, "WITH HEADER LINE is obsolete", this.getMetadata().key, this.conf.severity); issues.push(issue); } } } if (this.conf.fieldSymbolStructure && sta instanceof Statements.FieldSymbol) { const token = staNode.findDirectTokenByText("STRUCTURE"); if (token) { const issue = issue_1.Issue.atToken(file, token, "FIELD-SYMBOLS ... STRUCTURE is obsolete", this.getMetadata().key, this.conf.severity); issues.push(issue); } } if (this.conf.typePools && sta instanceof Statements.TypePools && configVersion >= version_1.Version.v702) { const fix = edit_helper_1.EditHelper.deleteStatement(file, staNode); const issue = issue_1.Issue.atStatement(file, staNode, "Statement \"TYPE-POOLS\" is obsolete", this.getMetadata().key, this.conf.severity, fix); issues.push(issue); } if (this.conf.freeMemory && sta instanceof Statements.FreeMemory) { const concat = staNode.concatTokens().toUpperCase(); if (concat === "FREE MEMORY.") { const issue = issue_1.Issue.atStatement(file, staNode, "Statement \"FREE MEMORY\" without ID is obsolete", this.getMetadata().key, this.conf.severity); issues.push(issue); } } if (this.conf.exitFromSQL && sta instanceof Statements.Exit) { const concat = staNode.concatTokens().toUpperCase(); if (concat === "EXIT FROM SQL.") { const issue = issue_1.Issue.atStatement(file, staNode, "Statement \"EXIT FROM SQL\" is obsolete", this.getMetadata().key, this.conf.severity); issues.push(issue); } } if (this.conf.sortByFS && sta instanceof Statements.Sort) { const afterBy = staNode.findExpressionAfterToken("BY"); if (afterBy instanceof nodes_1.ExpressionNode && afterBy.get() instanceof expressions_1.SourceFieldSymbol) { const issue = issue_1.Issue.atStatement(file, staNode, "Statement \"SORT itab BY <fs>\" is obsolete", this.getMetadata().key, this.conf.severity); issues.push(issue); } } if (this.conf.callTransformation && sta instanceof Statements.CallTransformation) { const objects = staNode.findExpressionAfterToken("OBJECTS"); if (objects) { const issue = issue_1.Issue.atStatement(file, staNode, "Use PARAMETERS instead of OBJECTS", this.getMetadata().key, this.conf.severity); issues.push(issue); } } if (this.conf.occurences && sta instanceof Statements.Replace) { const concat = staNode.concatTokens().toUpperCase(); if (concat.includes(" OCCURENCES ")) { const issue = issue_1.Issue.atStatement(file, staNode, "Use \"OCCURRENCES\"", this.getMetadata().key, this.conf.severity); issues.push(issue); } } if (this.conf.formDefinition && sta instanceof Statements.FormDefinition) { const issue = issue_1.Issue.atStatement(file, staNode, "FORM DEFINITION", this.getMetadata().key, this.conf.severity); issues.push(issue); } if (this.conf.formImplementation && sta instanceof Statements.Form) { if (staNode.findDirectTokenByText("IMPLEMENTATION")) { const issue = issue_1.Issue.atStatement(file, staNode, "FORM IMPLEMENTATION", this.getMetadata().key, this.conf.severity); issues.push(issue); } } if (configVersion >= version_1.Version.v754 && this.conf.clientSpecified && (sta instanceof Statements.Select || sta instanceof Statements.SelectLoop || sta instanceof Statements.DeleteDatabase || sta instanceof Statements.InsertDatabase || sta instanceof Statements.ModifyDatabase || sta instanceof Statements.UpdateDatabase)) { const concat = staNode.concatTokens().toUpperCase(); if (concat.includes(" CLIENT SPECIFIED")) { const issue = issue_1.Issue.atStatement(file, staNode, "Use USING CLIENT", this.getMetadata().key, this.conf.severity); issues.push(issue); } } if (configVersion >= version_1.Version.v756 && this.conf.regex) { if (sta instanceof Statements.Find || sta instanceof Statements.Replace) { if ((_c = staNode.findFirstExpression(Expressions.FindType)) === null || _c === void 0 ? void 0 : _c.concatTokens().includes("REGEX")) { const issue = issue_1.Issue.atStatement(file, staNode, "REGEX obsolete, use PCRE", this.getMetadata().key, this.conf.severity); issues.push(issue); } } else { const classNameExpression = staNode.findAllExpressions(Expressions.ClassName); const methodNameExpression = staNode.findAllExpressions(Expressions.MethodName); if (classNameExpression.length !== 0 && methodNameExpression.length !== 0) { const className = classNameExpression[0].concatTokens(); const methodName = methodNameExpression[0].concatTokens(); if (className === "cl_abap_regex") { if (methodName === "create_posix") { const issue = issue_1.Issue.atStatement(file, staNode, "create_posix obsolete, use create_pcre", this.getMetadata().key, this.conf.severity); issues.push(issue); } } else if (className === "cl_abap_matcher") { if (methodName.includes("posix")) { const issue = issue_1.Issue.atStatement(file, staNode, "posix methods obsolete, use pcre methods", this.getMetadata().key, this.conf.severity); issues.push(issue); } } } } } } return issues; } getFix(file, statement, statementNode) { if (statement instanceof Statements.Refresh) { if (statementNode.getChildren().length === 6) { return undefined; } return edit_helper_1.EditHelper.replaceToken(file, statementNode.getFirstToken(), "CLEAR"); } else if (statement instanceof Statements.Compute) { const children = statementNode.getChildren(); if (children.length === 5) { const tokenForDeletion = statementNode.getFirstToken(); let endPosition = tokenForDeletion.getEnd(); endPosition = new position_1.Position(endPosition.getRow(), endPosition.getCol() + 1); return edit_helper_1.EditHelper.deleteRange(file, tokenForDeletion.getStart(), endPosition); } else { const targetString = children[2].concatTokens(); const sourceString = children[4].concatTokens(); const replacement = targetString + " = EXACT #( " + sourceString + " )."; return edit_helper_1.EditHelper.replaceRange(file, statementNode.getStart(), statementNode.getEnd(), replacement); } } else if (statement instanceof Statements.Add || statement instanceof Statements.Subtract) { const children = statementNode.getChildren(); const sourceString = children[1].concatTokens(); const targetString = children[3].concatTokens(); let replacement = ""; if (statement instanceof Statements.Add) { replacement = targetString + " = " + targetString + " + " + sourceString + "."; } else if (statement instanceof Statements.Subtract) { replacement = targetString + " = " + targetString + " - " + sourceString + "."; } return edit_helper_1.EditHelper.replaceRange(file, statementNode.getStart(), statementNode.getEnd(), replacement); } else if (statement instanceof Statements.Multiply || statement instanceof Statements.Divide) { const children = statementNode.getChildren(); const targetString = children[1].concatTokens(); const sourceString = children[3].concatTokens(); let replacement = ""; if (statement instanceof Statements.Multiply) { replacement = targetString + " = " + targetString + " * " + sourceString + "."; } else if (statement instanceof Statements.Divide) { replacement = targetString + " = " + targetString + " / " + sourceString + "."; } return edit_helper_1.EditHelper.replaceRange(file, statementNode.getStart(), statementNode.getEnd(), replacement); } else if (statement instanceof Statements.Move) { if (statementNode.getColon() !== undefined) { return undefined; } const children = statementNode.getChildren(); const sourceString = children[1].concatTokens(); const targetString = children[3].concatTokens(); let operator = children[2].concatTokens().toUpperCase(); if (operator === "TO") { operator = " = "; } else { operator = " ?= "; } const replacement = targetString + operator + sourceString + "."; return edit_helper_1.EditHelper.replaceRange(file, statementNode.getStart(), statementNode.getEnd(), replacement); } else if (statement instanceof Statements.ClassDefinitionLoad || statement instanceof Statements.InterfaceLoad) { let token = undefined; if (statement instanceof Statements.ClassDefinitionLoad) { token = statementNode.getChildren()[3].getFirstToken(); } else { token = statementNode.getChildren()[2].getFirstToken(); } let startPosition = token.getStart(); startPosition = new position_1.Position(startPosition.getRow(), startPosition.getCol() - 1); return edit_helper_1.EditHelper.deleteRange(file, startPosition, token.getEnd()); } return undefined; } } exports.ObsoleteStatement = ObsoleteStatement; //# sourceMappingURL=obsolete_statement.js.map