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 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | 403x 174x 142x 48x 22x 10x 6x 1x 8x 174x 174x 174x 174x 106x 174x 174x 174x 174x 142x 142x 142x 142x 21x 142x 106x 5x 106x 101x 5x 5x 2x 3x 48x 48x 48x 22x 22x 22x 10x 10x 10x 6x 6x 6x 1x 6x 1x | /**
* HeaderSymbolAdapter - Converts TSymbol to IHeaderSymbol.
*
* ADR-055 Phase 7: This adapter converts the TSymbol discriminated union
* to IHeaderSymbol for header generation.
*/
import TSymbol from "../../../types/symbols/TSymbol";
import IHeaderSymbol from "../types/IHeaderSymbol";
import IParameterSymbol from "../../../../utils/types/IParameterSymbol";
import TypeResolver from "../../../../utils/TypeResolver";
import ScopeUtils from "../../../../utils/ScopeUtils";
import type IScopeSymbol from "../../../types/symbols/IScopeSymbol";
import CodeGenState from "../../../state/CodeGenState";
import type TType from "../../../types/TType";
/**
* Adapter to convert TSymbol to IHeaderSymbol
*/
class HeaderSymbolAdapter {
/**
* Convert a TSymbol to IHeaderSymbol
*/
static fromTSymbol(symbol: TSymbol): IHeaderSymbol {
switch (symbol.kind) {
case "function":
return HeaderSymbolAdapter.convertFunction(symbol);
case "variable":
return HeaderSymbolAdapter.convertVariable(symbol);
case "struct":
return HeaderSymbolAdapter.convertStruct(symbol);
case "enum":
return HeaderSymbolAdapter.convertEnum(symbol);
case "bitmap":
return HeaderSymbolAdapter.convertBitmap(symbol);
case "register":
return HeaderSymbolAdapter.convertRegister(symbol);
case "scope":
return HeaderSymbolAdapter.convertScope(symbol);
}
}
/**
* Convert an array of TSymbols to IHeaderSymbols
*/
static fromTSymbols(symbols: TSymbol[]): IHeaderSymbol[] {
return symbols.map((s) => HeaderSymbolAdapter.fromTSymbol(s));
}
// ========================================================================
// Private conversion methods for each TSymbol kind
// ========================================================================
private static convertFunction(
func: import("../../../types/symbols/IFunctionSymbol").default,
): IHeaderSymbol {
// Convert TType return type to string
const returnTypeStr = TypeResolver.getTypeName(func.returnType);
// Get transpiled C name (scope-prefixed)
const cName = ScopeUtils.getTranspiledCName(func);
const isGlobal = ScopeUtils.isGlobalScope(func.scope);
// ADR-057: type names arrive already scope-qualified from the symbol
// layer (CNextResolver pre-pass), so no qualification is needed here.
const parameters: IParameterSymbol[] = func.parameters.map((p) => {
return {
name: p.name,
type: TypeResolver.getTypeName(p.type),
isConst: p.isConst,
isArray: p.isArray,
arrayDimensions: HeaderSymbolAdapter.headerArrayDimensions(p),
isAutoConst: p.isAutoConst,
};
});
// Build signature with return type and param types
const qualifiedReturn = returnTypeStr;
const paramTypes = parameters.map((p) => p.type);
const signature = `${qualifiedReturn} ${cName}(${paramTypes.join(", ")})`;
return {
name: cName,
kind: "function",
type: qualifiedReturn,
isExported: func.isExported,
parameters,
signature,
parent: isGlobal ? undefined : func.scope.name,
sourceFile: func.sourceFile,
sourceLine: func.sourceLine,
};
}
private static convertVariable(
variable: import("../../../types/symbols/IVariableSymbol").default,
): IHeaderSymbol {
// Get transpiled C name (scope-prefixed)
const cName = ScopeUtils.getTranspiledCName(variable);
const isGlobal = ScopeUtils.isGlobalScope(variable.scope);
// ADR-057: the symbol layer already qualified scope-local type names.
const typeStr = TypeResolver.getTypeName(variable.type);
// Convert dimensions to strings and resolve qualified enum access
const arrayDimensions = variable.arrayDimensions?.map((d) =>
typeof d === "number"
? String(d)
: HeaderSymbolAdapter.resolveArrayDimension(d, variable.scope),
);
return {
name: cName,
kind: "variable",
type: typeStr,
isExported: variable.isExported,
isConst: variable.isConst,
isAtomic: variable.isAtomic,
isVolatile: variable.isVolatile,
isArray: variable.isArray,
arrayDimensions,
parent: isGlobal ? undefined : variable.scope.name,
sourceFile: variable.sourceFile,
sourceLine: variable.sourceLine,
};
}
/**
* A parameter's array dimensions as the C declaration needs them.
*
* A bounded string array carries its capacity as the innermost dimension --
* `string<32>[5]` is `char[5][33]`. ParameterSignatureBuilder documents that
* "dimensions include capacity" and the .c path supplies it; the header did
* not, so it declared `char arr[5]` against a `char arr[5][33]` definition
* (#1164).
*/
private static headerArrayDimensions(parameter: {
readonly type: TType;
readonly arrayDimensions?: ReadonlyArray<number | string>;
}): string[] | undefined {
const dimensions = parameter.arrayDimensions?.map((d) =>
typeof d === "number"
? String(d)
: HeaderSymbolAdapter.resolveConstDimension(d),
);
if (!dimensions) {
return undefined;
}
const capacityMatch = /^string<(\d+)>$/.exec(
TypeResolver.getTypeName(parameter.type),
);
if (!capacityMatch) {
return dimensions;
}
// Whether the capacity is present is structural, not something to infer
// from the trailing value: a guard comparing it to `capacity + 1` misfires
// for any `string<N>[N+1]` and silently declares a different type than the
// .c defines. IParameterSymbol.arrayDimensions never carries the capacity --
// FunctionCollector.collectParameters records only the declared dimensions --
// so it is always appended here.
return [...dimensions, String(Number.parseInt(capacityMatch[1], 10) + 1)];
}
/**
* Resolve a parameter's array dimension that names a `const`.
*
* C-Next resolves const-sized arrays to their value rather than emitting a C
* VLA, so the implementation writes `uint8_t grid[6][4]`. The header kept the
* source text and wrote `grid[SIZE][4]`, which is a different declaration --
* and in C++ not a constant expression at all, since SIZE is an `extern
* const` there (#1164).
*
* Enum-qualified dimensions keep their own resolution path.
*/
private static resolveConstDimension(dimension: string): string {
const constValue = CodeGenState.constValues.get(dimension);
return constValue === undefined ? dimension : String(constValue);
}
private static convertStruct(
struct: import("../../../types/symbols/IStructSymbol").default,
): IHeaderSymbol {
// Get transpiled C name (scope-prefixed)
const cName = ScopeUtils.getTranspiledCName(struct);
const isGlobal = ScopeUtils.isGlobalScope(struct.scope);
return {
name: cName,
kind: "struct",
isExported: struct.isExported,
parent: isGlobal ? undefined : struct.scope.name,
sourceFile: struct.sourceFile,
sourceLine: struct.sourceLine,
};
}
private static convertEnum(
enumSym: import("../../../types/symbols/IEnumSymbol").default,
): IHeaderSymbol {
// Get transpiled C name (scope-prefixed)
const cName = ScopeUtils.getTranspiledCName(enumSym);
const isGlobal = ScopeUtils.isGlobalScope(enumSym.scope);
return {
name: cName,
kind: "enum",
isExported: enumSym.isExported,
parent: isGlobal ? undefined : enumSym.scope.name,
sourceFile: enumSym.sourceFile,
sourceLine: enumSym.sourceLine,
};
}
private static convertBitmap(
bitmap: import("../../../types/symbols/IBitmapSymbol").default,
): IHeaderSymbol {
// Get transpiled C name (scope-prefixed)
const cName = ScopeUtils.getTranspiledCName(bitmap);
const isGlobal = ScopeUtils.isGlobalScope(bitmap.scope);
return {
name: cName,
kind: "bitmap",
type: bitmap.backingType,
isExported: bitmap.isExported,
parent: isGlobal ? undefined : bitmap.scope.name,
sourceFile: bitmap.sourceFile,
sourceLine: bitmap.sourceLine,
};
}
private static convertRegister(
register: import("../../../types/symbols/IRegisterSymbol").default,
): IHeaderSymbol {
// Get transpiled C name (scope-prefixed)
const cName = ScopeUtils.getTranspiledCName(register);
const isGlobal = ScopeUtils.isGlobalScope(register.scope);
return {
name: cName,
kind: "register",
isExported: register.isExported,
parent: isGlobal ? undefined : register.scope.name,
sourceFile: register.sourceFile,
sourceLine: register.sourceLine,
};
}
private static convertScope(
scope: import("../../../types/symbols/IScopeSymbol").default,
): IHeaderSymbol {
return {
name: scope.name,
kind: "scope",
isExported: scope.isExported,
sourceFile: scope.sourceFile,
sourceLine: scope.sourceLine,
};
}
/**
* Convert an array dimension string to C-compatible format.
*
* The dimension arrives as written in C-Next SOURCE (`State.COUNT`,
* `this.State.COUNT`, `global.EColor.COUNT`), not as a generated C name, so it
* has to be resolved the same way the `.c` path resolves it — otherwise the
* header and the implementation derive different names for the same array and
* the header does not compile (#1117 review).
*
* Sharing `QualifiedCName.fromParts([])` is not sufficient on its own: both sides must
* also agree on *what to join*. A bare `State.COUNT` written inside `scope Motor`
* refers to `Motor.State.COUNT` and must become `Motor__State__COUNT`, while a
* top-level `EColor.COUNT` must stay `EColor__COUNT`.
*
* @param dim - Dimension as written in source; may be a qualified enum access
* @param scope - Scope declaring the variable, or the global scope at file scope
* @returns C-compatible dimension string
* @example resolveArrayDimension("EColor.COUNT", global) => "EColor__COUNT"
* @example resolveArrayDimension("State.COUNT", Motor) => "Motor__State__COUNT"
* @example resolveArrayDimension("this.State.COUNT", Motor) => "Motor__State__COUNT"
* @example resolveArrayDimension("global.EColor.COUNT", Motor) => "EColor__COUNT"
* @example resolveArrayDimension("10", Motor) => "10"
*/
private static resolveArrayDimension(
dim: string,
scope: IScopeSymbol | null,
): string {
// Issue #1127: the rule itself lives on ScopeUtils so the struct-field path
// applies the same one. This wrapper only binds the predicate.
return ScopeUtils.resolveDimensionName(dim, scope, (qualified: string) =>
CodeGenState.isKnownEnum(qualified),
);
}
}
export default HeaderSymbolAdapter;
|