All files / transpiler/logic/analysis StructFieldAnalyzer.ts

100% Statements 20/20
100% Branches 2/2
100% Functions 5/5
100% Lines 20/20

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                                            8x                         160x 160x     24x     24x 24x   24x 38x 38x 38x 38x   38x 8x                   159x                 160x   160x 160x   160x                       8x 8x                          
/**
 * Struct Field Analyzer
 * Validates that struct field names don't conflict with C-Next reserved property names
 *
 * Fields cannot use reserved names like 'length' which conflict with C-Next's
 * built-in .length property for primitives and arrays.
 */
 
import { ParseTreeWalker } from "antlr4ng";
import { CNextListener } from "../parser/grammar/CNextListener";
import * as Parser from "../parser/grammar/CNextParser";
import IStructFieldError from "./types/IStructFieldError";
import SymbolUtils from "../symbols/SymbolUtils";
 
/**
 * Pure function to create the error message
 *
 * @param fieldName - The field name
 * @param structName - The containing struct name
 * @returns The formatted error message
 */
function formatStructFieldError(fieldName: string, structName: string): string {
  return (
    `Struct field '${fieldName}' in '${structName}' uses a reserved C-Next property name. ` +
    `The '.${fieldName}' property is built-in and will shadow this field.`
  );
}
 
/**
 * Listener that walks the parse tree to find struct field naming violations
 */
class StructFieldListener extends CNextListener {
  private readonly analyzer: StructFieldAnalyzer;
 
  constructor(analyzer: StructFieldAnalyzer) {
    super();
    this.analyzer = analyzer;
  }
 
  override enterStructDeclaration = (
    ctx: Parser.StructDeclarationContext,
  ): void => {
    const structName = ctx.IDENTIFIER().getText();
    const members = ctx.structMember();
 
    for (const member of members) {
      const fieldIdentifier = member.IDENTIFIER();
      const fieldName = fieldIdentifier.getText();
      const line = fieldIdentifier.symbol.line;
      const column = fieldIdentifier.symbol.column;
 
      if (SymbolUtils.isReservedFieldName(fieldName)) {
        this.analyzer.addError(structName, fieldName, line, column);
      }
    }
  };
}
 
/**
 * Analyzer for struct field naming violations
 */
class StructFieldAnalyzer {
  private errors: IStructFieldError[] = [];
 
  /**
   * Analyze the parse tree for struct field naming violations
   *
   * @param tree - The parsed program AST
   * @returns Array of errors (empty if all pass)
   */
  public analyze(tree: Parser.ProgramContext): IStructFieldError[] {
    this.errors = [];
 
    const listener = new StructFieldListener(this);
    ParseTreeWalker.DEFAULT.walk(listener, tree);
 
    return this.errors;
  }
 
  /**
   * Add a struct field naming error
   */
  public addError(
    structName: string,
    fieldName: string,
    line: number,
    column: number,
  ): void {
    const reservedNames = SymbolUtils.getReservedFieldNames().join(", ");
    this.errors.push({
      code: "E0355",
      structName,
      fieldName,
      line,
      column,
      message: formatStructFieldError(fieldName, structName),
      helpText: `Reserved field names: ${reservedNames}. Consider using 'len', 'size', or 'count' instead.`,
    });
  }
}
 
export default StructFieldAnalyzer;