All files / transpiler/logic/analysis ParameterNamingAnalyzer.ts

100% Statements 21/21
100% Branches 4/4
100% Functions 6/6
100% Lines 21/21

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                                                  55x                           11x                         166x 166x     176x     176x 176x   176x 47x 55x 55x   55x 55x   55x 11x                     166x                 166x   166x 166x   166x                       11x                          
/**
 * Parameter Naming Analyzer
 * Issue #227: Validates that function parameters don't use reserved naming patterns
 *
 * Parameters cannot start with their containing function's name followed by underscore.
 * This naming pattern is reserved for scope-level variables and would bypass the
 * conflict detection heuristic in SymbolTable.detectConflict().
 */
 
import { ParseTreeWalker } from "antlr4ng";
import { CNextListener } from "../parser/grammar/CNextListener";
import * as Parser from "../parser/grammar/CNextParser";
import IParameterNamingError from "./types/IParameterNamingError";
 
/**
 * Pure function to check if a parameter name uses a reserved pattern
 *
 * @param parameterName - The parameter name to check
 * @param functionName - The name of the containing function (without scope prefix)
 * @returns true if the parameter name is problematic
 */
function isReservedParameterName(
  parameterName: string,
  functionName: string,
): boolean {
  return parameterName.startsWith(functionName + "_");
}
 
/**
 * Pure function to create the error message
 *
 * @param parameterName - The parameter name
 * @param functionName - The containing function name
 * @returns The formatted error message
 */
function formatParameterNamingError(
  parameterName: string,
  functionName: string,
): string {
  return (
    `Parameter '${parameterName}' cannot start with function name prefix ` +
    `'${functionName}_'. This naming pattern is reserved for scope-level variables.`
  );
}
 
/**
 * Listener that walks the parse tree to find parameter naming violations
 */
class ParameterNamingListener extends CNextListener {
  private readonly analyzer: ParameterNamingAnalyzer;
 
  constructor(analyzer: ParameterNamingAnalyzer) {
    super();
    this.analyzer = analyzer;
  }
 
  override enterFunctionDeclaration = (
    ctx: Parser.FunctionDeclarationContext,
  ): void => {
    const functionName = ctx.IDENTIFIER().getText();
    const paramList = ctx.parameterList();
 
    if (paramList) {
      for (const param of paramList.parameter()) {
        const paramIdentifier = param.IDENTIFIER();
        const paramName = paramIdentifier.getText();
        // Use the identifier's position for more precise error location
        const line = paramIdentifier.symbol.line;
        const column = paramIdentifier.symbol.column;
 
        if (isReservedParameterName(paramName, functionName)) {
          this.analyzer.addError(paramName, functionName, line, column);
        }
      }
    }
  };
}
 
/**
 * Analyzer for parameter naming violations
 */
class ParameterNamingAnalyzer {
  private errors: IParameterNamingError[] = [];
 
  /**
   * Analyze the parse tree for parameter naming violations
   *
   * @param tree - The parsed program AST
   * @returns Array of errors (empty if all pass)
   */
  public analyze(tree: Parser.ProgramContext): IParameterNamingError[] {
    this.errors = [];
 
    const listener = new ParameterNamingListener(this);
    ParseTreeWalker.DEFAULT.walk(listener, tree);
 
    return this.errors;
  }
 
  /**
   * Add a parameter naming error
   */
  public addError(
    parameterName: string,
    functionName: string,
    line: number,
    column: number,
  ): void {
    this.errors.push({
      code: "E0227",
      parameterName,
      functionName,
      line,
      column,
      message: formatParameterNamingError(parameterName, functionName),
      helpText: `Consider renaming to a name that doesn't start with '${functionName}_'`,
    });
  }
}
 
export default ParameterNamingAnalyzer;