All files / TRANSPILE/2-Plan HeaderTypeNames.ts

100% Statements 19/19
100% Branches 8/8
100% Functions 2/2
100% Lines 19/19

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                                                                                  616x 616x 1058x   616x 616x         1058x 331x 331x     727x 476x 476x 258x   476x           251x 118x 246x   118x               133x 17x            
/**
 * 2.2 Plan -- every type name a file's public header will name.
 *
 * ## Why this is one function and not two predicates
 *
 * "Which types does this header name?" was derived twice, and both derivations
 * stopped in the same place: at functions and variables (#1520).
 *
 *   `Transpiler._headerNeedsUserCHeaders` branched on `symbol.kind`, handled
 *   `variable` and `function`, and answered `false` for a struct -- so a struct
 *   never asked for the header defining its FIELD's type.
 *
 *   `HeaderGeneratorUtils.collectExternalTypes` walked function returns,
 *   function parameters and variables, and had no struct-field loop.
 *
 * Four `tests/issue-502` headers declared `SeaDash::Parse::ParseResult result;`
 * and included nothing that defines it. Patching one derivation would have left
 * the other wrong, which is why the enumeration moved here instead: a type the
 * header names is a fact about the symbols, and it has one answer.
 *
 * ## It enumerates; it does not judge
 *
 * Whether a name is a builtin, local, forward-declarable or needs an include
 * are all different questions with different answers, asked by different
 * callers. Deciding any of them here would put a second question in a function
 * that answers one -- and it is exactly how the `::` skip came to serve two
 * masters, right for "can this be forward-declared" and wrong for "does this
 * need an include".
 */
 
import type TSymbol from "../../types/symbols/TSymbol";
import TypeResolver from "../../utils/TypeResolver";
 
class HeaderTypeNames {
  /**
   * Every type name the header's declarations will name, bare.
   *
   * @param symbols the file's public interface, as `PublicInterface.forFile`
   *   settled it
   */
  static collect(symbols: readonly TSymbol[]): Set<string> {
    const named = new Set<string>();
    for (const symbol of symbols) {
      HeaderTypeNames.addNamesOf(symbol, named);
    }
    named.delete("");
    return named;
  }
 
  /** The type names one symbol contributes, by kind. */
  private static addNamesOf(symbol: TSymbol, into: Set<string>): void {
    if (symbol.kind === "variable") {
      into.add(TypeResolver.getTypeName(symbol.type));
      return;
    }
 
    if (symbol.kind === "function") {
      into.add(TypeResolver.getTypeName(symbol.returnType));
      for (const parameter of symbol.parameters) {
        into.add(TypeResolver.getTypeName(parameter.type));
      }
      return;
    }
 
    // A struct's own name is not what it NAMES -- its fields are. The struct is
    // declared by this header, so it needs nothing included for itself; the
    // types of its fields may need a great deal (#1520).
    if (symbol.kind === "struct") {
      for (const field of symbol.fields.values()) {
        into.add(TypeResolver.getTypeName(field.type));
      }
      return;
    }
 
    // A bitmap emits `typedef uint8_t Flags;`, and that `uint8_t` is the one
    // type it names. It is not on a field and not on the symbol's own `type` --
    // it is `backingType`, which is why the first version of this missed it and
    // twelve bitmap headers lost `<stdint.h>`. `headers:standalone:check`
    // caught it, for the second time on the same construct.
    if (symbol.kind === "bitmap") {
      into.add(symbol.backingType);
    }
  }
}
 
export default HeaderTypeNames;