c-next 0.1.46 → 0.1.48

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 (117) hide show
  1. package/README.md +48 -39
  2. package/package.json +6 -2
  3. package/src/analysis/DivisionByZeroAnalyzer.ts +3 -3
  4. package/src/analysis/FloatModuloAnalyzer.ts +3 -3
  5. package/src/analysis/FunctionCallAnalyzer.test.ts +5 -6
  6. package/src/analysis/FunctionCallAnalyzer.ts +4 -10
  7. package/src/analysis/GrammarCoverageListener.ts +2 -2
  8. package/src/analysis/InitializationAnalyzer.test.ts +238 -0
  9. package/src/analysis/InitializationAnalyzer.ts +48 -8
  10. package/src/analysis/NullCheckAnalyzer.ts +5 -5
  11. package/src/analysis/ParameterNamingAnalyzer.ts +1 -1
  12. package/src/analysis/StructFieldAnalyzer.ts +1 -1
  13. package/src/analysis/types/IBaseAnalysisError.ts +20 -0
  14. package/src/analysis/types/IDivisionByZeroError.ts +3 -11
  15. package/src/analysis/types/IFloatModuloError.ts +3 -12
  16. package/src/analysis/types/IFunctionCallError.ts +3 -9
  17. package/src/analysis/types/IInitializationError.ts +2 -10
  18. package/src/analysis/types/INullCheckError.ts +3 -11
  19. package/src/analysis/types/IParameterNamingError.ts +3 -11
  20. package/src/analysis/types/IStructFieldError.ts +3 -11
  21. package/src/analysis/types/__tests__/IBaseAnalysisError.test.ts +180 -0
  22. package/src/codegen/CodeGenerator.ts +3629 -4659
  23. package/src/codegen/CommentExtractor.ts +1 -1
  24. package/src/codegen/HeaderGenerator.test.ts +162 -0
  25. package/src/codegen/HeaderGenerator.ts +86 -52
  26. package/src/codegen/TypeRegistrationUtils.ts +110 -0
  27. package/src/codegen/TypeResolver.ts +8 -4
  28. package/src/codegen/TypeValidator.ts +104 -24
  29. package/src/codegen/__tests__/TypeRegistrationUtils.test.ts +222 -0
  30. package/src/codegen/__tests__/TypeValidator.resolution.test.ts +189 -0
  31. package/src/codegen/assignment/AssignmentClassifier.ts +552 -0
  32. package/src/codegen/assignment/AssignmentContextBuilder.ts +165 -0
  33. package/src/codegen/assignment/AssignmentKind.ts +124 -0
  34. package/src/codegen/assignment/IAssignmentContext.ts +93 -0
  35. package/src/codegen/assignment/__tests__/AssignmentClassifier.test.ts +647 -0
  36. package/src/codegen/assignment/handlers/AccessPatternHandlers.ts +158 -0
  37. package/src/codegen/assignment/handlers/ArrayHandlers.ts +146 -0
  38. package/src/codegen/assignment/handlers/BitAccessHandlers.ts +160 -0
  39. package/src/codegen/assignment/handlers/BitmapHandlers.ts +252 -0
  40. package/src/codegen/assignment/handlers/IHandlerDeps.ts +161 -0
  41. package/src/codegen/assignment/handlers/RegisterHandlers.ts +290 -0
  42. package/src/codegen/assignment/handlers/RegisterUtils.ts +25 -0
  43. package/src/codegen/assignment/handlers/SimpleHandler.ts +25 -0
  44. package/src/codegen/assignment/handlers/SpecialHandlers.ts +101 -0
  45. package/src/codegen/assignment/handlers/StringHandlers.ts +195 -0
  46. package/src/codegen/assignment/handlers/TAssignmentHandler.ts +15 -0
  47. package/src/codegen/assignment/handlers/__tests__/RegisterUtils.test.ts +39 -0
  48. package/src/codegen/assignment/handlers/__tests__/StringHandlers.test.ts +246 -0
  49. package/src/codegen/assignment/handlers/index.ts +70 -0
  50. package/src/codegen/assignment/index.ts +42 -0
  51. package/src/codegen/generators/GeneratorRegistry.ts +12 -3
  52. package/src/codegen/generators/declarationGenerators/BitmapGenerator.ts +1 -2
  53. package/src/codegen/generators/declarationGenerators/EnumGenerator.ts +1 -2
  54. package/src/codegen/generators/declarationGenerators/ScopeGenerator.ts +17 -17
  55. package/src/codegen/generators/declarationGenerators/StructGenerator.ts +6 -7
  56. package/src/codegen/generators/expressions/AccessExprGenerator.ts +14 -15
  57. package/src/codegen/generators/expressions/BinaryExprGenerator.ts +23 -103
  58. package/src/codegen/generators/expressions/BinaryExprUtils.ts +134 -0
  59. package/src/codegen/generators/expressions/CallExprGenerator.ts +19 -36
  60. package/src/codegen/generators/expressions/CallExprUtils.ts +68 -0
  61. package/src/codegen/generators/expressions/__tests__/BinaryExprUtils.test.ts +261 -0
  62. package/src/codegen/generators/expressions/__tests__/CallExprUtils.test.ts +122 -0
  63. package/src/codegen/generators/statements/SwitchGenerator.ts +9 -5
  64. package/src/codegen/generators/support/HelperGenerator.ts +38 -34
  65. package/src/codegen/headerGenerators/IHeaderTypeInput.ts +4 -0
  66. package/src/codegen/headerGenerators/generateBitmapHeader.ts +1 -2
  67. package/src/codegen/headerGenerators/generateStructHeader.ts +7 -1
  68. package/src/codegen/headerGenerators/mapType.ts +7 -23
  69. package/src/codegen/memberAccessChain.test.ts +114 -1
  70. package/src/codegen/memberAccessChain.ts +69 -5
  71. package/src/codegen/types/IHeaderOptions.ts +14 -0
  72. package/src/codegen/types/TYPE_MAP.ts +6 -16
  73. package/src/constants/TypeMappings.ts +35 -0
  74. package/src/index.ts +76 -8
  75. package/src/lib/IncludeDiscovery.ts +10 -8
  76. package/src/lib/IncludeResolver.ts +19 -11
  77. package/src/lib/__tests__/parseWithSymbols.test.ts +118 -0
  78. package/src/lib/__tests__/transpiler.test.ts +132 -0
  79. package/src/lib/parseWithSymbols.ts +3 -58
  80. package/src/lib/transpiler.ts +6 -60
  81. package/src/lib/types/ITranspileError.ts +2 -0
  82. package/src/pipeline/CNextSourceParser.ts +85 -0
  83. package/src/pipeline/CacheManager.ts +5 -5
  84. package/src/pipeline/Pipeline.ts +151 -77
  85. package/src/pipeline/__tests__/CNextSourceParser.test.ts +85 -0
  86. package/src/pipeline/__tests__/CacheManager.test.ts +9 -3
  87. package/src/pipeline/runAnalyzers.ts +3 -1
  88. package/src/pipeline/types/IPipelineConfig.ts +3 -0
  89. package/src/preprocessor/Preprocessor.ts +3 -3
  90. package/src/project/Project.ts +133 -16
  91. package/src/project/__tests__/Project.test.ts +368 -0
  92. package/src/project/types/IProjectConfig.ts +6 -0
  93. package/src/symbol_resolution/CSymbolCollector.ts +29 -34
  94. package/src/symbol_resolution/CppSymbolCollector.ts +34 -39
  95. package/src/symbol_resolution/SymbolCollectorContext.ts +68 -0
  96. package/src/symbol_resolution/SymbolTable.ts +6 -5
  97. package/src/symbol_resolution/SymbolUtils.ts +4 -4
  98. package/src/symbol_resolution/__tests__/CppSymbolCollector.test.ts +1081 -0
  99. package/src/symbol_resolution/__tests__/SymbolCollectorContext.test.ts +290 -0
  100. package/src/symbol_resolution/__tests__/cppTestHelpers.ts +40 -0
  101. package/src/symbol_resolution/cnext/__tests__/TSymbolInfoAdapter.test.ts +47 -0
  102. package/src/symbol_resolution/cnext/adapters/TSymbolAdapter.ts +8 -2
  103. package/src/symbol_resolution/cnext/adapters/TSymbolInfoAdapter.ts +11 -17
  104. package/src/symbol_resolution/cnext/collectors/FunctionCollector.ts +2 -1
  105. package/src/symbol_resolution/cnext/collectors/StructCollector.ts +0 -1
  106. package/src/symbol_resolution/cnext/utils/TypeUtils.ts +2 -19
  107. package/src/symbol_resolution/types/ICollectorContext.ts +19 -0
  108. package/src/utils/BitUtils.test.ts +387 -0
  109. package/src/utils/BitUtils.ts +198 -0
  110. package/src/utils/CppNamespaceUtils.test.ts +287 -0
  111. package/src/utils/CppNamespaceUtils.ts +122 -0
  112. package/src/utils/FormatUtils.test.ts +116 -0
  113. package/src/utils/FormatUtils.ts +64 -0
  114. package/src/utils/StringUtils.test.ts +216 -0
  115. package/src/utils/StringUtils.ts +188 -0
  116. package/src/utils/TypeCheckUtils.test.ts +210 -0
  117. package/src/utils/TypeCheckUtils.ts +116 -0
package/README.md CHANGED
@@ -654,6 +654,14 @@ pio run -t upload
654
654
 
655
655
  See `examples/blink.cnx` for the complete LED blink example.
656
656
 
657
+ ## Projects Using C-Next
658
+
659
+ | Project | Description |
660
+ | ----------------------------------------- | -------------------------------------------------------------------------- |
661
+ | [OSSM](https://github.com/jlaustill/ossm) | Open-source stroke machine firmware using C-Next for safe embedded control |
662
+
663
+ _Using C-Next in your project? Open an issue to get listed!_
664
+
657
665
  ## Project Structure
658
666
 
659
667
  ```
@@ -676,45 +684,46 @@ Decisions are documented in `/docs/decisions/`:
676
684
 
677
685
  ### Implemented
678
686
 
679
- | ADR | Title | Description |
680
- | --------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------- |
681
- | [ADR-001](docs/decisions/adr-001-assignment-operator.md) | Assignment Operator | `<-` for assignment, `=` for comparison |
682
- | [ADR-003](docs/decisions/adr-003-static-allocation.md) | Static Allocation | No dynamic memory after init |
683
- | [ADR-004](docs/decisions/adr-004-register-bindings.md) | Register Bindings | Type-safe hardware access |
684
- | [ADR-006](docs/decisions/adr-006-simplified-references.md) | Simplified References | Pass by reference, no pointer syntax |
685
- | [ADR-007](docs/decisions/adr-007-type-aware-bit-indexing.md) | Type-Aware Bit Indexing | Integers as bit arrays, `.length` property |
686
- | [ADR-010](docs/decisions/adr-010-c-interoperability.md) | C Interoperability | Unified ANTLR parser architecture |
687
- | [ADR-011](docs/decisions/adr-011-vscode-extension.md) | VS Code Extension | Live C preview with syntax highlighting |
688
- | [ADR-012](docs/decisions/adr-012-static-analysis.md) | Static Analysis | cppcheck integration for generated C |
689
- | [ADR-013](docs/decisions/adr-013-const-qualifier.md) | Const Qualifier | Compile-time const enforcement |
690
- | [ADR-014](docs/decisions/adr-014-structs.md) | Structs | Data containers without methods |
691
- | [ADR-015](docs/decisions/adr-015-null-state.md) | Null State | Zero initialization for all variables |
692
- | [ADR-016](docs/decisions/adr-016-scope.md) | Scope | `this.`/`global.` explicit qualification |
693
- | [ADR-017](docs/decisions/adr-017-enums.md) | Enums | Type-safe enums with C-style casting |
694
- | [ADR-030](docs/decisions/adr-030-forward-declarations.md) | Define-Before-Use | Functions must be defined before called |
695
- | [ADR-037](docs/decisions/adr-037-preprocessor.md) | Preprocessor | Flag-only defines, const for values |
696
- | [ADR-043](docs/decisions/adr-043-comments.md) | Comments | Comment preservation with MISRA compliance |
697
- | [ADR-044](docs/decisions/adr-044-primitive-types.md) | Primitive Types | Fixed-width types with `clamp`/`wrap` overflow |
698
- | [ADR-024](docs/decisions/adr-024-type-casting.md) | Type Casting | Widening implicit, narrowing uses bit indexing |
699
- | [ADR-022](docs/decisions/adr-022-conditional-expressions.md) | Conditional Expressions | Ternary with required parens, boolean condition, no nesting |
700
- | [ADR-025](docs/decisions/adr-025-switch-statements.md) | Switch Statements | Safe switch with braces, `\|\|` syntax, counted `default(n)` |
701
- | [ADR-029](docs/decisions/adr-029-function-pointers.md) | Callbacks | Function-as-Type pattern with nominal typing |
702
- | [ADR-045](docs/decisions/adr-045-string-type.md) | Bounded Strings | `string<N>` with compile-time safety |
703
- | [ADR-023](docs/decisions/adr-023-sizeof.md) | Sizeof | Type/value size queries with safety checks |
704
- | [ADR-027](docs/decisions/adr-027-do-while.md) | Do-While | `do { } while ()` with boolean condition (E0701) |
705
- | [ADR-032](docs/decisions/adr-032-nested-structs.md) | Nested Structs | Named nested structs only (no anonymous) |
706
- | [ADR-035](docs/decisions/adr-035-array-initializers.md) | Array Initializers | `[1, 2, 3]` syntax with `[0*]` fill-all |
707
- | [ADR-036](docs/decisions/adr-036-multidimensional-arrays.md) | Multi-dim Arrays | `arr[i][j]` with compile-time bounds enforcement |
708
- | [ADR-040](docs/decisions/adr-040-isr-declaration.md) | ISR Type | Built-in `ISR` type for `void(void)` function pointers |
709
- | [ADR-034](docs/decisions/adr-034-bit-fields.md) | Bitmap Types | `bitmap8`/`bitmap16`/`bitmap32` for portable bit-packed data |
710
- | [ADR-048](docs/decisions/adr-048-cli-executable.md) | CLI Executable | `cnext` command with smart defaults |
711
- | [ADR-049](docs/decisions/adr-049-atomic-types.md) | Atomic Types | `atomic` keyword with LDREX/STREX or PRIMASK fallback |
712
- | [ADR-050](docs/decisions/adr-050-critical-sections.md) | Critical Sections | `critical { }` blocks with PRIMASK save/restore |
713
- | [ADR-108](docs/decisions/adr-108-volatile-keyword.md) | Volatile Variables | `volatile` keyword prevents compiler optimization |
714
- | [ADR-046](docs/decisions/adr-046-nullable-c-interop.md) | Nullable C Interop | `c_` prefix for nullable C pointer types (supersedes ADR-047) |
715
- | [ADR-047](docs/decisions/adr-047-nullable-types.md) | NULL for C Interop | `NULL` keyword for C stream functions (superseded by ADR-046) |
716
- | [ADR-052](docs/decisions/adr-052-safe-numeric-literal-generation.md) | Safe Numeric Literals | `type_MIN`/`type_MAX` constants + safe hex conversion |
717
- | [ADR-053](docs/decisions/adr-053-transpiler-pipeline-architecture.md) | Transpiler Pipeline | Unified multi-pass pipeline with header symbol extraction |
687
+ | ADR | Title | Description |
688
+ | --------------------------------------------------------------------- | ------------------------- | ------------------------------------------------------------- |
689
+ | [ADR-001](docs/decisions/adr-001-assignment-operator.md) | Assignment Operator | `<-` for assignment, `=` for comparison |
690
+ | [ADR-003](docs/decisions/adr-003-static-allocation.md) | Static Allocation | No dynamic memory after init |
691
+ | [ADR-004](docs/decisions/adr-004-register-bindings.md) | Register Bindings | Type-safe hardware access |
692
+ | [ADR-006](docs/decisions/adr-006-simplified-references.md) | Simplified References | Pass by reference, no pointer syntax |
693
+ | [ADR-007](docs/decisions/adr-007-type-aware-bit-indexing.md) | Type-Aware Bit Indexing | Integers as bit arrays, `.length` property |
694
+ | [ADR-010](docs/decisions/adr-010-c-interoperability.md) | C Interoperability | Unified ANTLR parser architecture |
695
+ | [ADR-011](docs/decisions/adr-011-vscode-extension.md) | VS Code Extension | Live C preview with syntax highlighting |
696
+ | [ADR-012](docs/decisions/adr-012-static-analysis.md) | Static Analysis | cppcheck integration for generated C |
697
+ | [ADR-013](docs/decisions/adr-013-const-qualifier.md) | Const Qualifier | Compile-time const enforcement |
698
+ | [ADR-014](docs/decisions/adr-014-structs.md) | Structs | Data containers without methods |
699
+ | [ADR-015](docs/decisions/adr-015-null-state.md) | Null State | Zero initialization for all variables |
700
+ | [ADR-016](docs/decisions/adr-016-scope.md) | Scope | `this.`/`global.` explicit qualification |
701
+ | [ADR-017](docs/decisions/adr-017-enums.md) | Enums | Type-safe enums with C-style casting |
702
+ | [ADR-030](docs/decisions/adr-030-forward-declarations.md) | Define-Before-Use | Functions must be defined before called |
703
+ | [ADR-037](docs/decisions/adr-037-preprocessor.md) | Preprocessor | Flag-only defines, const for values |
704
+ | [ADR-043](docs/decisions/adr-043-comments.md) | Comments | Comment preservation with MISRA compliance |
705
+ | [ADR-044](docs/decisions/adr-044-primitive-types.md) | Primitive Types | Fixed-width types with `clamp`/`wrap` overflow |
706
+ | [ADR-024](docs/decisions/adr-024-type-casting.md) | Type Casting | Widening implicit, narrowing uses bit indexing |
707
+ | [ADR-022](docs/decisions/adr-022-conditional-expressions.md) | Conditional Expressions | Ternary with required parens, boolean condition, no nesting |
708
+ | [ADR-025](docs/decisions/adr-025-switch-statements.md) | Switch Statements | Safe switch with braces, `\|\|` syntax, counted `default(n)` |
709
+ | [ADR-029](docs/decisions/adr-029-function-pointers.md) | Callbacks | Function-as-Type pattern with nominal typing |
710
+ | [ADR-045](docs/decisions/adr-045-string-type.md) | Bounded Strings | `string<N>` with compile-time safety |
711
+ | [ADR-023](docs/decisions/adr-023-sizeof.md) | Sizeof | Type/value size queries with safety checks |
712
+ | [ADR-027](docs/decisions/adr-027-do-while.md) | Do-While | `do { } while ()` with boolean condition (E0701) |
713
+ | [ADR-032](docs/decisions/adr-032-nested-structs.md) | Nested Structs | Named nested structs only (no anonymous) |
714
+ | [ADR-035](docs/decisions/adr-035-array-initializers.md) | Array Initializers | `[1, 2, 3]` syntax with `[0*]` fill-all |
715
+ | [ADR-036](docs/decisions/adr-036-multidimensional-arrays.md) | Multi-dim Arrays | `arr[i][j]` with compile-time bounds enforcement |
716
+ | [ADR-040](docs/decisions/adr-040-isr-declaration.md) | ISR Type | Built-in `ISR` type for `void(void)` function pointers |
717
+ | [ADR-034](docs/decisions/adr-034-bit-fields.md) | Bitmap Types | `bitmap8`/`bitmap16`/`bitmap32` for portable bit-packed data |
718
+ | [ADR-048](docs/decisions/adr-048-cli-executable.md) | CLI Executable | `cnext` command with smart defaults |
719
+ | [ADR-049](docs/decisions/adr-049-atomic-types.md) | Atomic Types | `atomic` keyword with LDREX/STREX or PRIMASK fallback |
720
+ | [ADR-050](docs/decisions/adr-050-critical-sections.md) | Critical Sections | `critical { }` blocks with PRIMASK save/restore |
721
+ | [ADR-108](docs/decisions/adr-108-volatile-keyword.md) | Volatile Variables | `volatile` keyword prevents compiler optimization |
722
+ | [ADR-046](docs/decisions/adr-046-nullable-c-interop.md) | Nullable C Interop | `c_` prefix for nullable C pointer types (supersedes ADR-047) |
723
+ | [ADR-047](docs/decisions/adr-047-nullable-types.md) | NULL for C Interop | `NULL` keyword for C stream functions (superseded by ADR-046) |
724
+ | [ADR-052](docs/decisions/adr-052-safe-numeric-literal-generation.md) | Safe Numeric Literals | `type_MIN`/`type_MAX` constants + safe hex conversion |
725
+ | [ADR-053](docs/decisions/adr-053-transpiler-pipeline-architecture.md) | Transpiler Pipeline | Unified multi-pass pipeline with header symbol extraction |
726
+ | [ADR-057](docs/decisions/adr-057-implicit-scope-resolution.md) | Implicit Scope Resolution | Bare identifiers resolve local → scope → global |
718
727
 
719
728
  ### Research (v1 Roadmap)
720
729
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c-next",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
4
4
  "description": "A safer C for embedded systems development. Transpiles to clean, readable C.",
5
5
  "main": "src/index.ts",
6
6
  "bin": {
@@ -39,7 +39,11 @@
39
39
  "unit:watch": "vitest",
40
40
  "unit:coverage": "vitest run --coverage",
41
41
  "unit:coverage:html": "vitest run --coverage && echo 'Coverage report: coverage/index.html'",
42
- "test:all": "npm run unit && npm run test:q"
42
+ "test:all": "npm run unit && npm run test:q",
43
+ "duplication": "npx jscpd src/ scripts/ --reporters console",
44
+ "duplication:json": "npx jscpd src/ scripts/ --reporters json --output .jscpd",
45
+ "duplication:sonar": "curl -s 'https://sonarcloud.io/api/measures/component_tree?component=jlaustill_c-next&metricKeys=duplicated_blocks,duplicated_lines&s=metric&metricSort=duplicated_blocks&metricSortFilter=withMeasuresOnly&asc=false&ps=20' | jq -r '.components[] | \"\\(.path)\\t\\(.measures[0].value) blocks\\t\\(.measures[1].value) lines\"'",
46
+ "duplication:all": "echo '=== Local (jscpd) ===' && npm run duplication && echo '\n=== SonarCloud ===' && npm run duplication:sonar"
43
47
  },
44
48
  "keywords": [
45
49
  "c",
@@ -22,7 +22,7 @@ import ParserUtils from "../utils/ParserUtils";
22
22
  * First pass: Collect const declarations that are zero
23
23
  */
24
24
  class ConstZeroCollector extends CNextListener {
25
- private constZeros: Set<string> = new Set();
25
+ private readonly constZeros: Set<string> = new Set();
26
26
 
27
27
  public getConstZeros(): Set<string> {
28
28
  return this.constZeros;
@@ -63,9 +63,9 @@ class ConstZeroCollector extends CNextListener {
63
63
  * Second pass: Detect division by zero (including const identifiers)
64
64
  */
65
65
  class DivisionByZeroListener extends CNextListener {
66
- private analyzer: DivisionByZeroAnalyzer;
66
+ private readonly analyzer: DivisionByZeroAnalyzer;
67
67
  // eslint-disable-next-line @typescript-eslint/lines-between-class-members
68
- private constZeros: Set<string>;
68
+ private readonly constZeros: Set<string>;
69
69
 
70
70
  constructor(analyzer: DivisionByZeroAnalyzer, constZeros: Set<string>) {
71
71
  super();
@@ -22,7 +22,7 @@ import TypeConstants from "../constants/TypeConstants";
22
22
  * First pass: Collect variable declarations with float types
23
23
  */
24
24
  class FloatVariableCollector extends CNextListener {
25
- private floatVars: Set<string> = new Set();
25
+ private readonly floatVars: Set<string> = new Set();
26
26
 
27
27
  public getFloatVars(): Set<string> {
28
28
  return this.floatVars;
@@ -67,10 +67,10 @@ class FloatVariableCollector extends CNextListener {
67
67
  * Second pass: Detect modulo operations with float operands
68
68
  */
69
69
  class FloatModuloListener extends CNextListener {
70
- private analyzer: FloatModuloAnalyzer;
70
+ private readonly analyzer: FloatModuloAnalyzer;
71
71
 
72
72
  // eslint-disable-next-line @typescript-eslint/lines-between-class-members
73
- private floatVars: Set<string>;
73
+ private readonly floatVars: Set<string>;
74
74
 
75
75
  constructor(analyzer: FloatModuloAnalyzer, floatVars: Set<string>) {
76
76
  super();
@@ -197,7 +197,9 @@ describe("FunctionCallAnalyzer", () => {
197
197
  expect(errors[0].message).toContain("called before definition");
198
198
  });
199
199
 
200
- it("should suggest this.name() for unqualified scope calls", () => {
200
+ // ADR-057: With implicit scope resolution, bare function calls to scope functions
201
+ // are now allowed (resolve automatically). This test verifies no error is thrown.
202
+ it("should allow unqualified scope function calls (ADR-057 implicit resolution)", () => {
201
203
  const code = `
202
204
  scope Test {
203
205
  void helper() {
@@ -213,11 +215,8 @@ describe("FunctionCallAnalyzer", () => {
213
215
  const analyzer = new FunctionCallAnalyzer();
214
216
  const errors = analyzer.analyze(tree);
215
217
 
216
- expect(errors).toHaveLength(1);
217
- expect(errors[0].code).toBe("E0422");
218
- expect(errors[0].functionName).toBe("helper");
219
- expect(errors[0].message).toContain("scope");
220
- expect(errors[0].message).toContain("this.helper()");
218
+ // ADR-057: Implicit resolution allows bare scope function calls
219
+ expect(errors).toHaveLength(0);
221
220
  });
222
221
 
223
222
  it("should not suggest this. for truly undefined in scope", () => {
@@ -220,7 +220,7 @@ const STDLIB_FUNCTIONS: Map<string, Set<string>> = new Map([
220
220
  * Listener that walks the parse tree and checks function calls
221
221
  */
222
222
  class FunctionCallListener extends CNextListener {
223
- private analyzer: FunctionCallAnalyzer;
223
+ private readonly analyzer: FunctionCallAnalyzer;
224
224
 
225
225
  /** Current scope name (for member function resolution) */
226
226
  private currentScope: string | null = null;
@@ -477,7 +477,7 @@ class FunctionCallAnalyzer {
477
477
  private isStdlibFunction(name: string): boolean {
478
478
  for (const header of this.includedHeaders) {
479
479
  const funcs = STDLIB_FUNCTIONS.get(header);
480
- if (funcs && funcs.has(name)) {
480
+ if (funcs?.has(name)) {
481
481
  return true;
482
482
  }
483
483
  }
@@ -589,19 +589,13 @@ class FunctionCallAnalyzer {
589
589
  return; // OK - invoking a function pointer variable
590
590
  }
591
591
 
592
+ // ADR-057: Allow implicit scope function calls without this. prefix
592
593
  // Check if this is an unqualified call to a scope function
593
594
  // e.g., calling helper() instead of this.helper() inside a scope
594
595
  if (currentScope) {
595
596
  const qualifiedName = `${currentScope}_${name}`;
596
597
  if (this.definedFunctions.has(qualifiedName)) {
597
- this.errors.push({
598
- code: "E0422",
599
- functionName: name,
600
- line,
601
- column,
602
- message: `'${name}' is a scope function - use 'this.${name}()' to call it`,
603
- });
604
- return;
598
+ return; // OK - implicit resolution will handle it
605
599
  }
606
600
  }
607
601
 
@@ -18,8 +18,8 @@ import {
18
18
  import IGrammarCoverageReport from "./types/IGrammarCoverageReport";
19
19
 
20
20
  class GrammarCoverageListener implements ParseTreeListener {
21
- private parserRuleVisits: Map<string, number> = new Map();
22
- private lexerRuleVisits: Map<string, number> = new Map();
21
+ private readonly parserRuleVisits: Map<string, number> = new Map();
22
+ private readonly lexerRuleVisits: Map<string, number> = new Map();
23
23
  private readonly parserRuleNames: string[];
24
24
  private readonly lexerRuleNames: string[];
25
25
 
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Unit tests for InitializationAnalyzer
3
+ * Issue #503: Tests for C++ class initialization handling
4
+ */
5
+
6
+ import { CharStream, CommonTokenStream } from "antlr4ng";
7
+ import { describe, expect, it } from "vitest";
8
+ import { CNextLexer } from "../antlr_parser/grammar/CNextLexer";
9
+ import { CNextParser } from "../antlr_parser/grammar/CNextParser";
10
+ import InitializationAnalyzer from "./InitializationAnalyzer";
11
+ import SymbolTable from "../symbol_resolution/SymbolTable";
12
+ import ESymbolKind from "../types/ESymbolKind";
13
+ import ESourceLanguage from "../types/ESourceLanguage";
14
+
15
+ /**
16
+ * Parse C-Next source code into an AST
17
+ */
18
+ function parse(source: string) {
19
+ const charStream = CharStream.fromString(source);
20
+ const lexer = new CNextLexer(charStream);
21
+ const tokenStream = new CommonTokenStream(lexer);
22
+ const parser = new CNextParser(tokenStream);
23
+ return parser.program();
24
+ }
25
+
26
+ describe("InitializationAnalyzer", () => {
27
+ // ========================================================================
28
+ // Issue #503: C++ Class Initialization
29
+ // ========================================================================
30
+
31
+ describe("C++ class initialization (Issue #503)", () => {
32
+ it("should not flag C++ class variables as uninitialized", () => {
33
+ const code = `
34
+ void main() {
35
+ CppMessage msg;
36
+ u16 pgn <- msg.pgn;
37
+ }
38
+ `;
39
+ const tree = parse(code);
40
+
41
+ // Create symbol table with C++ class
42
+ const symbolTable = new SymbolTable();
43
+ symbolTable.addSymbol({
44
+ name: "CppMessage",
45
+ kind: ESymbolKind.Class,
46
+ sourceLanguage: ESourceLanguage.Cpp,
47
+ sourceFile: "CppMessage.hpp",
48
+ sourceLine: 1,
49
+ isExported: true,
50
+ });
51
+ // Add the field so the analyzer knows about it
52
+ symbolTable.addStructField("CppMessage", "pgn", "u16");
53
+
54
+ const analyzer = new InitializationAnalyzer();
55
+ analyzer.registerExternalStructFields(
56
+ new Map([["CppMessage", new Set(["pgn"])]]),
57
+ );
58
+
59
+ const errors = analyzer.analyze(tree, symbolTable);
60
+
61
+ // Should have NO errors - C++ class is initialized by constructor
62
+ expect(errors).toHaveLength(0);
63
+ });
64
+
65
+ it("should still flag C-Next structs as uninitialized", () => {
66
+ const code = `
67
+ struct MyStruct {
68
+ u16 value;
69
+ }
70
+ void main() {
71
+ MyStruct s;
72
+ u16 v <- s.value;
73
+ }
74
+ `;
75
+ const tree = parse(code);
76
+
77
+ // No C++ symbols in symbol table - MyStruct is a C-Next struct
78
+ const symbolTable = new SymbolTable();
79
+
80
+ const analyzer = new InitializationAnalyzer();
81
+ const errors = analyzer.analyze(tree, symbolTable);
82
+
83
+ // SHOULD have an error - C-Next struct is NOT initialized
84
+ expect(errors.length).toBeGreaterThan(0);
85
+ expect(errors[0].code).toBe("E0381");
86
+ expect(errors[0].variable).toContain("value");
87
+ });
88
+
89
+ it("should not flag C++ struct variables as uninitialized", () => {
90
+ const code = `
91
+ void main() {
92
+ CppStruct data;
93
+ u32 val <- data.value;
94
+ }
95
+ `;
96
+ const tree = parse(code);
97
+
98
+ // Create symbol table with C++ struct (not class)
99
+ const symbolTable = new SymbolTable();
100
+ symbolTable.addSymbol({
101
+ name: "CppStruct",
102
+ kind: ESymbolKind.Struct,
103
+ sourceLanguage: ESourceLanguage.Cpp,
104
+ sourceFile: "types.hpp",
105
+ sourceLine: 1,
106
+ isExported: true,
107
+ });
108
+ symbolTable.addStructField("CppStruct", "value", "u32");
109
+
110
+ const analyzer = new InitializationAnalyzer();
111
+ analyzer.registerExternalStructFields(
112
+ new Map([["CppStruct", new Set(["value"])]]),
113
+ );
114
+
115
+ const errors = analyzer.analyze(tree, symbolTable);
116
+
117
+ // Should have NO errors - C++ structs also have default constructors
118
+ expect(errors).toHaveLength(0);
119
+ });
120
+
121
+ it("should still flag C struct variables as uninitialized", () => {
122
+ const code = `
123
+ void main() {
124
+ CStruct data;
125
+ u32 val <- data.value;
126
+ }
127
+ `;
128
+ const tree = parse(code);
129
+
130
+ // Create symbol table with C struct (not C++)
131
+ const symbolTable = new SymbolTable();
132
+ symbolTable.addSymbol({
133
+ name: "CStruct",
134
+ kind: ESymbolKind.Struct,
135
+ sourceLanguage: ESourceLanguage.C,
136
+ sourceFile: "types.h",
137
+ sourceLine: 1,
138
+ isExported: true,
139
+ });
140
+ symbolTable.addStructField("CStruct", "value", "u32");
141
+
142
+ const analyzer = new InitializationAnalyzer();
143
+ analyzer.registerExternalStructFields(
144
+ new Map([["CStruct", new Set(["value"])]]),
145
+ );
146
+
147
+ const errors = analyzer.analyze(tree, symbolTable);
148
+
149
+ // SHOULD have an error - C structs don't have constructors
150
+ expect(errors.length).toBeGreaterThan(0);
151
+ expect(errors[0].code).toBe("E0381");
152
+ });
153
+
154
+ it("should work without symbol table (backward compatibility)", () => {
155
+ const code = `
156
+ struct LocalStruct {
157
+ u16 field;
158
+ }
159
+ void main() {
160
+ LocalStruct s;
161
+ u16 f <- s.field;
162
+ }
163
+ `;
164
+ const tree = parse(code);
165
+
166
+ // No symbol table passed - should still work
167
+ const analyzer = new InitializationAnalyzer();
168
+ const errors = analyzer.analyze(tree);
169
+
170
+ // Should have an error for uninitialized local struct
171
+ expect(errors.length).toBeGreaterThan(0);
172
+ expect(errors[0].code).toBe("E0381");
173
+ });
174
+ });
175
+
176
+ // ========================================================================
177
+ // Basic Initialization Checks
178
+ // ========================================================================
179
+
180
+ describe("basic initialization", () => {
181
+ it("should not flag initialized variables", () => {
182
+ const code = `
183
+ void main() {
184
+ u32 x <- 5;
185
+ u32 y <- x;
186
+ }
187
+ `;
188
+ const tree = parse(code);
189
+ const analyzer = new InitializationAnalyzer();
190
+ const errors = analyzer.analyze(tree);
191
+
192
+ expect(errors).toHaveLength(0);
193
+ });
194
+
195
+ it("should flag uninitialized primitive variables", () => {
196
+ const code = `
197
+ void main() {
198
+ u32 x;
199
+ u32 y <- x;
200
+ }
201
+ `;
202
+ const tree = parse(code);
203
+ const analyzer = new InitializationAnalyzer();
204
+ const errors = analyzer.analyze(tree);
205
+
206
+ expect(errors.length).toBeGreaterThan(0);
207
+ expect(errors[0].code).toBe("E0381");
208
+ expect(errors[0].variable).toBe("x");
209
+ });
210
+
211
+ it("should not flag global variables (zero-initialized)", () => {
212
+ const code = `
213
+ u32 globalVar;
214
+ void main() {
215
+ u32 x <- globalVar;
216
+ }
217
+ `;
218
+ const tree = parse(code);
219
+ const analyzer = new InitializationAnalyzer();
220
+ const errors = analyzer.analyze(tree);
221
+
222
+ expect(errors).toHaveLength(0);
223
+ });
224
+
225
+ it("should not flag function parameters", () => {
226
+ const code = `
227
+ void process(u32 param) {
228
+ u32 x <- param;
229
+ }
230
+ `;
231
+ const tree = parse(code);
232
+ const analyzer = new InitializationAnalyzer();
233
+ const errors = analyzer.analyze(tree);
234
+
235
+ expect(errors).toHaveLength(0);
236
+ });
237
+ });
238
+ });
@@ -19,6 +19,9 @@ import IDeclarationInfo from "./types/IDeclarationInfo";
19
19
  import ScopeStack from "./ScopeStack";
20
20
  import ExpressionUtils from "../utils/ExpressionUtils";
21
21
  import ParserUtils from "../utils/ParserUtils";
22
+ import SymbolTable from "../symbol_resolution/SymbolTable";
23
+ import ESourceLanguage from "../types/ESourceLanguage";
24
+ import ESymbolKind from "../types/ESymbolKind";
22
25
 
23
26
  /**
24
27
  * Tracks the initialization state of a variable
@@ -42,10 +45,10 @@ interface IVariableState {
42
45
  * Listener that walks the parse tree and tracks initialization
43
46
  */
44
47
  class InitializationListener extends CNextListener {
45
- private analyzer: InitializationAnalyzer;
48
+ private readonly analyzer: InitializationAnalyzer;
46
49
 
47
50
  /** Stack of saved states before each if statement */
48
- private savedStates: Map<string, IVariableState>[] = [];
51
+ private readonly savedStates: Map<string, IVariableState>[] = [];
49
52
 
50
53
  /** Track when we're inside a function call's argument list */
51
54
  private inFunctionCallArgs: number = 0;
@@ -420,11 +423,14 @@ class InitializationAnalyzer {
420
423
  private scopeStack: ScopeStack<IVariableState> = new ScopeStack();
421
424
 
422
425
  /** Known struct types and their fields */
423
- private structFields: Map<string, Set<string>> = new Map();
426
+ private readonly structFields: Map<string, Set<string>> = new Map();
424
427
 
425
428
  /** Track if we're processing a write target (left side of assignment) */
426
429
  private inWriteContext: boolean = false;
427
430
 
431
+ /** Symbol table for checking C++ types (Issue #503) */
432
+ private symbolTable: SymbolTable | null = null;
433
+
428
434
  /**
429
435
  * Register external struct fields from C/C++ headers
430
436
  * This allows the analyzer to recognize types defined in headers
@@ -439,14 +445,44 @@ class InitializationAnalyzer {
439
445
  }
440
446
  }
441
447
 
448
+ /**
449
+ * Issue #503: Check if a type name is a C++ class/struct
450
+ * C++ classes with default constructors are automatically initialized.
451
+ *
452
+ * @param typeName The type name to check
453
+ * @returns true if the type is from C++ (has constructor-based init)
454
+ */
455
+ private isCppClass(typeName: string): boolean {
456
+ if (!this.symbolTable) {
457
+ return false;
458
+ }
459
+
460
+ const symbols = this.symbolTable.getOverloads(typeName);
461
+ for (const sym of symbols) {
462
+ if (sym.sourceLanguage === ESourceLanguage.Cpp) {
463
+ // C++ classes and structs have default constructors
464
+ if (sym.kind === ESymbolKind.Struct || sym.kind === ESymbolKind.Class) {
465
+ return true;
466
+ }
467
+ }
468
+ }
469
+
470
+ return false;
471
+ }
472
+
442
473
  /**
443
474
  * Analyze a parsed program for initialization errors
444
475
  * @param tree The parsed program AST
476
+ * @param symbolTable Optional symbol table for C++ type detection
445
477
  * @returns Array of initialization errors
446
478
  */
447
- public analyze(tree: Parser.ProgramContext): IInitializationError[] {
479
+ public analyze(
480
+ tree: Parser.ProgramContext,
481
+ symbolTable?: SymbolTable,
482
+ ): IInitializationError[] {
448
483
  this.errors = [];
449
484
  this.scopeStack = new ScopeStack();
485
+ this.symbolTable = symbolTable ?? null;
450
486
  // Don't clear structFields - external fields may have been registered
451
487
 
452
488
  // First pass: collect struct definitions
@@ -576,14 +612,18 @@ class InitializationAnalyzer {
576
612
  ? this.structFields.get(typeName)!
577
613
  : new Set<string>();
578
614
 
615
+ // Issue #503: C++ classes with default constructors are automatically initialized
616
+ const isCppClassType = typeName !== null && this.isCppClass(typeName);
617
+ const isInitialized = hasInitializer || isCppClassType;
618
+
579
619
  const state: IVariableState = {
580
620
  declaration: { name, line, column },
581
- initialized: hasInitializer,
621
+ initialized: isInitialized,
582
622
  typeName,
583
623
  isStruct,
584
624
  isStringType,
585
- // If initialized with full struct initializer, all fields are initialized
586
- initializedFields: hasInitializer ? new Set(fields) : new Set(),
625
+ // If initialized with full struct initializer or C++ class, all fields are initialized
626
+ initializedFields: isInitialized ? new Set(fields) : new Set(),
587
627
  };
588
628
 
589
629
  this.scopeStack.declare(name, state);
@@ -648,7 +688,7 @@ class InitializationAnalyzer {
648
688
  if (state.isStruct && state.typeName) {
649
689
  // Struct type: check if this is a real field
650
690
  const structFields = this.structFields.get(state.typeName);
651
- if (structFields && structFields.has(field)) {
691
+ if (structFields?.has(field)) {
652
692
  // This is a real struct field - check initialization
653
693
  if (!state.initializedFields.has(field)) {
654
694
  this.addError(
@@ -201,7 +201,7 @@ interface INullCheckScope {
201
201
  * Listener that walks the parse tree and checks NULL usage
202
202
  */
203
203
  class NullCheckListener extends CNextListener {
204
- private analyzer: NullCheckAnalyzer;
204
+ private readonly analyzer: NullCheckAnalyzer;
205
205
 
206
206
  /** Whether we're currently inside an equality comparison (= or !=) */
207
207
  private inEqualityComparison = false;
@@ -226,20 +226,20 @@ class NullCheckListener extends CNextListener {
226
226
  private currentScope: INullCheckScope | null = null;
227
227
 
228
228
  /** Stack of if-statement info for tracking nested conditions */
229
- private ifStack: Array<{
229
+ private readonly ifStack: Array<{
230
230
  varName: string | null;
231
231
  isNullCheck: boolean;
232
232
  hasReturn: boolean;
233
233
  }> = [];
234
234
 
235
235
  /** Track if we're in the body of an if statement (first statement) */
236
- private inIfBody = false;
236
+ private readonly inIfBody = false;
237
237
 
238
238
  /** Track the current if-statement context for body detection */
239
239
  private currentIfCtx: Parser.IfStatementContext | null = null;
240
240
 
241
241
  /** Stack of while-statement info for tracking nested while conditions */
242
- private whileStack: Array<{
242
+ private readonly whileStack: Array<{
243
243
  varName: string | null;
244
244
  isNotNullCheck: boolean;
245
245
  }> = [];
@@ -629,7 +629,7 @@ class NullCheckListener extends CNextListener {
629
629
  // Check if argument is a simple c_ prefixed variable
630
630
  if (/^c_[a-zA-Z_]\w*$/.test(argText)) {
631
631
  const varState = this.lookupVariable(argText);
632
- if (varState && varState.state === NullCheckState.Unchecked) {
632
+ if (varState?.state === NullCheckState.Unchecked) {
633
633
  const argLine = arg.start?.line ?? line;
634
634
  const argColumn = arg.start?.column ?? 0;
635
635
  this.analyzer.reportMissingNullCheckBeforeUse(
@@ -47,7 +47,7 @@ function formatParameterNamingError(
47
47
  * Listener that walks the parse tree to find parameter naming violations
48
48
  */
49
49
  class ParameterNamingListener extends CNextListener {
50
- private analyzer: ParameterNamingAnalyzer;
50
+ private readonly analyzer: ParameterNamingAnalyzer;
51
51
 
52
52
  constructor(analyzer: ParameterNamingAnalyzer) {
53
53
  super();