All files / transpiler/logic/analysis runAnalyzers.ts

89.13% Statements 41/46
80.76% Branches 21/26
100% Functions 5/5
88.88% Lines 40/45

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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190                                                                                                                    1943x 1943x 9x             1943x                               167x 167x 7x     167x 167x 1x       166x 166x             166x   166x 167x             2x       164x 164x             1x       163x 163x 1x       162x 162x 2x       160x 160x 1x       159x 159x         159x 159x             159x 159x                     159x 159x         159x 159x     1x     159x        
/**
 * Run all semantic analyzers on a parsed C-Next program
 *
 * Extracted from transpiler.ts for reuse in the unified pipeline.
 * All 11 analyzers (plus comment validation) run in sequence, each returning
 * errors that block compilation.
 */
 
import { CommonTokenStream } from "antlr4ng";
import { ProgramContext } from "../parser/grammar/CNextParser";
import ParameterNamingAnalyzer from "./ParameterNamingAnalyzer";
import StructFieldAnalyzer from "./StructFieldAnalyzer";
import InitializationAnalyzer from "./InitializationAnalyzer";
import FunctionCallAnalyzer from "./FunctionCallAnalyzer";
import NullCheckAnalyzer from "./NullCheckAnalyzer";
import DivisionByZeroAnalyzer from "./DivisionByZeroAnalyzer";
import FloatModuloAnalyzer from "./FloatModuloAnalyzer";
import ArrayIndexTypeAnalyzer from "./ArrayIndexTypeAnalyzer";
import SignedShiftAnalyzer from "./SignedShiftAnalyzer";
import MixedTypeCategoryAnalyzer from "./MixedTypeCategoryAnalyzer";
import ReturnPathAnalyzer from "./ReturnPathAnalyzer";
import CommentExtractor from "./CommentExtractor";
import ITranspileError from "../../../lib/types/ITranspileError";
import SymbolTable from "../symbols/SymbolTable";
import CodeGenState from "../../state/CodeGenState";
 
/**
 * Options for running analyzers
 */
interface IAnalyzerOptions {
  /**
   * Symbol table containing external function definitions from C/C++ headers
   * Used by FunctionCallAnalyzer to recognize external functions.
   * Falls back to CodeGenState.symbolTable if not provided.
   */
  symbolTable?: SymbolTable;
}
 
/**
 * Generic analyzer error with common fields
 */
interface IAnalyzerError {
  line: number;
  column: number;
  message: string;
  code?: string;
  rule?: string;
}
 
/**
 * Convert analyzer errors to ITranspileError format and add to accumulator.
 * Returns true if any errors were added (for early return logic).
 */
function collectErrors(
  analyzerErrors: IAnalyzerError[],
  target: ITranspileError[],
  formatMessage?: (err: IAnalyzerError) => string,
): boolean {
  const formatter = formatMessage ?? ((e) => e.message);
  for (const err of analyzerErrors) {
    target.push({
      line: err.line,
      column: err.column,
      message: formatter(err),
      severity: "error",
    });
  }
  return analyzerErrors.length > 0;
}
 
/**
 * Run all semantic analyzers on a parsed program
 *
 * @param tree - The parsed program AST
 * @param tokenStream - Token stream for comment validation
 * @param options - Optional configuration including external struct info
 * @returns Array of errors (empty if all pass)
 */
function runAnalyzers(
  tree: ProgramContext,
  tokenStream: CommonTokenStream,
  options?: IAnalyzerOptions,
): ITranspileError[] {
  const errors: ITranspileError[] = [];
  const formatWithCode = (e: IAnalyzerError) =>
    `error[${e.code}]: ${e.message}`;
 
  // 1. Parameter naming validation (Issue #227: reserved naming patterns)
  const paramNamingAnalyzer = new ParameterNamingAnalyzer();
  if (collectErrors(paramNamingAnalyzer.analyze(tree), errors)) {
    return errors;
  }
 
  // 2. Struct field validation (reserved field names like 'length')
  const structFieldAnalyzer = new StructFieldAnalyzer();
  Iif (
    collectErrors(structFieldAnalyzer.analyze(tree), errors, formatWithCode)
  ) {
    return errors;
  }
 
  // 3. Initialization analysis (Rust-style use-before-init detection)
  const initAnalyzer = new InitializationAnalyzer();
  // External struct fields and symbolTable are read from CodeGenState directly
  const symbolTable = options?.symbolTable ?? CodeGenState.symbolTable;
  if (
    collectErrors(
      initAnalyzer.analyze(tree, symbolTable),
      errors,
      formatWithCode,
    )
  ) {
    return errors;
  }
 
  // 4. Call analysis (ADR-030: define-before-use)
  const callAnalyzer = new FunctionCallAnalyzer();
  if (
    collectErrors(
      callAnalyzer.analyze(tree, symbolTable),
      errors,
      formatWithCode,
    )
  ) {
    return errors;
  }
 
  // 5. NULL check analysis (ADR-047: C library interop)
  const nullAnalyzer = new NullCheckAnalyzer();
  if (collectErrors(nullAnalyzer.analyze(tree), errors, formatWithCode)) {
    return errors;
  }
 
  // 6. Division by zero analysis (ADR-051: compile-time detection)
  const divZeroAnalyzer = new DivisionByZeroAnalyzer();
  if (collectErrors(divZeroAnalyzer.analyze(tree), errors, formatWithCode)) {
    return errors;
  }
 
  // 7. Float modulo analysis (catch % with f32/f64 early)
  const floatModAnalyzer = new FloatModuloAnalyzer();
  if (collectErrors(floatModAnalyzer.analyze(tree), errors, formatWithCode)) {
    return errors;
  }
 
  // 8. Array index type validation (ADR-054: unsigned indexes only)
  const indexTypeAnalyzer = new ArrayIndexTypeAnalyzer();
  Iif (collectErrors(indexTypeAnalyzer.analyze(tree), errors, formatWithCode)) {
    return errors;
  }
 
  // 9. Signed shift validation (MISRA C:2012 Rule 10.1: no signed shifts)
  const signedShiftAnalyzer = new SignedShiftAnalyzer();
  Iif (
    collectErrors(signedShiftAnalyzer.analyze(tree), errors, formatWithCode)
  ) {
    return errors;
  }
 
  // 10. Mixed essential type category (MISRA C:2012 Rule 10.4, ADR-024 / Issue #1091)
  const mixedTypeCategoryAnalyzer = new MixedTypeCategoryAnalyzer();
  Iif (
    collectErrors(
      mixedTypeCategoryAnalyzer.analyze(tree),
      errors,
      formatWithCode,
    )
  ) {
    return errors;
  }
 
  // 11. Return-path analysis (ADR-112: non-void functions must return on all paths)
  const returnPathAnalyzer = new ReturnPathAnalyzer();
  Iif (collectErrors(returnPathAnalyzer.analyze(tree), errors, formatWithCode)) {
    return errors;
  }
 
  // 12. Comment validation (MISRA C:2012 Rules 3.1, 3.2) - ADR-043
  const commentExtractor = new CommentExtractor(tokenStream);
  collectErrors(
    commentExtractor.validate(),
    errors,
    (e) => `error[MISRA-${e.rule}]: ${e.message}`,
  );
 
  return errors;
}
 
export default runAnalyzers;