All files / lib parseCHeader.ts

100% Statements 34/34
100% Branches 17/17
100% Functions 4/4
100% Lines 34/34

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                                      32x   4x   8x   2x   3x   8x   4x   3x                       21x         32x   8x 8x   2x 2x   8x 8x   4x 4x       32x 32x                                                                                     24x   24x   24x 24x 24x 24x     24x 24x   24x 24x     24x   24x           3x             3x                  
/**
 * Parse C/C++ header source and extract symbols for IDE features
 * ADR-055 Phase 7: Direct TCSymbol → ISymbolInfo conversion (no ISymbol intermediate)
 */
 
import { CharStream, CommonTokenStream } from "antlr4ng";
import { CLexer } from "../transpiler/logic/parser/c/grammar/CLexer";
import { CParser } from "../transpiler/logic/parser/c/grammar/CParser";
import CResolver from "../transpiler/logic/symbols/c";
import ISymbolInfo from "./types/ISymbolInfo";
import IParseWithSymbolsResult from "./types/IParseWithSymbolsResult";
import TSymbolKind from "./types/TSymbolKind";
import TCSymbol from "../transpiler/types/symbols/c/TCSymbol";
import SymbolPathUtils from "./utils/SymbolPathUtils";
 
/**
 * Map TCSymbol kind to library TSymbolKind
 */
function mapCSymbolKind(kind: TCSymbol["kind"]): TSymbolKind {
  switch (kind) {
    case "struct":
      return "struct";
    case "function":
      return "function";
    case "variable":
      return "variable";
    case "enum":
      return "enum";
    case "enum_member":
      return "enumMember";
    case "type":
      return "type";
    default:
      return "variable";
  }
}
 
/**
 * ADR-055 Phase 7: Convert TCSymbol directly to ISymbolInfo.
 * Handles the discriminated union by extracting common fields and type-specific fields.
 */
function convertTCSymbolsToISymbolInfo(
  symbols: TCSymbol[],
  filePath?: string,
): ISymbolInfo[] {
  return symbols.map((sym) => {
    // Extract type and parent based on symbol kind
    let type: string | undefined;
    let parent: string | undefined;
 
    switch (sym.kind) {
      case "function":
        type = sym.type;
        break;
      case "variable":
        type = sym.type;
        break;
      case "enum_member":
        parent = sym.parent;
        break;
      case "type":
        type = sym.type;
        break;
      // struct and enum have no type field
    }
 
    const id = SymbolPathUtils.buildSimpleDotPath(parent, sym.name);
    return {
      name: sym.name,
      fullName: SymbolPathUtils.buildSimpleDotPath(parent, sym.name),
      kind: mapCSymbolKind(sym.kind),
      id,
      parentId: parent,
      type,
      parent,
      line: sym.sourceLine ?? 0,
      sourceFile: filePath,
      language: "c",
    };
  });
}
 
/**
 * Parse C/C++ header source and extract symbols for IDE features
 *
 * Unlike transpilation, this function attempts to extract symbols even when
 * there are parse errors, making it suitable for autocomplete during typing.
 *
 * @param source - C/C++ header source code string
 * @param filePath - Optional file path for symbol source tracking
 * @returns Parse result with symbols
 *
 * @example
 * ```typescript
 * import parseCHeader from './lib/parseCHeader';
 *
 * const result = parseCHeader(headerSource, 'config.h');
 * // Find all functions defined in the header
 * const functions = result.symbols.filter(s => s.kind === 'function');
 * ```
 */
function parseCHeader(
  source: string,
  filePath?: string,
): IParseWithSymbolsResult {
  const errors: Array<{
    line: number;
    column: number;
    message: string;
    severity: "error" | "warning";
  }> = [];
 
  try {
    // Parse C source
    const charStream = CharStream.fromString(source);
    const lexer = new CLexer(charStream);
    const tokenStream = new CommonTokenStream(lexer);
    const parser = new CParser(tokenStream);
 
    // Suppress error output for headers (they often have incomplete code)
    lexer.removeErrorListeners();
    parser.removeErrorListeners();
 
    const tree = parser.compilationUnit();
    const result = CResolver.resolve(tree, filePath ?? "<header>");
 
    // ADR-055 Phase 7: Direct TCSymbol → ISymbolInfo conversion
    const symbols = convertTCSymbolsToISymbolInfo(result.symbols, filePath);
 
    return {
      success: true,
      errors,
      symbols,
    };
  } catch (err) {
    errors.push({
      line: 1,
      column: 0,
      message: err instanceof Error ? err.message : String(err),
      severity: "error",
    });
 
    return {
      success: false,
      errors,
      symbols: [],
    };
  }
}
 
export default parseCHeader;