@abaplint/core
Version:
abaplint - Core API
443 lines (419 loc) • 24 kB
JavaScript
"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