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 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 14682x 14682x 14682x 14682x 14682x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 142758x 14682x 14682x 14120x 14120x 14682x 14682x 264x 264x 14682x 14682x 291x 291x 291x 14682x 14682x 388x 80x 80x 308x 308x 388x 8x 8x 300x 300x 388x 310x 1666x 1622x 1622x 1666x 1666x 1666x 1666x 1666x 1666x 21x 21x 23x 23x 1666x 23x 23x 1666x 13x 13x 10x 10x 10x 10x 10x 310x 300x 300x 300x 14682x 14682x 14682x 14682x 45x 45x 45x 90x 90x 2x 2x 2x 90x 22x 1x 1x 21x 88x 22x 22x 8x 8x 14x 14x 66x 30x 44x 5x 14x 9x 9x 44x 44x 90x 90x 11x 11x 33x 33x 25x 25x 25x 14682x 14682x 5x 5x 5x 5x 5x 25x 5x 5x 2x 2x 5x 5x 14682x 14682x 30x 48x 30x 30x 30x 30x 30x 34x 34x 32x 34x 2x 2x 2x 2x 34x 30x 30x 14682x 14682x | import * as Expressions from "../abap/2_statements/expressions";
import * as Statements from "../abap/2_statements/statements";
import {ReferenceType} from "../abap/5_syntax/_reference";
import {ISpaghettiScopeNode} from "../abap/5_syntax/_spaghetti_scope";
import {Constant as ConstantSyntax} from "../abap/5_syntax/expressions/constant";
import {SyntaxLogic} from "../abap/5_syntax/syntax";
import {ExpressionNode, TokenNode} from "../abap/nodes";
import {MethodDefinition} from "../abap/types";
import {TypedIdentifier} from "../abap/types/_typed_identifier";
import {AbstractType} from "../abap/types/basic/_abstract_type";
import {Integer8Type, IntegerType, PackedType, StructureType, TableType} from "../abap/types/basic";
import {Issue} from "../issue";
import {ABAPObject} from "../objects/_abap_object";
import {IObject} from "../objects/_iobject";
import {BasicRuleConfig} from "./_basic_rule_config";
import {IRule, IRuleMetadata} from "./_irule";
import {IRegistry} from "../_iregistry";
export class InlinePackedComputationConf extends BasicRuleConfig {
}
export class InlinePackedComputation implements IRule {
private reg: IRegistry;
private conf = new InlinePackedComputationConf();
public getMetadata(): IRuleMetadata {
return {
key: "inline_packed_computation",
title: "Inline declaration from a packed computation",
shortDescription: `Finds inline declarations whose value is an arithmetic expression with calculation type p`,
extendedInformation: `The result of a computation with calculation type p has no length and number of decimals
that a declaration could take over, so DATA( ) or FINAL( ) gets the type p LENGTH 8 DECIMALS 0 and the
syntax check warns "For the result of a computation with type P, the type P(8,0) is used here implicitly".
Decimals of the operands are lost, and so are digits beyond the length.
Reported when at least one operand is packed and all other operands are of type i, int8 or p. Operands
of other types, generic or unknown types, and the operator ** are not reported.
Declare the variable with the operands' type, or name the type with CONV.`,
tags: [],
badExample: `DATA a TYPE p LENGTH 8 DECIMALS 2.
DATA b TYPE p LENGTH 8 DECIMALS 2.
DATA(diff) = a - b.`,
goodExample: `DATA a TYPE p LENGTH 8 DECIMALS 2.
DATA b TYPE p LENGTH 8 DECIMALS 2.
DATA diff LIKE a.
diff = a - b.`,
};
}
public getConfig() {
return this.conf;
}
public setConfig(conf: InlinePackedComputationConf) {
this.conf = conf;
}
public initialize(reg: IRegistry) {
this.reg = reg;
return this;
}
public run(obj: IObject): readonly Issue[] {
if (!(obj instanceof ABAPObject)) {
return [];
}
const syntax = new SyntaxLogic(this.reg, obj).run();
if (syntax.issues.length > 0) {
return [];
}
const issues: Issue[] = [];
for (const file of obj.getABAPFiles()) {
for (const statement of file.getStatements()) {
if (!(statement.get() instanceof Statements.Move)) {
continue;
}
const inline = statement.findDirectExpression(Expressions.Target)?.findDirectExpression(Expressions.InlineData);
const name = inline?.findDirectExpression(Expressions.TargetField)?.getFirstToken();
const source = statement.findDirectExpression(Expressions.Source);
if (name === undefined
|| source === undefined
|| source.findDirectExpression(Expressions.ArithOperator) === undefined) {
continue;
}
const scope = syntax.spaghetti.lookupPosition(name.getStart(), file.getFilename());
if (scope === undefined) {
continue;
}
const operands = this.operandTypes(source, scope);
if (operands === undefined || operands.some(t => t instanceof PackedType) === false) {
continue;
}
const message = `"${name.getStr()}" gets type p LENGTH 8 DECIMALS 0 from this packed computation, ` +
`declare it with the operands' type or use CONV`;
issues.push(Issue.atToken(file, name, message, this.getMetadata().key, this.conf.severity));
}
}
return issues;
}
/** The types of the operands of an arithmetic expression, undefined when an operand is not of type
* i, int8 or p, or is not known */
private operandTypes(source: ExpressionNode, scope: ISpaghettiScopeNode): AbstractType[] | undefined {
const ret: AbstractType[] = [];
for (const child of source.getChildren()) {
let found: AbstractType | undefined = undefined;
if (child instanceof TokenNode) {
// parentheses and a sign, anything else is a constructor expression or a string operator
if (["(", ")", "+", "-"].includes(child.getFirstToken().getStr()) === false) {
return undefined;
}
continue;
} else if (child.get() instanceof Expressions.ArithOperator) {
if (["+", "-", "*", "/", "DIV", "MOD"].includes(child.concatTokens().toUpperCase()) === false) {
return undefined;
}
continue;
} else if (child.get() instanceof Expressions.Source) {
const nested = this.operandTypes(child, scope);
if (nested === undefined) {
return undefined;
}
ret.push(...nested);
continue;
} else if (child.get() instanceof Expressions.FieldChain) {
found = this.fieldChainType(child, scope);
} else if (child.get() instanceof Expressions.MethodCallChain) {
found = this.methodCallType(child, scope);
} else if (child.get() instanceof Expressions.Constant) {
found = ConstantSyntax.runSyntax(child);
}
if (found === undefined
|| found.isGeneric()
|| !(found instanceof PackedType || found instanceof IntegerType || found instanceof Integer8Type)) {
return undefined;
}
ret.push(found);
}
return ret;
}
private methodCallType(chain: ExpressionNode, scope: ISpaghettiScopeNode): AbstractType | undefined {
const last = chain.getLastChild();
if (!(last instanceof ExpressionNode) || !(last.get() instanceof Expressions.MethodCall)) {
return undefined;
}
const name = last.findDirectExpression(Expressions.MethodName)?.getFirstToken();
if (name === undefined) {
return undefined;
}
const method = scope.getData().references.find(reference =>
reference.referenceType === ReferenceType.MethodReference
&& reference.position.getStart().equals(name.getStart()))?.resolved;
if (!(method instanceof MethodDefinition)) {
return undefined;
}
return method.getParameters().getReturning()?.getType();
}
private fieldChainType(fieldChain: ExpressionNode, scope: ISpaghettiScopeNode): AbstractType | undefined {
const operand = scope.getData().references.find(reference =>
reference.referenceType === ReferenceType.DataReadReference
&& reference.position.getStart().equals(fieldChain.getFirstToken().getStart()))?.resolved;
if (!(operand instanceof TypedIdentifier)) {
return undefined;
}
let type: AbstractType | undefined = operand.getType();
for (const child of fieldChain.getChildren()) {
if (!(child instanceof ExpressionNode)
|| child.get() instanceof Expressions.SourceField) {
continue;
} else if (child.get() instanceof Expressions.ComponentName) {
if (type instanceof TableType) {
type = type.getRowType();
}
if (!(type instanceof StructureType)) {
return undefined;
}
type = type.getComponentByName(child.concatTokens());
} else {
return undefined;
}
}
return type;
}
}
|