All files / PARSE/3-Declare/c/collectors EnumCollector.ts

93.75% Statements 15/16
83.33% Branches 5/6
100% Functions 2/2
100% Lines 14/14

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                                                                14x 14x   12x     12x       12x 12x   12x 25x       25x   25x 25x                     12x                   12x                                                               25x                                  
/**
 * EnumCollector - Collects enum symbols from C parse trees.
 */
 
import type { EnumSpecifierContext } from "../../../2-Parse/c/grammar/CParser";
import type ICEnumSymbol from "../../../../types/symbols/c/ICEnumSymbol";
import type ICEnumMemberSymbol from "../../../../types/symbols/c/ICEnumMemberSymbol";
import ESourceLanguage from "../../../../utils/types/ESourceLanguage";
import type ISourceSpan from "../../../../types/ISourceSpan";
import ParserUtils from "../../../../utils/ParserUtils";
 
/**
 * Result of collecting an enum - includes both the enum symbol and its members.
 */
interface IEnumCollectorResult {
  readonly enum: ICEnumSymbol;
  readonly members: ReadonlyArray<ICEnumMemberSymbol>;
}
 
class EnumCollector {
  /**
   * Collect an enum symbol and its members from a specifier context.
   *
   * @param enumSpec The enum specifier context
   * @param sourceFile Source file path
   * @param span Source span of the declaration
   */
  static collect(
    enumSpec: EnumSpecifierContext,
    sourceFile: string,
    span: ISourceSpan,
  ): IEnumCollectorResult | null {
    const identifier = enumSpec.Identifier();
    if (!identifier) return null;
 
    const name = identifier.getText();
 
    // Collect enum members as separate symbols and as inline member info
    const memberSymbols: ICEnumMemberSymbol[] = [];
    const memberInfos: Array<{
      readonly name: string;
      readonly value?: number;
    }> = [];
    const enumList = enumSpec.enumeratorList();
 
    for (const enumeratorDef of enumList?.enumerator() ?? []) {
      const memberName = enumeratorDef
        .enumerationConstant()
        ?.Identifier()
        ?.getText();
      Iif (!memberName) continue;
 
      memberInfos.push({ name: memberName });
      memberSymbols.push(
        EnumCollector.memberSymbol(
          memberName,
          name,
          sourceFile,
          enumeratorDef,
          span,
        ),
      );
    }
 
    const enumSymbol: ICEnumSymbol = {
      kind: "enum",
      name,
      sourceFile,
      span,
      sourceLanguage: ESourceLanguage.C,
      visibility: "public",
      members: memberInfos,
    };
 
    return {
      enum: enumSymbol,
      members: memberSymbols,
    };
  }
 
  /**
   * One enum member as a symbol.
   *
   * Extracted from `collect` (#1318): adding the member span pushed that
   * method's cognitive complexity to 16 against a limit of 15, and three levels
   * of nesting around one push was the reason it was close to the limit at all.
   */
  private static memberSymbol(
    memberName: string,
    enumName: string,
    sourceFile: string,
    // The real shape, not `{ start?: unknown }`: an `unknown` here threw away
    // what the caller had and forced a cast, which would have waved
    // `{ start: "yes" }` straight through into getSpan (#1318 review).
    enumeratorDef: {
      start?: { line?: number; column?: number } | null;
      stop?: {
        line?: number;
        column?: number;
        text?: string | null;
        start?: number;
        stop?: number;
      } | null;
    },
    enumSpan: ISourceSpan,
  ): ICEnumMemberSymbol {
    return {
      kind: "enum_member",
      name: memberName,
      sourceFile,
      // #1318: a member carries its OWN span, not its parent's -- falling back
      // to the enum's only when the enumerator has no start token. The fallback
      // decision itself lives in `getSpanOr`, so the C-Next path cannot answer
      // it differently, which it did until the #1318 review caught it.
      span: ParserUtils.getSpanOr(enumeratorDef, enumSpan),
      sourceLanguage: ESourceLanguage.C,
      visibility: "public",
      parent: enumName,
    };
  }
}
 
export default EnumCollector;