All files / TRANSPILE/1-Analyze/helpers SafeDivision.ts

92.3% Statements 12/13
85.71% Branches 12/14
100% Functions 2/2
100% Lines 10/10

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                                          47x                             1944x 1944x 1944x 362x 1944x 12x 12x                 9x 9x              
/**
 * ADR-051's `safe_div` / `safe_mod` calls, and which argument they write to.
 *
 * #1322: two rules ask the same thing of these calls and they are not the same
 * rule. ADR-051 governs their SHAPE -- four arguments, the first a variable to
 * receive the quotient (E0884/E0885). ADR-013 governs const-ness, and the
 * first argument is written through, so a `const` there is an assignment to a
 * const exactly as `K <- 1` is (E0877).
 *
 * The fact both need -- "argument 0 of this call is an output parameter" -- is
 * a property of these two builtins and belongs in one place. Before it was
 * shared, the const rule could not see the site at all: it walks assignment
 * targets, and this is a call argument. `safe_div(K, 10, 2, 0)` emitted `&K`
 * into a non-const pointer parameter, which gcc rejects with
 * `discards 'const' qualifier`, at exit 0.
 */
 
import * as Parser from "../../../PARSE/2-Parse/grammar/CNextParser";
import ExpressionUnwrapper from "../../../utils/ExpressionUnwrapper";
 
/** The two ADR-051 builtins, which share a signature. */
const SAFE_DIVISION = new Set(["safe_div", "safe_mod"]);
 
class SafeDivision {
  /**
   * The call this postfix expression is, when it is one of ADR-051's, with the
   * arguments as written. Null for anything else.
   *
   * The name must be a bare primary identifier followed by exactly one call
   * op: `safe_div(...)`. A member access that happens to end in the same name
   * is a different function.
   */
  static callOf(ctx: Parser.PostfixExpressionContext): {
    name: string;
    args: Parser.ExpressionContext[];
  } | null {
    const primary = ctx.primaryExpression();
    const ops = ctx.postfixOp();
    if (!primary || ops.length !== 1) return null;
    const name = primary.IDENTIFIER()?.getText();
    if (name === undefined || !SAFE_DIVISION.has(name)) return null;
    Iif (ops[0].LPAREN() === null) return null;
    return { name, args: ops[0].argumentList()?.expression() ?? [] };
  }
 
  /**
   * The name the call writes its result to, or null when the first argument is
   * not a plain variable name. ADR-051 needs a storage location: the helper
   * takes its address.
   */
  static outputName(args: readonly Parser.ExpressionContext[]): string | null {
    const first = args[0];
    return first === undefined
      ? null
      : ExpressionUnwrapper.getSimpleIdentifier(first);
  }
}
 
export default SafeDivision;