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 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 994x 994x 994x 994x 1658x 1658x 994x 994x 1x 1x 994x 994x 31x 31x 994x 994x 994x 3x 3x 3x 991x 991x 994x 8x 8x 8x 983x 983x 994x 994x 994x 994x 994x 1x 1x 994x 982x 982x 3x 3x 3x 3x 3x 2x 3x 1x 1x 3x 3x 994x 1x 1x 1x 1x 994x 4x 4x 990x 994x 861x 861x 994x 994x 994x 994x 1x 1x 128x 994x 994x 2x 2x 126x 126x 1x 1x 1x 1x 991x 4x 4x 991x 1641x 1641x 8x 8x 1641x 979x 979x 1x 1x 1x 1769x 1769x 1727x 1727x 42x 1769x 1x 1x 1769x 1769x 11x 11x 30x 30x 1769x 3x 3x 27x 27x 1x 1x | import * as Expressions from "../../2_statements/expressions";
import {ExpressionNode, TokenNode} from "../../nodes";
import {Source} from "./source";
import {MethodCallChain} from "./method_call_chain";
import {SourceFieldSymbol} from "./source_field_symbol";
import {SyntaxInput, syntaxIssue} from "../_syntax_input";
import {AbstractType} from "../../types/basic/_abstract_type";
import {CharacterType, DateType, IntegerType, StringType} from "../../types/basic";
import {TypeUtils} from "../_type_utils";
import {BuiltIn} from "../_builtin";
import {ObjectOriented} from "../_object_oriented";
export class Compare {
public static runSyntax(node: ExpressionNode, input: SyntaxInput): void {
const sourceTypes: (AbstractType | undefined)[] = [];
const sources = node.findDirectExpressions(Expressions.Source);
for (const t of sources) {
sourceTypes.push(Source.runSyntax(t, input));
}
for (const t of node.findDirectExpressions(Expressions.SourceFieldSymbolChain)) {
SourceFieldSymbol.runSyntax(t, input);
}
for (const t of node.findDirectExpressions(Expressions.MethodCallChain)) {
MethodCallChain.runSyntax(t, input);
}
const initialMessage = this.checkInitialOperand(node, input);
if (initialMessage !== undefined) {
input.issues.push(syntaxIssue(input, node.getFirstToken(), initialMessage));
return;
}
const predicateMessage = this.checkPredicateOperand(sources, input);
if (predicateMessage !== undefined) {
input.issues.push(syntaxIssue(input, node.getFirstToken(), predicateMessage));
return;
}
const typeUtils = new TypeUtils(input.scope);
const compareOperator = node.findDirectExpression(Expressions.CompareOperator)?.concatTokens().toUpperCase();
if (compareOperator === "CS"
&& sourceTypes.length === 2
&& sourceTypes.some((type) => typeUtils.isCharLikeForCompare(type) === false)) {
const message = "CS operands must be character-like (data type C, N, D, T, or STRING)";
input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
} else if (node.findDirectExpression(Expressions.CompareOperator)
&& typeUtils.isCompareable(sourceTypes[0], sourceTypes[1], sources[0], sources[1]) === false
&& sourceTypes.length === 2) {
let message = "Incompatible types for comparison";
if ((sourceTypes[0] instanceof DateType
&& sourceTypes[1] instanceof IntegerType
&& sources[1].findFirstExpression(Expressions.ArithOperator))
|| (sourceTypes[1] instanceof DateType
&& sourceTypes[0] instanceof IntegerType
&& sources[0].findFirstExpression(Expressions.ArithOperator))) {
message = "Date cannot be compared with arithmetic result of type Integer";
}
input.issues.push(syntaxIssue(input, node.getFirstToken(), message));
}
}
// a built-in function with a character-like result cannot be the operand of IS [NOT] INITIAL,
// "Unexpected operator IS" on a system, while one with a numeric result, lines( ), can
private static checkInitialOperand(node: ExpressionNode, input: SyntaxInput): string | undefined {
if (input.scope.getOpenABAP()) {
return undefined;
}
const tokens = node.getChildren().filter(c => c instanceof TokenNode).map(c => c.getFirstToken().getStr().toUpperCase());
if (tokens.includes("IS") === false || tokens[tokens.length - 1] !== "INITIAL") {
return undefined;
}
const chain = node.findDirectExpression(Expressions.Source)?.getChildren();
// xsdbool( ) and boolc( ) are their own form in the Source grammar, a condition in parentheses
const first = chain?.[0]?.getFirstToken().getStr().toUpperCase();
if (chain?.length === 4 && chain[0] instanceof TokenNode && (first === "XSDBOOL" || first === "BOOLC")) {
return `Built-in function "${chain[0].getFirstToken().getStr()}" cannot be the operand of IS INITIAL`;
}
const name = this.builtinCallName(node.findDirectExpression(Expressions.Source), input);
const returning = BuiltIn.searchBuiltin(name)?.getParameters().getReturning()?.getType();
if (returning instanceof StringType || returning instanceof CharacterType) {
return `Built-in function "${name}" cannot be the operand of IS INITIAL`;
}
return undefined;
}
// a predicate function, contains( ) or line_exists( ), is a relational expression and not a value,
// it can stand alone as a condition or inside boolc( ) or xsdbool( ), but cannot be an operand of a comparison
private static checkPredicateOperand(sources: readonly ExpressionNode[], input: SyntaxInput): string | undefined {
if (input.scope.getOpenABAP()) {
return undefined;
}
for (const source of sources) {
const name = this.builtinCallName(source, input);
if (BuiltIn.isPredicate(name) === true) {
return `Predicate function "${name}" cannot be the operand of a comparison`;
}
}
return undefined;
}
// the name of the built-in function when the source is exactly one call of it, not hidden by a method of the same name
private static builtinCallName(source: ExpressionNode | undefined, input: SyntaxInput): string | undefined {
const chain = source?.getChildren();
if (chain?.length !== 1 || !(chain[0] instanceof ExpressionNode) || !(chain[0].get() instanceof Expressions.MethodCallChain)) {
return undefined;
}
const calls = chain[0].getChildren();
if (calls.length !== 1 || !(calls[0] instanceof ExpressionNode) || !(calls[0].get() instanceof Expressions.MethodCall)) {
return undefined;
}
const name = calls[0].findDirectExpression(Expressions.MethodName)?.concatTokens();
if (name === undefined || BuiltIn.searchBuiltin(name) === undefined) {
return undefined;
}
// a method of the same name hides the built-in function
const enclosing = input.scope.findClassDefinition(input.scope.getEnclosingClassName());
if (enclosing !== undefined && new ObjectOriented(input.scope).searchMethodName(enclosing, name).method !== undefined) {
return undefined;
}
return name;
}
}
|