All files / PARSE/3-Declare/cnext/utils DimensionResolver.ts

85.71% Statements 6/7
100% Branches 4/4
66.66% Functions 2/3
85.71% Lines 6/7

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                                                101x 108x                                           840x 840x   840x     840x              
/**
 * DimensionResolver - resolves one array dimension expression during symbol
 * collection.
 *
 * Issue #1127: VariableCollector and StructCollector each carried their own
 * version of this, and they disagreed. Both now resolve through this one
 * function.
 *
 * #1175: a dimension that needs a name is recorded as WRITTEN, as a
 * `TConstExpr`, and 1.4 Resolve settles it where the declaration is written.
 * It used to be recorded as its `getText()`, which joins tokens with no
 * separator, so the text that reached the header could be different tokens
 * (`1 - -1` became `1--1`) or name what C cannot see (`(LOCAL)`, `u32`).
 */
 
import * as Parser from "../../../2-Parse/grammar/CNextParser";
import ConstExprLowering from "../../../../utils/ConstExprLowering";
import ConstantEvaluator from "../../../../utils/ConstantEvaluator";
import invariant from "../../../../utils/invariant";
import UNRESOLVED_DIMENSION from "../../../../types/UNRESOLVED_DIMENSION";
import type IConstantEnvironment from "../../../../utils/types/IConstantEnvironment";
import type IDeclaredDimension from "../types/IDeclaredDimension";
 
/** 1.3 binds no name: every name waits for 1.4 */
const NO_NAMES: IConstantEnvironment = {
  valueOf: (name) => ({
    kind: "notConstant",
    reason: "unknown",
    spelling: name.path.join("."),
    at: name.at,
  }),
  // 1.3 only folds what needs no name; writing C for the rest is 1.4's
  cTypeName: (typeName): string => {
    invariant(false, `1.3 writes no constant expression as C: ${typeName}`);
  },
};
 
class DimensionResolver {
  /**
   * Resolve one array dimension expression.
   *
   * A dimension that needs no name (a literal, arithmetic over literals,
   * `sizeof` of a primitive) folds here, by the one evaluator. Anything else
   * is kept as written for 1.4, which binds its names where the declaration
   * is written (#1664 box 7).
   */
  static resolve(sizeExpr: Parser.ExpressionContext): IDeclaredDimension {
    const expr = ConstExprLowering.lower(sizeExpr);
    const result = ConstantEvaluator.evaluate(expr, NO_NAMES);
    const size =
      result.kind === "value"
        ? ConstantEvaluator.toNumber(result.value)
        : undefined;
    return size === undefined
      ? { size: UNRESOLVED_DIMENSION, expr }
      : { size, expr: null };
  }
}
 
export default DimensionResolver;