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.
- package/README.md +48 -39
- package/package.json +6 -2
- package/src/analysis/DivisionByZeroAnalyzer.ts +3 -3
- package/src/analysis/FloatModuloAnalyzer.ts +3 -3
- package/src/analysis/FunctionCallAnalyzer.test.ts +5 -6
- package/src/analysis/FunctionCallAnalyzer.ts +4 -10
- package/src/analysis/GrammarCoverageListener.ts +2 -2
- package/src/analysis/InitializationAnalyzer.test.ts +238 -0
- package/src/analysis/InitializationAnalyzer.ts +48 -8
- package/src/analysis/NullCheckAnalyzer.ts +5 -5
- package/src/analysis/ParameterNamingAnalyzer.ts +1 -1
- package/src/analysis/StructFieldAnalyzer.ts +1 -1
- package/src/analysis/types/IBaseAnalysisError.ts +20 -0
- package/src/analysis/types/IDivisionByZeroError.ts +3 -11
- package/src/analysis/types/IFloatModuloError.ts +3 -12
- package/src/analysis/types/IFunctionCallError.ts +3 -9
- package/src/analysis/types/IInitializationError.ts +2 -10
- package/src/analysis/types/INullCheckError.ts +3 -11
- package/src/analysis/types/IParameterNamingError.ts +3 -11
- package/src/analysis/types/IStructFieldError.ts +3 -11
- package/src/analysis/types/__tests__/IBaseAnalysisError.test.ts +180 -0
- package/src/codegen/CodeGenerator.ts +3629 -4659
- package/src/codegen/CommentExtractor.ts +1 -1
- package/src/codegen/HeaderGenerator.test.ts +162 -0
- package/src/codegen/HeaderGenerator.ts +86 -52
- package/src/codegen/TypeRegistrationUtils.ts +110 -0
- package/src/codegen/TypeResolver.ts +8 -4
- package/src/codegen/TypeValidator.ts +104 -24
- package/src/codegen/__tests__/TypeRegistrationUtils.test.ts +222 -0
- package/src/codegen/__tests__/TypeValidator.resolution.test.ts +189 -0
- package/src/codegen/assignment/AssignmentClassifier.ts +552 -0
- package/src/codegen/assignment/AssignmentContextBuilder.ts +165 -0
- package/src/codegen/assignment/AssignmentKind.ts +124 -0
- package/src/codegen/assignment/IAssignmentContext.ts +93 -0
- package/src/codegen/assignment/__tests__/AssignmentClassifier.test.ts +647 -0
- package/src/codegen/assignment/handlers/AccessPatternHandlers.ts +158 -0
- package/src/codegen/assignment/handlers/ArrayHandlers.ts +146 -0
- package/src/codegen/assignment/handlers/BitAccessHandlers.ts +160 -0
- package/src/codegen/assignment/handlers/BitmapHandlers.ts +252 -0
- package/src/codegen/assignment/handlers/IHandlerDeps.ts +161 -0
- package/src/codegen/assignment/handlers/RegisterHandlers.ts +290 -0
- package/src/codegen/assignment/handlers/RegisterUtils.ts +25 -0
- package/src/codegen/assignment/handlers/SimpleHandler.ts +25 -0
- package/src/codegen/assignment/handlers/SpecialHandlers.ts +101 -0
- package/src/codegen/assignment/handlers/StringHandlers.ts +195 -0
- package/src/codegen/assignment/handlers/TAssignmentHandler.ts +15 -0
- package/src/codegen/assignment/handlers/__tests__/RegisterUtils.test.ts +39 -0
- package/src/codegen/assignment/handlers/__tests__/StringHandlers.test.ts +246 -0
- package/src/codegen/assignment/handlers/index.ts +70 -0
- package/src/codegen/assignment/index.ts +42 -0
- package/src/codegen/generators/GeneratorRegistry.ts +12 -3
- package/src/codegen/generators/declarationGenerators/BitmapGenerator.ts +1 -2
- package/src/codegen/generators/declarationGenerators/EnumGenerator.ts +1 -2
- package/src/codegen/generators/declarationGenerators/ScopeGenerator.ts +17 -17
- package/src/codegen/generators/declarationGenerators/StructGenerator.ts +6 -7
- package/src/codegen/generators/expressions/AccessExprGenerator.ts +14 -15
- package/src/codegen/generators/expressions/BinaryExprGenerator.ts +23 -103
- package/src/codegen/generators/expressions/BinaryExprUtils.ts +134 -0
- package/src/codegen/generators/expressions/CallExprGenerator.ts +19 -36
- package/src/codegen/generators/expressions/CallExprUtils.ts +68 -0
- package/src/codegen/generators/expressions/__tests__/BinaryExprUtils.test.ts +261 -0
- package/src/codegen/generators/expressions/__tests__/CallExprUtils.test.ts +122 -0
- package/src/codegen/generators/statements/SwitchGenerator.ts +9 -5
- package/src/codegen/generators/support/HelperGenerator.ts +38 -34
- package/src/codegen/headerGenerators/IHeaderTypeInput.ts +4 -0
- package/src/codegen/headerGenerators/generateBitmapHeader.ts +1 -2
- package/src/codegen/headerGenerators/generateStructHeader.ts +7 -1
- package/src/codegen/headerGenerators/mapType.ts +7 -23
- package/src/codegen/memberAccessChain.test.ts +114 -1
- package/src/codegen/memberAccessChain.ts +69 -5
- package/src/codegen/types/IHeaderOptions.ts +14 -0
- package/src/codegen/types/TYPE_MAP.ts +6 -16
- package/src/constants/TypeMappings.ts +35 -0
- package/src/index.ts +76 -8
- package/src/lib/IncludeDiscovery.ts +10 -8
- package/src/lib/IncludeResolver.ts +19 -11
- package/src/lib/__tests__/parseWithSymbols.test.ts +118 -0
- package/src/lib/__tests__/transpiler.test.ts +132 -0
- package/src/lib/parseWithSymbols.ts +3 -58
- package/src/lib/transpiler.ts +6 -60
- package/src/lib/types/ITranspileError.ts +2 -0
- package/src/pipeline/CNextSourceParser.ts +85 -0
- package/src/pipeline/CacheManager.ts +5 -5
- package/src/pipeline/Pipeline.ts +151 -77
- package/src/pipeline/__tests__/CNextSourceParser.test.ts +85 -0
- package/src/pipeline/__tests__/CacheManager.test.ts +9 -3
- package/src/pipeline/runAnalyzers.ts +3 -1
- package/src/pipeline/types/IPipelineConfig.ts +3 -0
- package/src/preprocessor/Preprocessor.ts +3 -3
- package/src/project/Project.ts +133 -16
- package/src/project/__tests__/Project.test.ts +368 -0
- package/src/project/types/IProjectConfig.ts +6 -0
- package/src/symbol_resolution/CSymbolCollector.ts +29 -34
- package/src/symbol_resolution/CppSymbolCollector.ts +34 -39
- package/src/symbol_resolution/SymbolCollectorContext.ts +68 -0
- package/src/symbol_resolution/SymbolTable.ts +6 -5
- package/src/symbol_resolution/SymbolUtils.ts +4 -4
- package/src/symbol_resolution/__tests__/CppSymbolCollector.test.ts +1081 -0
- package/src/symbol_resolution/__tests__/SymbolCollectorContext.test.ts +290 -0
- package/src/symbol_resolution/__tests__/cppTestHelpers.ts +40 -0
- package/src/symbol_resolution/cnext/__tests__/TSymbolInfoAdapter.test.ts +47 -0
- package/src/symbol_resolution/cnext/adapters/TSymbolAdapter.ts +8 -2
- package/src/symbol_resolution/cnext/adapters/TSymbolInfoAdapter.ts +11 -17
- package/src/symbol_resolution/cnext/collectors/FunctionCollector.ts +2 -1
- package/src/symbol_resolution/cnext/collectors/StructCollector.ts +0 -1
- package/src/symbol_resolution/cnext/utils/TypeUtils.ts +2 -19
- package/src/symbol_resolution/types/ICollectorContext.ts +19 -0
- package/src/utils/BitUtils.test.ts +387 -0
- package/src/utils/BitUtils.ts +198 -0
- package/src/utils/CppNamespaceUtils.test.ts +287 -0
- package/src/utils/CppNamespaceUtils.ts +122 -0
- package/src/utils/FormatUtils.test.ts +116 -0
- package/src/utils/FormatUtils.ts +64 -0
- package/src/utils/StringUtils.test.ts +216 -0
- package/src/utils/StringUtils.ts +188 -0
- package/src/utils/TypeCheckUtils.test.ts +210 -0
- 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
|
|
680
|
-
| --------------------------------------------------------------------- |
|
|
681
|
-
| [ADR-001](docs/decisions/adr-001-assignment-operator.md) | Assignment Operator
|
|
682
|
-
| [ADR-003](docs/decisions/adr-003-static-allocation.md) | Static Allocation
|
|
683
|
-
| [ADR-004](docs/decisions/adr-004-register-bindings.md) | Register Bindings
|
|
684
|
-
| [ADR-006](docs/decisions/adr-006-simplified-references.md) | Simplified References
|
|
685
|
-
| [ADR-007](docs/decisions/adr-007-type-aware-bit-indexing.md) | Type-Aware Bit Indexing
|
|
686
|
-
| [ADR-010](docs/decisions/adr-010-c-interoperability.md) | C Interoperability
|
|
687
|
-
| [ADR-011](docs/decisions/adr-011-vscode-extension.md) | VS Code Extension
|
|
688
|
-
| [ADR-012](docs/decisions/adr-012-static-analysis.md) | Static Analysis
|
|
689
|
-
| [ADR-013](docs/decisions/adr-013-const-qualifier.md) | Const Qualifier
|
|
690
|
-
| [ADR-014](docs/decisions/adr-014-structs.md) | Structs
|
|
691
|
-
| [ADR-015](docs/decisions/adr-015-null-state.md) | Null State
|
|
692
|
-
| [ADR-016](docs/decisions/adr-016-scope.md) | Scope
|
|
693
|
-
| [ADR-017](docs/decisions/adr-017-enums.md) | Enums
|
|
694
|
-
| [ADR-030](docs/decisions/adr-030-forward-declarations.md) | Define-Before-Use
|
|
695
|
-
| [ADR-037](docs/decisions/adr-037-preprocessor.md) | Preprocessor
|
|
696
|
-
| [ADR-043](docs/decisions/adr-043-comments.md) | Comments
|
|
697
|
-
| [ADR-044](docs/decisions/adr-044-primitive-types.md) | Primitive Types
|
|
698
|
-
| [ADR-024](docs/decisions/adr-024-type-casting.md) | Type Casting
|
|
699
|
-
| [ADR-022](docs/decisions/adr-022-conditional-expressions.md) | Conditional Expressions
|
|
700
|
-
| [ADR-025](docs/decisions/adr-025-switch-statements.md) | Switch Statements
|
|
701
|
-
| [ADR-029](docs/decisions/adr-029-function-pointers.md) | Callbacks
|
|
702
|
-
| [ADR-045](docs/decisions/adr-045-string-type.md) | Bounded Strings
|
|
703
|
-
| [ADR-023](docs/decisions/adr-023-sizeof.md) | Sizeof
|
|
704
|
-
| [ADR-027](docs/decisions/adr-027-do-while.md) | Do-While
|
|
705
|
-
| [ADR-032](docs/decisions/adr-032-nested-structs.md) | Nested Structs
|
|
706
|
-
| [ADR-035](docs/decisions/adr-035-array-initializers.md) | Array Initializers
|
|
707
|
-
| [ADR-036](docs/decisions/adr-036-multidimensional-arrays.md) | Multi-dim Arrays
|
|
708
|
-
| [ADR-040](docs/decisions/adr-040-isr-declaration.md) | ISR Type
|
|
709
|
-
| [ADR-034](docs/decisions/adr-034-bit-fields.md) | Bitmap Types
|
|
710
|
-
| [ADR-048](docs/decisions/adr-048-cli-executable.md) | CLI Executable
|
|
711
|
-
| [ADR-049](docs/decisions/adr-049-atomic-types.md) | Atomic Types
|
|
712
|
-
| [ADR-050](docs/decisions/adr-050-critical-sections.md) | Critical Sections
|
|
713
|
-
| [ADR-108](docs/decisions/adr-108-volatile-keyword.md) | Volatile Variables
|
|
714
|
-
| [ADR-046](docs/decisions/adr-046-nullable-c-interop.md) | Nullable C Interop
|
|
715
|
-
| [ADR-047](docs/decisions/adr-047-nullable-types.md) | NULL for C Interop
|
|
716
|
-
| [ADR-052](docs/decisions/adr-052-safe-numeric-literal-generation.md) | Safe Numeric Literals
|
|
717
|
-
| [ADR-053](docs/decisions/adr-053-transpiler-pipeline-architecture.md) | Transpiler Pipeline
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
217
|
-
expect(errors
|
|
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
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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();
|