c-next 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/index.js +231 -42
  2. package/dist/index.js.map +2 -2
  3. package/package.json +1 -1
  4. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +82 -7
  5. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +92 -0
  6. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +23 -0
  7. package/src/transpiler/logic/symbols/c/collectors/FunctionCollector.ts +11 -5
  8. package/src/transpiler/output/codegen/CodeGenerator.ts +157 -7
  9. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +129 -0
  10. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +30 -5
  11. package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +2 -2
  12. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +2 -2
  13. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +2 -2
  14. package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +4 -4
  15. package/src/transpiler/output/codegen/assignment/handlers/__tests__/AccessPatternHandlers.test.ts +4 -4
  16. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +8 -8
  17. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitmapHandlers.test.ts +5 -5
  18. package/src/transpiler/output/codegen/assignment/handlers/__tests__/RegisterHandlers.test.ts +4 -4
  19. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +8 -1
  20. package/src/transpiler/output/codegen/helpers/ArgumentGenerator.ts +5 -0
  21. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +8 -1
  22. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +11 -0
  23. package/src/transpiler/output/codegen/helpers/VariableModifierBuilder.ts +16 -1
  24. package/src/transpiler/output/codegen/helpers/__tests__/VariableModifierBuilder.test.ts +34 -2
  25. package/src/transpiler/output/codegen/types/TTypeInfo.ts +1 -0
  26. package/src/utils/BitUtils.ts +17 -13
  27. package/src/utils/__tests__/BitUtils.test.ts +56 -56
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { hideBin } from "yargs/helpers";
10
10
  // package.json
11
11
  var package_default = {
12
12
  name: "c-next",
13
- version: "0.2.5",
13
+ version: "0.2.6",
14
14
  description: "A safer C for embedded systems development. Transpiles to clean, readable C.",
15
15
  packageManager: "npm@11.9.0",
16
16
  type: "module",
@@ -115165,13 +115165,17 @@ var _generateCFunctionArg = (e, targetParam, input, orchestrator) => {
115165
115165
  return wrapWithCppEnumCast(argCode, e, targetParam?.baseType, orchestrator);
115166
115166
  }
115167
115167
  let argType = orchestrator.getExpressionType(e);
115168
+ let isPointerVariable = false;
115169
+ const typeInfo = CodeGenState.getVariableTypeInfo(argCode);
115168
115170
  if (!argType && !argCode.startsWith("&")) {
115169
- const typeInfo = CodeGenState.getVariableTypeInfo(argCode);
115170
115171
  if (typeInfo) {
115171
115172
  argType = typeInfo.baseType;
115172
115173
  }
115173
115174
  }
115174
- const needsAddressOf = argType && !argType.endsWith("*") && !argCode.startsWith("&") && !targetParam.isArray && (orchestrator.isStructType(argType) || _parameterExpectsAddressOf(targetParam.baseType, argType, orchestrator));
115175
+ if (typeInfo?.isPointer) {
115176
+ isPointerVariable = true;
115177
+ }
115178
+ const needsAddressOf = argType && !argType.endsWith("*") && !argCode.startsWith("&") && !targetParam.isArray && !isPointerVariable && (orchestrator.isStructType(argType) || _parameterExpectsAddressOf(targetParam.baseType, argType, orchestrator));
115175
115179
  if (needsAddressOf) {
115176
115180
  argCode = `&${argCode}`;
115177
115181
  }
@@ -116861,15 +116865,18 @@ var VariableModifierBuilder = class {
116861
116865
  *
116862
116866
  * @param ctx - Parser context with modifier methods
116863
116867
  * @param inFunctionBody - Whether we're inside a function body (affects extern)
116868
+ * @param hasInitializer - Whether the variable has an initializer (affects extern in C mode)
116869
+ * @param cppMode - Whether we're generating C++ code (affects extern behavior)
116864
116870
  * @returns Modifier strings ready for use in generated code
116865
116871
  * @throws Error if both atomic and volatile are specified
116866
116872
  */
116867
- static build(ctx, inFunctionBody) {
116873
+ static build(ctx, inFunctionBody, hasInitializer = false, cppMode = false) {
116868
116874
  const hasConst = ctx.constModifier?.() ?? false;
116869
116875
  const constMod = hasConst ? "const " : "";
116870
116876
  const atomicMod = ctx.atomicModifier() ? "volatile " : "";
116871
116877
  const volatileMod = ctx.volatileModifier() ? "volatile " : "";
116872
- const externMod = hasConst && !inFunctionBody ? "extern " : "";
116878
+ const needsExtern = hasConst && !inFunctionBody && (cppMode || !hasInitializer);
116879
+ const externMod = needsExtern ? "extern " : "";
116873
116880
  if (ctx.atomicModifier() && ctx.volatileModifier()) {
116874
116881
  const line = ctx.start?.line ?? 0;
116875
116882
  throw new Error(
@@ -118057,16 +118064,17 @@ var ScopeGenerator_default = generateScope;
118057
118064
  // src/utils/BitUtils.ts
118058
118065
  var BitUtils = class _BitUtils {
118059
118066
  /**
118060
- * Convert a boolean expression to an integer (0 or 1).
118067
+ * Convert a boolean expression to an unsigned integer (0U or 1U).
118061
118068
  * Handles literal "true"/"false" and generates ternary for expressions.
118069
+ * Uses unsigned literals for MISRA C:2012 Rule 10.1 compliance.
118062
118070
  *
118063
118071
  * @param expr - The expression to convert
118064
- * @returns C code string representing the integer value
118072
+ * @returns C code string representing the unsigned integer value
118065
118073
  */
118066
118074
  static boolToInt(expr) {
118067
- if (expr === "true") return "1";
118068
- if (expr === "false") return "0";
118069
- return `(${expr} ? 1 : 0)`;
118075
+ if (expr === "true") return "1U";
118076
+ if (expr === "false") return "0U";
118077
+ return `(${expr} ? 1U : 0U)`;
118070
118078
  }
118071
118079
  /**
118072
118080
  * Generate a bit mask for the given width.
@@ -118123,24 +118131,26 @@ var BitUtils = class _BitUtils {
118123
118131
  }
118124
118132
  }
118125
118133
  /**
118126
- * Return the appropriate "1" literal for a given type.
118127
- * Uses "1ULL" for 64-bit types to avoid undefined behavior on large shifts.
118134
+ * Return the appropriate unsigned "1" literal for a given type.
118135
+ * Uses "1ULL" for 64-bit types, "1U" for others.
118136
+ * MISRA C:2012 Rule 10.1 requires unsigned operands for bitwise operations.
118128
118137
  *
118129
118138
  * @param typeName - The C-Next type name (e.g., "u64", "i32")
118130
- * @returns "1ULL" for 64-bit types, "1" otherwise
118139
+ * @returns "1ULL" for 64-bit types, "1U" otherwise
118131
118140
  */
118132
118141
  static oneForType(typeName) {
118133
- return typeName === "u64" || typeName === "i64" ? "1ULL" : "1";
118142
+ return typeName === "u64" || typeName === "i64" ? "1ULL" : "1U";
118134
118143
  }
118135
118144
  /**
118136
- * Format a number as an uppercase hex string (e.g., 255 -> "0xFF").
118145
+ * Format a number as an unsigned uppercase hex string (e.g., 255 -> "0xFFU").
118137
118146
  * Used for generating hex mask literals in generated C code.
118147
+ * Includes U suffix for MISRA C:2012 Rule 10.1 compliance.
118138
118148
  *
118139
118149
  * @param value - The numeric value to format
118140
- * @returns Hex string like "0xFF" or "0x1F"
118150
+ * @returns Hex string like "0xFFU" or "0x1FU"
118141
118151
  */
118142
118152
  static formatHex(value) {
118143
- return `0x${value.toString(16).toUpperCase()}`;
118153
+ return `0x${value.toString(16).toUpperCase()}U`;
118144
118154
  }
118145
118155
  /**
118146
118156
  * Generate code to read a single bit from a value.
@@ -118186,7 +118196,7 @@ var BitUtils = class _BitUtils {
118186
118196
  static singleBitWrite(target, offset, value, targetType) {
118187
118197
  const intValue = _BitUtils.boolToInt(value);
118188
118198
  const is64Bit = targetType === "u64" || targetType === "i64";
118189
- const one = is64Bit ? "1ULL" : "1";
118199
+ const one = is64Bit ? "1ULL" : "1U";
118190
118200
  const valueShift = is64Bit ? `((uint64_t)${intValue} << ${offset})` : `(${intValue} << ${offset})`;
118191
118201
  return `${target} = (${target} & ~(${one} << ${offset})) | ${valueShift};`;
118192
118202
  }
@@ -120870,7 +120880,7 @@ function getBitmapFieldInfo(bitmapType, fieldName, ctx) {
120870
120880
  function generateBitmapWrite(target, fieldInfo, value) {
120871
120881
  const { maskHex } = calculateMask(fieldInfo.width);
120872
120882
  if (fieldInfo.width === 1) {
120873
- return `${target} = (${target} & ~(1 << ${fieldInfo.offset})) | (${BitUtils_default.boolToInt(value)} << ${fieldInfo.offset});`;
120883
+ return `${target} = (${target} & ~(1U << ${fieldInfo.offset})) | (${BitUtils_default.boolToInt(value)} << ${fieldInfo.offset});`;
120874
120884
  } else {
120875
120885
  return `${target} = (${target} & ~(${maskHex} << ${fieldInfo.offset})) | ((${value} & ${maskHex}) << ${fieldInfo.offset});`;
120876
120886
  }
@@ -121123,9 +121133,9 @@ function handleRegisterBit(ctx) {
121123
121133
  bitIndex,
121124
121134
  true
121125
121135
  );
121126
- return `${fullName} = (1 << ${bitIndex});`;
121136
+ return `${fullName} = (1U << ${bitIndex});`;
121127
121137
  }
121128
- return `${fullName} = (${fullName} & ~(1 << ${bitIndex})) | (${BitUtils_default.boolToInt(ctx.generatedValue)} << ${bitIndex});`;
121138
+ return `${fullName} = (${fullName} & ~(1U << ${bitIndex})) | (${BitUtils_default.boolToInt(ctx.generatedValue)} << ${bitIndex});`;
121129
121139
  }
121130
121140
  function handleRegisterBitRange(ctx) {
121131
121141
  AssignmentHandlerUtils_default.validateNoCompoundForBitAccess(
@@ -121191,9 +121201,9 @@ function handleScopedRegisterBit(ctx) {
121191
121201
  bitIndex,
121192
121202
  true
121193
121203
  );
121194
- return `${regName} = (1 << ${bitIndex});`;
121204
+ return `${regName} = (1U << ${bitIndex});`;
121195
121205
  }
121196
- return `${regName} = (${regName} & ~(1 << ${bitIndex})) | (${BitUtils_default.boolToInt(ctx.generatedValue)} << ${bitIndex});`;
121206
+ return `${regName} = (${regName} & ~(1U << ${bitIndex})) | (${BitUtils_default.boolToInt(ctx.generatedValue)} << ${bitIndex});`;
121197
121207
  }
121198
121208
  function handleScopedRegisterBitRange(ctx) {
121199
121209
  AssignmentHandlerUtils_default.validateScopeContext(CodeGenState.currentScope);
@@ -121439,7 +121449,7 @@ function handleStructMemberBit(ctx) {
121439
121449
  validateNotCompound2(ctx);
121440
121450
  const target = gen6().generateAssignmentTarget(ctx.targetCtx);
121441
121451
  const bitIndex = gen6().generateExpression(ctx.subscripts.at(-1));
121442
- const one = "1";
121452
+ const one = "1U";
121443
121453
  const intValue = BitUtils_default.boolToInt(ctx.generatedValue);
121444
121454
  return `${target} = (${target} & ~(${one} << ${bitIndex})) | (${intValue} << ${bitIndex});`;
121445
121455
  }
@@ -121687,9 +121697,9 @@ function handleGlobalRegisterBit(ctx) {
121687
121697
  `Cannot assign false to write-only register bit ${regName}[${bitIndex}]. Use the corresponding CLEAR register to clear bits.`
121688
121698
  );
121689
121699
  }
121690
- return `${regName} = (1 << ${bitIndex});`;
121700
+ return `${regName} = (1U << ${bitIndex});`;
121691
121701
  }
121692
- return `${regName} = (${regName} & ~(1 << ${bitIndex})) | (${BitUtils_default.boolToInt(ctx.generatedValue)} << ${bitIndex});`;
121702
+ return `${regName} = (${regName} & ~(1U << ${bitIndex})) | (${BitUtils_default.boolToInt(ctx.generatedValue)} << ${bitIndex});`;
121693
121703
  }
121694
121704
  function handleMemberChain(ctx) {
121695
121705
  const bitAnalysis = gen9().analyzeMemberChainForBitAccess(ctx.targetCtx);
@@ -122839,6 +122849,9 @@ var ArgumentGenerator = class _ArgumentGenerator {
122839
122849
  if (typeInfo?.isArray && !typeInfo.isString) {
122840
122850
  return id;
122841
122851
  }
122852
+ if (typeInfo?.isPointer) {
122853
+ return id;
122854
+ }
122842
122855
  if (CodeGenState.currentScope) {
122843
122856
  const members = CodeGenState.getScopeMembers(CodeGenState.currentScope);
122844
122857
  if (members?.has(id)) {
@@ -124105,7 +124118,11 @@ var StringDeclHelper = class _StringDeclHelper {
124105
124118
  initValue,
124106
124119
  arrayDims
124107
124120
  );
124108
- return { code: `${decl} = ${finalInitValue};`, handled: true };
124121
+ const suppression = "// cppcheck-suppress misra-c2012-9.3\n// cppcheck-suppress misra-c2012-9.4\n";
124122
+ return {
124123
+ code: `${suppression}${decl} = ${finalInitValue};`,
124124
+ handled: true
124125
+ };
124109
124126
  }
124110
124127
  /**
124111
124128
  * Handle size inference for empty array dimension.
@@ -124581,15 +124598,21 @@ ${assignments}`;
124581
124598
  { generateType: callbacks.generateType }
124582
124599
  );
124583
124600
  }
124601
+ const hasInitializer = ctx.expression() !== null;
124584
124602
  const modifiers = VariableModifierBuilder_default.build(
124585
124603
  ctx,
124586
- CodeGenState.inFunctionBody
124604
+ CodeGenState.inFunctionBody,
124605
+ hasInitializer,
124606
+ CodeGenState.cppMode
124587
124607
  );
124588
124608
  const name = ctx.IDENTIFIER().getText();
124589
124609
  const typeCtx = ctx.type();
124590
124610
  _VariableDeclHelper.validateArrayDeclarationSyntax(ctx, typeCtx, name);
124591
124611
  const type = callbacks.inferVariableType(ctx, name);
124592
124612
  callbacks.trackLocalVariable(ctx, name);
124613
+ if (type.endsWith("*")) {
124614
+ callbacks.markVariableAsPointer(name);
124615
+ }
124593
124616
  const stringResult = StringDeclHelper_default.generateStringDecl(
124594
124617
  typeCtx,
124595
124618
  name,
@@ -129771,6 +129794,7 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
129771
129794
  getExpressionType: (exprCtx) => this.getExpressionType(exprCtx),
129772
129795
  inferVariableType: (varCtx, name) => this._inferVariableType(varCtx, name),
129773
129796
  trackLocalVariable: (varCtx, name) => this._trackLocalVariable(varCtx, name),
129797
+ markVariableAsPointer: (name) => this._markVariableAsPointer(name),
129774
129798
  getStringConcatOperands: (concatCtx) => this._getStringConcatOperands(concatCtx),
129775
129799
  getSubstringOperands: (substrCtx) => this._getSubstringOperands(substrCtx),
129776
129800
  getStringExprCapacity: (exprCode) => this.getStringExprCapacity(exprCode),
@@ -129779,20 +129803,116 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
129779
129803
  }
129780
129804
  /**
129781
129805
  * Issue #696: Infer variable type, handling nullable C pointer types.
129806
+ * Issue #895 Bug B: Infer pointer type from C function return type.
129782
129807
  */
129783
129808
  _inferVariableType(ctx, name) {
129784
- let type = this.generateType(ctx.type());
129785
- if (!name.startsWith("c_") || !ctx.expression()) {
129809
+ const type = this.generateType(ctx.type());
129810
+ if (!ctx.expression()) {
129786
129811
  return type;
129787
129812
  }
129788
- const exprText = ctx.expression().getText();
129789
- for (const funcName of NullCheckAnalyzer_default.getStructPointerFunctions()) {
129790
- if (exprText.includes(`${funcName}(`)) {
129791
- return `${type}*`;
129813
+ const pointerType = this._inferPointerTypeFromFunctionCall(
129814
+ ctx.expression(),
129815
+ type
129816
+ );
129817
+ if (pointerType) {
129818
+ return pointerType;
129819
+ }
129820
+ if (name.startsWith("c_")) {
129821
+ const exprText = ctx.expression().getText();
129822
+ for (const funcName of NullCheckAnalyzer_default.getStructPointerFunctions()) {
129823
+ if (exprText.includes(`${funcName}(`)) {
129824
+ return `${type}*`;
129825
+ }
129792
129826
  }
129793
129827
  }
129794
129828
  return type;
129795
129829
  }
129830
+ /**
129831
+ * Issue #895 Bug B: Infer pointer type from C function return type.
129832
+ * If initializer is a call to a C function that returns T*, and declared
129833
+ * type is T, return T* instead of T.
129834
+ */
129835
+ _inferPointerTypeFromFunctionCall(expr, declaredType) {
129836
+ const funcName = this._extractCFunctionName(expr);
129837
+ if (!funcName) {
129838
+ return null;
129839
+ }
129840
+ const cFunc = CodeGenState.symbolTable?.getCSymbol(funcName);
129841
+ if (cFunc?.kind !== "function") {
129842
+ return null;
129843
+ }
129844
+ const returnType = cFunc.type;
129845
+ if (!returnType.endsWith("*")) {
129846
+ return null;
129847
+ }
129848
+ const returnBaseType = returnType.replace(/\s*\*\s*$/, "").trim();
129849
+ if (returnBaseType === declaredType) {
129850
+ return `${declaredType}*`;
129851
+ }
129852
+ return null;
129853
+ }
129854
+ /**
129855
+ * Extract C function name from expression patterns.
129856
+ * Handles both:
129857
+ * - global.funcName(...) - explicit global access
129858
+ * - funcName(...) - direct call (if funcName is a known C function)
129859
+ * Returns null if expression doesn't match these patterns.
129860
+ */
129861
+ _extractCFunctionName(expr) {
129862
+ const postfix = ExpressionUnwrapper_default.getPostfixExpression(expr);
129863
+ if (!postfix) {
129864
+ return null;
129865
+ }
129866
+ const primary = postfix.primaryExpression();
129867
+ const ops = postfix.postfixOp();
129868
+ if (primary.GLOBAL()) {
129869
+ return this._extractGlobalPatternFuncName(ops);
129870
+ }
129871
+ const identifier = primary.IDENTIFIER();
129872
+ if (identifier) {
129873
+ return this._extractDirectCallFuncName(identifier.getText(), ops);
129874
+ }
129875
+ return null;
129876
+ }
129877
+ /**
129878
+ * Extract function name from global.funcName(...) pattern.
129879
+ */
129880
+ _extractGlobalPatternFuncName(ops) {
129881
+ if (ops.length < 2) {
129882
+ return null;
129883
+ }
129884
+ const memberOp = ops[0];
129885
+ if (!memberOp.IDENTIFIER()) {
129886
+ return null;
129887
+ }
129888
+ const callOp = ops[1];
129889
+ if (!this._isCallOp(callOp)) {
129890
+ return null;
129891
+ }
129892
+ return memberOp.IDENTIFIER().getText();
129893
+ }
129894
+ /**
129895
+ * Extract function name from direct funcName(...) call if it's a C function.
129896
+ */
129897
+ _extractDirectCallFuncName(funcName, ops) {
129898
+ if (ops.length < 1) {
129899
+ return null;
129900
+ }
129901
+ if (!this._isCallOp(ops[0])) {
129902
+ return null;
129903
+ }
129904
+ const cFunc = CodeGenState.symbolTable?.getCSymbol(funcName);
129905
+ if (cFunc?.kind === "function") {
129906
+ return funcName;
129907
+ }
129908
+ return null;
129909
+ }
129910
+ /**
129911
+ * Check if a postfix op is a function call.
129912
+ */
129913
+ _isCallOp(op) {
129914
+ return Boolean(op.argumentList() || op.getText().startsWith("("));
129915
+ }
129796
129916
  /**
129797
129917
  * Issue #696: Track local variable for type registry and const values.
129798
129918
  */
@@ -129813,6 +129933,20 @@ typedef ${callbackInfo.returnType} (*${callbackInfo.typedefName})(${paramList});
129813
129933
  }
129814
129934
  }
129815
129935
  }
129936
+ /**
129937
+ * Issue #895 Bug B: Mark variable as a pointer in the type registry.
129938
+ * Called when type inference detects that a variable should be a pointer
129939
+ * (e.g., initialized from a C function returning T*).
129940
+ */
129941
+ _markVariableAsPointer(name) {
129942
+ const typeInfo = CodeGenState.getVariableTypeInfo(name);
129943
+ if (typeInfo) {
129944
+ CodeGenState.setVariableTypeInfo(name, {
129945
+ ...typeInfo,
129946
+ isPointer: true
129947
+ });
129948
+ }
129949
+ }
129816
129950
  // Issue #792: Methods _handleArrayDeclaration, _getArrayTypeDimension, _parseArrayTypeDimension,
129817
129951
  // _parseFirstArrayDimension, _validateArrayDeclarationSyntax, _extractBaseTypeName,
129818
129952
  // _generateVariableInitializer, _validateIntegerInitializer, _finalizeCppClassAssignments,
@@ -133389,6 +133523,8 @@ var FunctionCollector2 = class _FunctionCollector {
133389
133523
  const parameters = _FunctionCollector._mapParameters(
133390
133524
  DeclaratorUtils_default.extractFunctionParameters(declarator)
133391
133525
  );
133526
+ const hasPointer = declarator.pointer() !== null;
133527
+ const returnType = hasPointer ? `${baseType}*` : baseType;
133392
133528
  return {
133393
133529
  kind: "function",
133394
133530
  name,
@@ -133396,7 +133532,7 @@ var FunctionCollector2 = class _FunctionCollector {
133396
133532
  sourceLine: line,
133397
133533
  sourceLanguage: ESourceLanguage_default.C,
133398
133534
  isExported: !isExtern,
133399
- type: baseType,
133535
+ type: returnType,
133400
133536
  parameters: parameters.length > 0 ? parameters : void 0,
133401
133537
  isDeclaration: true
133402
133538
  };
@@ -137085,6 +137221,8 @@ var FunctionCallAnalyzer = class {
137085
137221
  if (sym?.kind !== "type") return false;
137086
137222
  return "type" in sym && typeof sym.type === "string" && sym.type.includes("(*)");
137087
137223
  }
137224
+ /** Current scope name during callback assignment scanning */
137225
+ scanCurrentScope = null;
137088
137226
  /**
137089
137227
  * Detect functions assigned to C function pointer typedefs.
137090
137228
  * When `PointCallback cb <- my_handler;` is found and PointCallback
@@ -137093,12 +137231,46 @@ var FunctionCallAnalyzer = class {
137093
137231
  collectCallbackCompatibleFunctions(tree) {
137094
137232
  for (const decl of tree.declaration()) {
137095
137233
  const funcDecl = decl.functionDeclaration();
137096
- if (!funcDecl) continue;
137097
- const block = funcDecl.block();
137098
- if (!block) continue;
137099
- this.scanBlockForCallbackAssignments(block);
137234
+ if (funcDecl) {
137235
+ this.scanStandaloneFunctionForCallbacks(funcDecl);
137236
+ continue;
137237
+ }
137238
+ const scopeDecl = decl.scopeDeclaration();
137239
+ if (scopeDecl) {
137240
+ this.scanScopeForCallbacks(scopeDecl);
137241
+ }
137100
137242
  }
137101
137243
  }
137244
+ /**
137245
+ * Scan a standalone function declaration for callback assignments.
137246
+ */
137247
+ scanStandaloneFunctionForCallbacks(funcDecl) {
137248
+ const block = funcDecl.block();
137249
+ if (!block) return;
137250
+ this.scanCurrentScope = null;
137251
+ this.scanBlockForCallbackAssignments(block);
137252
+ }
137253
+ /**
137254
+ * Scan all member functions in a scope for callback assignments (Issue #895).
137255
+ */
137256
+ scanScopeForCallbacks(scopeDecl) {
137257
+ const scopeName = scopeDecl.IDENTIFIER().getText();
137258
+ for (const member of scopeDecl.scopeMember()) {
137259
+ this.scanScopeMemberForCallbacks(member, scopeName);
137260
+ }
137261
+ this.scanCurrentScope = null;
137262
+ }
137263
+ /**
137264
+ * Scan a single scope member for callback assignments.
137265
+ */
137266
+ scanScopeMemberForCallbacks(member, scopeName) {
137267
+ const memberFunc = member.functionDeclaration();
137268
+ if (!memberFunc) return;
137269
+ const block = memberFunc.block();
137270
+ if (!block) return;
137271
+ this.scanCurrentScope = scopeName;
137272
+ this.scanBlockForCallbackAssignments(block);
137273
+ }
137102
137274
  /**
137103
137275
  * Recursively scan all statements in a block for callback typedef assignments.
137104
137276
  */
@@ -137182,13 +137354,30 @@ var FunctionCallAnalyzer = class {
137182
137354
  }
137183
137355
  /**
137184
137356
  * Extract a function reference from an expression context.
137185
- * Matches bare identifiers (e.g., "my_handler") and qualified scope
137186
- * names (e.g., "MyScope.handler").
137357
+ * Matches:
137358
+ * - Bare identifiers: "my_handler"
137359
+ * - Qualified scope names: "MyScope.handler"
137360
+ * - Self-scope reference: "this.handler" (resolved using scanCurrentScope)
137361
+ * - Global scope reference: "global.ScopeName.handler"
137187
137362
  * Returns null if the expression is not a function reference.
137188
137363
  */
137189
137364
  extractFunctionReference(expr) {
137190
137365
  const text = expr.getText();
137191
- if (/^\w+(\.\w+)?$/.test(text)) {
137366
+ const thisPattern = /^this\.(\w+)$/;
137367
+ const thisMatch = thisPattern.exec(text);
137368
+ if (thisMatch) {
137369
+ if (!this.scanCurrentScope) {
137370
+ return null;
137371
+ }
137372
+ return this.scanCurrentScope + "." + thisMatch[1];
137373
+ }
137374
+ const globalPattern = /^global\.(\w+)\.(\w+)$/;
137375
+ const globalMatch = globalPattern.exec(text);
137376
+ if (globalMatch) {
137377
+ return globalMatch[1] + "." + globalMatch[2];
137378
+ }
137379
+ const simplePattern = /^\w+(\.\w+)?$/;
137380
+ if (simplePattern.test(text)) {
137192
137381
  return text;
137193
137382
  }
137194
137383
  return null;