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 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | 44x 44x 4x 40x 40x 40x 40x 40x 40x 1x 39x 78x 39x 40x 40x 40x 977x 48x 929x 48x 48x 40x 39x 39x 39x 39x 893x 8x 8x 885x 8x 8x 877x 39x 39x 838x 39x 39x 39x 78x 78x 78x 78x 78x 36x 78x 78x 32x 32x 5x 27x 16x 16x 5x 9x 9x 9x | /**
* TypedefParamParser - Parses C function pointer typedef signatures
*
* Extracts parameter types from typedef strings like:
* "void (*)(widget_t *, const rect_t *, uint8_t *)"
* "void (*)(Point p)"
*
* Used by Issue #895 to determine if callback params should be pointers or values.
* Used by Issue #914 to resolve callback pointer/const info onto IParameterSymbol.
*/
import IParameterSymbol from "../../../../utils/types/IParameterSymbol";
/**
* Parsed parameter info from a typedef.
*/
interface ITypedefParam {
/** Full type string (e.g., "widget_t *", "const rect_t *", "uint8_t *") */
type: string;
/** Whether this is a pointer type */
isPointer: boolean;
/** Whether this has const qualifier */
isConst: boolean;
/** Base type without pointer/const (e.g., "widget_t", "rect_t", "uint8_t") */
baseType: string;
}
/**
* Parse result for a typedef signature.
*/
interface ITypedefParseResult {
/** Return type */
returnType: string;
/** Parsed parameters */
params: ITypedefParam[];
}
class TypedefParamParser {
/**
* Parse a function pointer typedef type string.
*
* @param typedefType - The type string, e.g., "void (*)(widget_t *, const rect_t *, uint8_t *)"
* @returns Parsed result with return type and parameters, or null if parsing fails
*/
static parse(typedefType: string): ITypedefParseResult | null {
// Expected format: "return_type (*)(param1, param2, ...)"
// Find the (*) marker first
const funcPtrIndex = typedefType.indexOf("(*)");
if (funcPtrIndex === -1) {
return null;
}
const returnType = typedefType.substring(0, funcPtrIndex).trim();
// Find the opening paren after (*)
const afterFuncPtr = typedefType.substring(funcPtrIndex + 3).trim();
Iif (!afterFuncPtr.startsWith("(")) {
return null;
}
// Extract params by finding the matching closing paren
const paramsStr = TypedefParamParser.extractParenContent(afterFuncPtr);
Iif (paramsStr === null) {
return null;
}
// Handle void or empty params
if (!paramsStr || paramsStr === "void") {
return { returnType, params: [] };
}
// Split by comma, handling nested parentheses
const paramStrings = TypedefParamParser.splitParams(paramsStr);
const params = paramStrings.map((p) => TypedefParamParser.parseParam(p));
return { returnType, params };
}
/**
* Extract content between matching parentheses, handling arbitrary nesting.
* @param str - String starting with '('
* @returns Content between outer parens, or null if no match
*/
private static extractParenContent(str: string): string | null {
Iif (!str.startsWith("(")) {
return null;
}
let depth = 0;
for (let i = 0; i < str.length; i++) {
if (str[i] === "(") {
depth++;
} else if (str[i] === ")") {
depth--;
if (depth === 0) {
// Found matching close paren - return content between
return str.substring(1, i);
}
}
}
// No matching close paren found
return null;
}
/**
* Split parameter string by commas, respecting nested parentheses.
*/
private static splitParams(paramsStr: string): string[] {
const params: string[] = [];
let current = "";
let depth = 0;
for (const char of paramsStr) {
if (char === "(") {
depth++;
current += char;
} else if (char === ")") {
depth--;
current += char;
} else if (char === "," && depth === 0) {
params.push(current.trim());
current = "";
} else {
current += char;
}
}
Eif (current.trim()) {
params.push(current.trim());
}
return params;
}
/**
* Parse a single parameter type string.
*/
private static parseParam(paramStr: string): ITypedefParam {
const trimmed = paramStr.trim();
// Check for pointer
const isPointer = trimmed.includes("*");
// Check for const - handles both "const " (with space) and merged forms
// C grammar getText() strips spaces, so "const rect_t*" may appear merged
const isConst = /\bconst\b/.test(trimmed) || trimmed.startsWith("const");
// Extract base type (remove const, *, and param name if present)
let baseType = trimmed
.replaceAll(/\bconst\b/g, "") // Remove const (with word boundary)
.replace(/^const/, "") // Remove const at start (no space case) - only once
.replaceAll("*", "") // Remove pointers
.replaceAll(/\s+/g, " ") // Normalize whitespace
.trim();
// Remove trailing param name if present (e.g., "rect_t area" -> "rect_t")
// Only remove if there are multiple words (space-separated)
if (baseType.includes(" ")) {
baseType = baseType.replace(/\s+\w+$/, "");
}
// Handle struct keyword
Iif (baseType.startsWith("struct ")) {
baseType = baseType.substring(7);
}
return {
type: trimmed,
isPointer,
isConst,
baseType,
};
}
/**
* Get the parameter info at a given index, or null if not found.
*/
private static getParamAt(
typedefType: string,
paramIndex: number,
): ITypedefParam | null {
const parsed = TypedefParamParser.parse(typedefType);
if (!parsed || paramIndex >= parsed.params.length) {
return null;
}
return parsed.params[paramIndex];
}
/**
* Check if a parameter at a given index should be a pointer based on the typedef.
*
* @param typedefType - The typedef type string
* @param paramIndex - The parameter index (0-based)
* @returns true if the param should be a pointer, false for value, null if unknown
*/
static shouldBePointer(
typedefType: string,
paramIndex: number,
): boolean | null {
return (
TypedefParamParser.getParamAt(typedefType, paramIndex)?.isPointer ?? null
);
}
/**
* Check if a parameter at a given index should be const based on the typedef.
*
* @param typedefType - The typedef type string
* @param paramIndex - The parameter index (0-based)
* @returns true if the param should be const, false otherwise, null if unknown
*/
static shouldBeConst(
typedefType: string,
paramIndex: number,
): boolean | null {
return (
TypedefParamParser.getParamAt(typedefType, paramIndex)?.isConst ?? null
);
}
/**
* Resolve callback pointer/const overrides onto an array of IParameterSymbol.
*
* Issue #914: This is the single point where callback typedef info is applied
* to parameters — used by both .c and .h generation paths via IParameterSymbol.
*
* @param params - The original parameter symbols
* @param callbackTypedefType - The typedef type string (e.g., "void (*)(widget_t *, const rect_t *)")
* @returns New array with isCallbackPointer/isCallbackConst resolved
*/
static resolveCallbackParams(
params: readonly IParameterSymbol[],
callbackTypedefType: string,
): IParameterSymbol[] {
return params.map((param, index) => {
const shouldBePointer = TypedefParamParser.shouldBePointer(
callbackTypedefType,
index,
);
const shouldBeConst = TypedefParamParser.shouldBeConst(
callbackTypedefType,
index,
);
return {
...param,
isCallbackPointer: shouldBePointer ?? undefined,
isCallbackConst: shouldBeConst ?? undefined,
};
});
}
}
export default TypedefParamParser;
|