All files / transpiler/logic/symbols/cnext/collectors FunctionCollector.ts

100% Statements 29/29
84.61% Branches 11/13
100% Functions 4/4
100% Lines 28/28

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                                                        987x 987x 987x     987x 987x     987x 987x     987x 987x   987x                                           987x 253x 253x 253x 253x   253x 253x     253x 253x 23x 25x 25x 25x 25x       253x             253x 23x     253x            
/**
 * FunctionCollector - Extracts function declarations from parse trees.
 * Handles return types, parameters, visibility, and signature generation.
 */
 
import * as Parser from "../../../parser/grammar/CNextParser";
import ESourceLanguage from "../../../../../utils/types/ESourceLanguage";
import ESymbolKind from "../../../../../utils/types/ESymbolKind";
import IFunctionSymbol from "../../types/IFunctionSymbol";
import IParameterInfo from "../../types/IParameterInfo";
import TypeUtils from "../utils/TypeUtils";
 
class FunctionCollector {
  /**
   * Collect a function declaration and return an IFunctionSymbol.
   *
   * @param ctx The function declaration context
   * @param sourceFile Source file path
   * @param scopeName Optional scope name for nested functions
   * @param visibility Visibility for scope functions (default "private")
   * @returns The function symbol
   */
  static collect(
    ctx: Parser.FunctionDeclarationContext,
    sourceFile: string,
    scopeName?: string,
    visibility: "public" | "private" = "private",
  ): IFunctionSymbol {
    const name = ctx.IDENTIFIER().getText();
    const fullName = scopeName ? `${scopeName}_${name}` : name;
    const line = ctx.start?.line ?? 0;
 
    // Get return type
    const returnTypeCtx = ctx.type();
    const returnType = TypeUtils.getTypeName(returnTypeCtx, scopeName);
 
    // Collect parameters
    const params = ctx.parameterList()?.parameter() ?? [];
    const parameters = FunctionCollector.collectParameters(params, scopeName);
 
    // Generate signature for overload detection
    const paramTypes = parameters.map((p) => p.type);
    const signature = `${returnType} ${fullName}(${paramTypes.join(", ")})`;
 
    return {
      name: fullName,
      parent: scopeName,
      sourceFile,
      sourceLine: line,
      sourceLanguage: ESourceLanguage.CNext,
      isExported: visibility === "public",
      kind: ESymbolKind.Function,
      returnType,
      parameters,
      visibility,
      signature,
    };
  }
 
  /**
   * Extract parameter information from parameter contexts.
   */
  private static collectParameters(
    params: Parser.ParameterContext[],
    scopeName?: string,
  ): IParameterInfo[] {
    return params.map((p) => {
      const name = p.IDENTIFIER().getText();
      const typeCtx = p.type();
      const type = TypeUtils.getTypeName(typeCtx, scopeName);
      const isConst = p.constModifier() !== null;
 
      const arrayDims = p.arrayDimension();
      const isArray = arrayDims.length > 0;
 
      // Extract array dimensions as strings (can contain expressions like SIZE)
      const arrayDimensions: string[] = [];
      if (isArray) {
        for (const dim of arrayDims) {
          const text = dim.getText();
          const regex = /\[([^\]]*)\]/;
          const match = regex.exec(text);
          arrayDimensions.push(match ? match[1] : ""); // "" means unbounded
        }
      }
 
      const paramInfo: IParameterInfo = {
        name,
        type,
        isConst,
        isArray,
      };
 
      if (arrayDimensions.length > 0) {
        paramInfo.arrayDimensions = arrayDimensions;
      }
 
      return paramInfo;
    });
  }
}
 
export default FunctionCollector;