c-next 0.2.16 → 0.2.17
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 +16 -0
- package/dist/index.js +1347 -414
- package/dist/index.js.map +3 -3
- package/grammar/CNext.g4 +4 -0
- package/package.json +5 -1
- package/src/transpiler/Transpiler.ts +90 -22
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +57 -10
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
- package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -1
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +154 -86
- package/src/transpiler/logic/symbols/SymbolTable.ts +17 -2
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +6 -4
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
- package/src/transpiler/output/codegen/CodeGenerator.ts +151 -94
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +150 -34
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +11 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
- package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +48 -43
- package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +227 -32
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
- package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +268 -1
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
- package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
- package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +75 -0
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
- package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
- package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
- package/src/transpiler/state/CodeGenState.ts +71 -6
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/types/IParameterSymbol.ts +1 -0
|
@@ -822,7 +822,159 @@ describe("InitializationAnalyzer", () => {
|
|
|
822
822
|
});
|
|
823
823
|
|
|
824
824
|
// ========================================================================
|
|
825
|
-
// Group I:
|
|
825
|
+
// Group I: Compound Assignment (Issue #1012)
|
|
826
|
+
// ========================================================================
|
|
827
|
+
|
|
828
|
+
describe("compound assignment (Issue #1012)", () => {
|
|
829
|
+
it("should flag compound assignment on uninitialized variable", () => {
|
|
830
|
+
const code = `
|
|
831
|
+
void main() {
|
|
832
|
+
u32 sum;
|
|
833
|
+
sum +<- 5;
|
|
834
|
+
}
|
|
835
|
+
`;
|
|
836
|
+
const tree = parse(code);
|
|
837
|
+
const analyzer = new InitializationAnalyzer();
|
|
838
|
+
const errors = analyzer.analyze(tree);
|
|
839
|
+
|
|
840
|
+
expect(errors).toHaveLength(1);
|
|
841
|
+
expect(errors[0].code).toBe("E0381");
|
|
842
|
+
expect(errors[0].variable).toBe("sum");
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
it("should not flag compound assignment on initialized variable", () => {
|
|
846
|
+
const code = `
|
|
847
|
+
void main() {
|
|
848
|
+
u32 sum <- 0;
|
|
849
|
+
sum +<- 5;
|
|
850
|
+
}
|
|
851
|
+
`;
|
|
852
|
+
const tree = parse(code);
|
|
853
|
+
const analyzer = new InitializationAnalyzer();
|
|
854
|
+
const errors = analyzer.analyze(tree);
|
|
855
|
+
|
|
856
|
+
expect(errors).toHaveLength(0);
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
it("should flag compound assignment on uninitialized struct field", () => {
|
|
860
|
+
const code = `
|
|
861
|
+
struct Point {
|
|
862
|
+
u32 x;
|
|
863
|
+
u32 y;
|
|
864
|
+
}
|
|
865
|
+
void main() {
|
|
866
|
+
Point p;
|
|
867
|
+
p.x +<- 10;
|
|
868
|
+
}
|
|
869
|
+
`;
|
|
870
|
+
const tree = parse(code);
|
|
871
|
+
const analyzer = new InitializationAnalyzer();
|
|
872
|
+
const errors = analyzer.analyze(tree);
|
|
873
|
+
|
|
874
|
+
expect(errors).toHaveLength(1);
|
|
875
|
+
expect(errors[0].code).toBe("E0381");
|
|
876
|
+
expect(errors[0].variable).toContain("p.x");
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
it("should not flag compound assignment on initialized struct field", () => {
|
|
880
|
+
const code = `
|
|
881
|
+
struct Point {
|
|
882
|
+
u32 x;
|
|
883
|
+
u32 y;
|
|
884
|
+
}
|
|
885
|
+
void main() {
|
|
886
|
+
Point p <- {x: 10, y: 20};
|
|
887
|
+
p.x +<- 5;
|
|
888
|
+
}
|
|
889
|
+
`;
|
|
890
|
+
const tree = parse(code);
|
|
891
|
+
const analyzer = new InitializationAnalyzer();
|
|
892
|
+
const errors = analyzer.analyze(tree);
|
|
893
|
+
|
|
894
|
+
expect(errors).toHaveLength(0);
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
it("should flag all compound operators on uninitialized variable", () => {
|
|
898
|
+
const operators = [
|
|
899
|
+
"+<-",
|
|
900
|
+
"-<-",
|
|
901
|
+
"*<-",
|
|
902
|
+
"/<-",
|
|
903
|
+
"%<-",
|
|
904
|
+
"&<-",
|
|
905
|
+
"|<-",
|
|
906
|
+
"^<-",
|
|
907
|
+
"<<<-",
|
|
908
|
+
">><-",
|
|
909
|
+
];
|
|
910
|
+
|
|
911
|
+
for (const op of operators) {
|
|
912
|
+
const code = `
|
|
913
|
+
void main() {
|
|
914
|
+
u32 x;
|
|
915
|
+
x ${op} 1;
|
|
916
|
+
}
|
|
917
|
+
`;
|
|
918
|
+
const tree = parse(code);
|
|
919
|
+
const analyzer = new InitializationAnalyzer();
|
|
920
|
+
const errors = analyzer.analyze(tree);
|
|
921
|
+
|
|
922
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
923
|
+
expect(errors[0].code).toBe("E0381");
|
|
924
|
+
expect(errors[0].variable).toBe("x");
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
it("should not flag simple assignment on uninitialized variable", () => {
|
|
929
|
+
const code = `
|
|
930
|
+
void main() {
|
|
931
|
+
u32 x;
|
|
932
|
+
x <- 5;
|
|
933
|
+
u32 y <- x;
|
|
934
|
+
}
|
|
935
|
+
`;
|
|
936
|
+
const tree = parse(code);
|
|
937
|
+
const analyzer = new InitializationAnalyzer();
|
|
938
|
+
const errors = analyzer.analyze(tree);
|
|
939
|
+
|
|
940
|
+
// Simple assignment initializes the variable, so no error
|
|
941
|
+
expect(errors).toHaveLength(0);
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
it("should flag compound assignment on array element with uninitialized array", () => {
|
|
945
|
+
const code = `
|
|
946
|
+
void main() {
|
|
947
|
+
u32[4] arr;
|
|
948
|
+
arr[0] +<- 5;
|
|
949
|
+
}
|
|
950
|
+
`;
|
|
951
|
+
const tree = parse(code);
|
|
952
|
+
const analyzer = new InitializationAnalyzer();
|
|
953
|
+
const errors = analyzer.analyze(tree);
|
|
954
|
+
|
|
955
|
+
// Array is uninitialized, reading arr[0] for compound assignment is an error
|
|
956
|
+
expect(errors).toHaveLength(1);
|
|
957
|
+
expect(errors[0].code).toBe("E0381");
|
|
958
|
+
expect(errors[0].variable).toBe("arr");
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
it("should not flag compound assignment on array element with initialized array", () => {
|
|
962
|
+
const code = `
|
|
963
|
+
void main() {
|
|
964
|
+
u32[4] arr <- [1, 2, 3, 4];
|
|
965
|
+
arr[0] +<- 5;
|
|
966
|
+
}
|
|
967
|
+
`;
|
|
968
|
+
const tree = parse(code);
|
|
969
|
+
const analyzer = new InitializationAnalyzer();
|
|
970
|
+
const errors = analyzer.analyze(tree);
|
|
971
|
+
|
|
972
|
+
expect(errors).toHaveLength(0);
|
|
973
|
+
});
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
// ========================================================================
|
|
977
|
+
// Group J: C++ Non-Struct Symbol
|
|
826
978
|
// ========================================================================
|
|
827
979
|
|
|
828
980
|
describe("C++ non-struct symbol", () => {
|
|
@@ -854,4 +1006,237 @@ describe("InitializationAnalyzer", () => {
|
|
|
854
1006
|
expect(errors[0].variable).toBe("e");
|
|
855
1007
|
});
|
|
856
1008
|
});
|
|
1009
|
+
|
|
1010
|
+
// ========================================================================
|
|
1011
|
+
// Issue #1019: Scope Member Initialization
|
|
1012
|
+
// ========================================================================
|
|
1013
|
+
|
|
1014
|
+
describe("scope member initialization (Issue #1019)", () => {
|
|
1015
|
+
it("should allow reading scope member with inline initializer", () => {
|
|
1016
|
+
const code = `
|
|
1017
|
+
scope S {
|
|
1018
|
+
u32 value <- 42;
|
|
1019
|
+
public u32 get() { return value; }
|
|
1020
|
+
}
|
|
1021
|
+
void main() {
|
|
1022
|
+
u32 v <- S.get();
|
|
1023
|
+
}
|
|
1024
|
+
`;
|
|
1025
|
+
const tree = parse(code);
|
|
1026
|
+
const analyzer = new InitializationAnalyzer();
|
|
1027
|
+
const errors = analyzer.analyze(tree);
|
|
1028
|
+
|
|
1029
|
+
expect(errors).toHaveLength(0);
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
it("should allow reading scope member assigned in another function", () => {
|
|
1033
|
+
const code = `
|
|
1034
|
+
scope S {
|
|
1035
|
+
u32 value;
|
|
1036
|
+
public void init() { value <- 10; }
|
|
1037
|
+
public u32 get() { return value; }
|
|
1038
|
+
}
|
|
1039
|
+
void main() {
|
|
1040
|
+
S.init();
|
|
1041
|
+
u32 v <- S.get();
|
|
1042
|
+
}
|
|
1043
|
+
`;
|
|
1044
|
+
const tree = parse(code);
|
|
1045
|
+
const analyzer = new InitializationAnalyzer();
|
|
1046
|
+
const errors = analyzer.analyze(tree);
|
|
1047
|
+
|
|
1048
|
+
// value is assigned in init(), so get() can read it
|
|
1049
|
+
expect(errors).toHaveLength(0);
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
it("should flag uninitialized scope member read", () => {
|
|
1053
|
+
const code = `
|
|
1054
|
+
scope S {
|
|
1055
|
+
u32 value;
|
|
1056
|
+
public u32 get() { return value; }
|
|
1057
|
+
}
|
|
1058
|
+
void main() {
|
|
1059
|
+
u32 v <- S.get();
|
|
1060
|
+
}
|
|
1061
|
+
`;
|
|
1062
|
+
const tree = parse(code);
|
|
1063
|
+
const analyzer = new InitializationAnalyzer();
|
|
1064
|
+
const errors = analyzer.analyze(tree);
|
|
1065
|
+
|
|
1066
|
+
// value is never assigned - should error
|
|
1067
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
1068
|
+
expect(errors[0].code).toBe("E0381");
|
|
1069
|
+
expect(errors[0].variable).toBe("value");
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
it("should detect assignment in do-while body", () => {
|
|
1073
|
+
const code = `
|
|
1074
|
+
scope S {
|
|
1075
|
+
u32 count;
|
|
1076
|
+
public void init() {
|
|
1077
|
+
u32 i <- 0;
|
|
1078
|
+
do {
|
|
1079
|
+
count <- i;
|
|
1080
|
+
i <- i + 1;
|
|
1081
|
+
} while (i < 1);
|
|
1082
|
+
}
|
|
1083
|
+
public u32 get() { return count; }
|
|
1084
|
+
}
|
|
1085
|
+
void main() {
|
|
1086
|
+
S.init();
|
|
1087
|
+
u32 c <- S.get();
|
|
1088
|
+
}
|
|
1089
|
+
`;
|
|
1090
|
+
const tree = parse(code);
|
|
1091
|
+
const analyzer = new InitializationAnalyzer();
|
|
1092
|
+
const errors = analyzer.analyze(tree);
|
|
1093
|
+
|
|
1094
|
+
// count is assigned inside do-while in init()
|
|
1095
|
+
expect(errors).toHaveLength(0);
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
it("should detect assignment in if statement", () => {
|
|
1099
|
+
const code = `
|
|
1100
|
+
scope S {
|
|
1101
|
+
u32 value;
|
|
1102
|
+
public void set(bool flag) {
|
|
1103
|
+
if (flag = true) {
|
|
1104
|
+
value <- 1;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
public u32 get() { return value; }
|
|
1108
|
+
}
|
|
1109
|
+
void main() {
|
|
1110
|
+
S.set(true);
|
|
1111
|
+
u32 v <- S.get();
|
|
1112
|
+
}
|
|
1113
|
+
`;
|
|
1114
|
+
const tree = parse(code);
|
|
1115
|
+
const analyzer = new InitializationAnalyzer();
|
|
1116
|
+
const errors = analyzer.analyze(tree);
|
|
1117
|
+
|
|
1118
|
+
// value is assigned in if branch
|
|
1119
|
+
expect(errors).toHaveLength(0);
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1122
|
+
it("should detect assignment in for loop", () => {
|
|
1123
|
+
const code = `
|
|
1124
|
+
scope S {
|
|
1125
|
+
u32 total;
|
|
1126
|
+
public void calc() {
|
|
1127
|
+
for (u32 i <- 0; i < 5; i <- i + 1) {
|
|
1128
|
+
total <- i;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
public u32 get() { return total; }
|
|
1132
|
+
}
|
|
1133
|
+
void main() {
|
|
1134
|
+
S.calc();
|
|
1135
|
+
u32 t <- S.get();
|
|
1136
|
+
}
|
|
1137
|
+
`;
|
|
1138
|
+
const tree = parse(code);
|
|
1139
|
+
const analyzer = new InitializationAnalyzer();
|
|
1140
|
+
const errors = analyzer.analyze(tree);
|
|
1141
|
+
|
|
1142
|
+
// total is assigned in for loop
|
|
1143
|
+
expect(errors).toHaveLength(0);
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
it("should detect assignment via this.member", () => {
|
|
1147
|
+
const code = `
|
|
1148
|
+
scope S {
|
|
1149
|
+
u32 data;
|
|
1150
|
+
public void set() { this.data <- 100; }
|
|
1151
|
+
public u32 get() { return data; }
|
|
1152
|
+
}
|
|
1153
|
+
void main() {
|
|
1154
|
+
S.set();
|
|
1155
|
+
u32 d <- S.get();
|
|
1156
|
+
}
|
|
1157
|
+
`;
|
|
1158
|
+
const tree = parse(code);
|
|
1159
|
+
const analyzer = new InitializationAnalyzer();
|
|
1160
|
+
const errors = analyzer.analyze(tree);
|
|
1161
|
+
|
|
1162
|
+
// data is assigned via this.data
|
|
1163
|
+
expect(errors).toHaveLength(0);
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
it("should detect assignment in switch case", () => {
|
|
1167
|
+
const code = `
|
|
1168
|
+
scope S {
|
|
1169
|
+
u32 result;
|
|
1170
|
+
public void choose(u32 opt) {
|
|
1171
|
+
switch (opt) {
|
|
1172
|
+
case 1 { result <- 10; }
|
|
1173
|
+
case 2 { result <- 20; }
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
public u32 get() { return result; }
|
|
1177
|
+
}
|
|
1178
|
+
void main() {
|
|
1179
|
+
S.choose(1);
|
|
1180
|
+
u32 r <- S.get();
|
|
1181
|
+
}
|
|
1182
|
+
`;
|
|
1183
|
+
const tree = parse(code);
|
|
1184
|
+
const analyzer = new InitializationAnalyzer();
|
|
1185
|
+
const errors = analyzer.analyze(tree);
|
|
1186
|
+
|
|
1187
|
+
// result is assigned in switch cases
|
|
1188
|
+
expect(errors).toHaveLength(0);
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
it("should detect assignment in while loop", () => {
|
|
1192
|
+
const code = `
|
|
1193
|
+
scope S {
|
|
1194
|
+
u32 counter;
|
|
1195
|
+
public void loop() {
|
|
1196
|
+
u32 i <- 0;
|
|
1197
|
+
while (i < 3) {
|
|
1198
|
+
counter <- i;
|
|
1199
|
+
i <- i + 1;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
public u32 get() { return counter; }
|
|
1203
|
+
}
|
|
1204
|
+
void main() {
|
|
1205
|
+
S.loop();
|
|
1206
|
+
u32 c <- S.get();
|
|
1207
|
+
}
|
|
1208
|
+
`;
|
|
1209
|
+
const tree = parse(code);
|
|
1210
|
+
const analyzer = new InitializationAnalyzer();
|
|
1211
|
+
const errors = analyzer.analyze(tree);
|
|
1212
|
+
|
|
1213
|
+
// counter is assigned in while loop
|
|
1214
|
+
expect(errors).toHaveLength(0);
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
it("should detect assignment in switch default case", () => {
|
|
1218
|
+
const code = `
|
|
1219
|
+
scope S {
|
|
1220
|
+
u32 result;
|
|
1221
|
+
public void choose(u32 opt) {
|
|
1222
|
+
switch (opt) {
|
|
1223
|
+
case 1 { result <- 10; }
|
|
1224
|
+
default { result <- 99; }
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
public u32 get() { return result; }
|
|
1228
|
+
}
|
|
1229
|
+
void main() {
|
|
1230
|
+
S.choose(5);
|
|
1231
|
+
u32 r <- S.get();
|
|
1232
|
+
}
|
|
1233
|
+
`;
|
|
1234
|
+
const tree = parse(code);
|
|
1235
|
+
const analyzer = new InitializationAnalyzer();
|
|
1236
|
+
const errors = analyzer.analyze(tree);
|
|
1237
|
+
|
|
1238
|
+
// result is assigned in default case
|
|
1239
|
+
expect(errors).toHaveLength(0);
|
|
1240
|
+
});
|
|
1241
|
+
});
|
|
857
1242
|
});
|
|
@@ -411,4 +411,215 @@ describe("SignedShiftAnalyzer", () => {
|
|
|
411
411
|
expect(errors).toHaveLength(0);
|
|
412
412
|
});
|
|
413
413
|
});
|
|
414
|
+
|
|
415
|
+
// ========================================================================
|
|
416
|
+
// Compound Shift-Assign (Issue #1008)
|
|
417
|
+
// ========================================================================
|
|
418
|
+
|
|
419
|
+
describe("compound shift-assign (issue #1008)", () => {
|
|
420
|
+
it("should detect left shift compound-assign with i8 target", () => {
|
|
421
|
+
const code = `
|
|
422
|
+
void main() {
|
|
423
|
+
i8 x <- 1;
|
|
424
|
+
x <<<- 2;
|
|
425
|
+
}
|
|
426
|
+
`;
|
|
427
|
+
const tree = parse(code);
|
|
428
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
429
|
+
const errors = analyzer.analyze(tree);
|
|
430
|
+
|
|
431
|
+
expect(errors).toHaveLength(1);
|
|
432
|
+
expect(errors[0].code).toBe("E0805");
|
|
433
|
+
expect(errors[0].message).toContain("Shift operator '<<<-'");
|
|
434
|
+
expect(errors[0].message).toContain("signed integer types");
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it("should detect right shift compound-assign with i8 target", () => {
|
|
438
|
+
const code = `
|
|
439
|
+
void main() {
|
|
440
|
+
i8 x <- 64;
|
|
441
|
+
x >><- 2;
|
|
442
|
+
}
|
|
443
|
+
`;
|
|
444
|
+
const tree = parse(code);
|
|
445
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
446
|
+
const errors = analyzer.analyze(tree);
|
|
447
|
+
|
|
448
|
+
expect(errors).toHaveLength(1);
|
|
449
|
+
expect(errors[0].code).toBe("E0805");
|
|
450
|
+
expect(errors[0].message).toContain("Shift operator '>><-'");
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it("should detect left shift compound-assign with i16 target", () => {
|
|
454
|
+
const code = `
|
|
455
|
+
void main() {
|
|
456
|
+
i16 x <- 1;
|
|
457
|
+
x <<<- 4;
|
|
458
|
+
}
|
|
459
|
+
`;
|
|
460
|
+
const tree = parse(code);
|
|
461
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
462
|
+
const errors = analyzer.analyze(tree);
|
|
463
|
+
|
|
464
|
+
expect(errors).toHaveLength(1);
|
|
465
|
+
expect(errors[0].code).toBe("E0805");
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it("should detect left shift compound-assign with i32 target", () => {
|
|
469
|
+
const code = `
|
|
470
|
+
void main() {
|
|
471
|
+
i32 x <- 1;
|
|
472
|
+
x <<<- 8;
|
|
473
|
+
}
|
|
474
|
+
`;
|
|
475
|
+
const tree = parse(code);
|
|
476
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
477
|
+
const errors = analyzer.analyze(tree);
|
|
478
|
+
|
|
479
|
+
expect(errors).toHaveLength(1);
|
|
480
|
+
expect(errors[0].code).toBe("E0805");
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it("should detect left shift compound-assign with i64 target", () => {
|
|
484
|
+
const code = `
|
|
485
|
+
void main() {
|
|
486
|
+
i64 x <- 1;
|
|
487
|
+
x <<<- 16;
|
|
488
|
+
}
|
|
489
|
+
`;
|
|
490
|
+
const tree = parse(code);
|
|
491
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
492
|
+
const errors = analyzer.analyze(tree);
|
|
493
|
+
|
|
494
|
+
expect(errors).toHaveLength(1);
|
|
495
|
+
expect(errors[0].code).toBe("E0805");
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it("should not flag left shift compound-assign with u8 target", () => {
|
|
499
|
+
const code = `
|
|
500
|
+
void main() {
|
|
501
|
+
u8 x <- 1;
|
|
502
|
+
x <<<- 2;
|
|
503
|
+
}
|
|
504
|
+
`;
|
|
505
|
+
const tree = parse(code);
|
|
506
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
507
|
+
const errors = analyzer.analyze(tree);
|
|
508
|
+
|
|
509
|
+
expect(errors).toHaveLength(0);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it("should not flag right shift compound-assign with u32 target", () => {
|
|
513
|
+
const code = `
|
|
514
|
+
void main() {
|
|
515
|
+
u32 x <- 256;
|
|
516
|
+
x >><- 4;
|
|
517
|
+
}
|
|
518
|
+
`;
|
|
519
|
+
const tree = parse(code);
|
|
520
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
521
|
+
const errors = analyzer.analyze(tree);
|
|
522
|
+
|
|
523
|
+
expect(errors).toHaveLength(0);
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it("should detect multiple compound shift-assign violations", () => {
|
|
527
|
+
const code = `
|
|
528
|
+
void main() {
|
|
529
|
+
i8 a <- 1;
|
|
530
|
+
a <<<- 2;
|
|
531
|
+
i16 b <- 64;
|
|
532
|
+
b >><- 3;
|
|
533
|
+
}
|
|
534
|
+
`;
|
|
535
|
+
const tree = parse(code);
|
|
536
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
537
|
+
const errors = analyzer.analyze(tree);
|
|
538
|
+
|
|
539
|
+
expect(errors).toHaveLength(2);
|
|
540
|
+
expect(errors[0].message).toContain("<<<-");
|
|
541
|
+
expect(errors[1].message).toContain(">><-");
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it("should not flag other compound-assign operators on signed types", () => {
|
|
545
|
+
const code = `
|
|
546
|
+
void main() {
|
|
547
|
+
i32 x <- 10;
|
|
548
|
+
x +<- 5;
|
|
549
|
+
x -<- 2;
|
|
550
|
+
x *<- 3;
|
|
551
|
+
x /<- 2;
|
|
552
|
+
x %<- 3;
|
|
553
|
+
x &<- 0xFF;
|
|
554
|
+
x |<- 0x0F;
|
|
555
|
+
x ^<- 0xAA;
|
|
556
|
+
}
|
|
557
|
+
`;
|
|
558
|
+
const tree = parse(code);
|
|
559
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
560
|
+
const errors = analyzer.analyze(tree);
|
|
561
|
+
|
|
562
|
+
expect(errors).toHaveLength(0);
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// ========================================================================
|
|
567
|
+
// Struct Member Compound Shift-Assign (PR #1013 review feedback)
|
|
568
|
+
// ========================================================================
|
|
569
|
+
|
|
570
|
+
describe("struct member compound shift-assign", () => {
|
|
571
|
+
it("should detect left shift compound-assign on signed struct field", () => {
|
|
572
|
+
// Note: This requires CodeGenState.symbols to have struct field info
|
|
573
|
+
// In real usage, symbols are populated during transpilation
|
|
574
|
+
// For unit tests without symbols, we rely on the integration tests
|
|
575
|
+
const code = `
|
|
576
|
+
struct Data { i8 val; }
|
|
577
|
+
void main() {
|
|
578
|
+
Data d <- {val: 1};
|
|
579
|
+
d.val <<<- 2;
|
|
580
|
+
}
|
|
581
|
+
`;
|
|
582
|
+
const tree = parse(code);
|
|
583
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
584
|
+
// Without CodeGenState.symbols populated, this won't detect the issue
|
|
585
|
+
// The integration test covers this case
|
|
586
|
+
const errors = analyzer.analyze(tree);
|
|
587
|
+
// This returns 0 because CodeGenState.getStructFieldType returns undefined
|
|
588
|
+
// without populated symbols. The integration test verifies the full path.
|
|
589
|
+
expect(errors).toHaveLength(0);
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it("should not flag unsigned struct field shift", () => {
|
|
593
|
+
const code = `
|
|
594
|
+
struct Data { u8 val; }
|
|
595
|
+
void main() {
|
|
596
|
+
Data d <- {val: 1};
|
|
597
|
+
d.val <<<- 2;
|
|
598
|
+
}
|
|
599
|
+
`;
|
|
600
|
+
const tree = parse(code);
|
|
601
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
602
|
+
const errors = analyzer.analyze(tree);
|
|
603
|
+
|
|
604
|
+
expect(errors).toHaveLength(0);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
it("should still detect simple signed variable shift alongside struct access", () => {
|
|
608
|
+
const code = `
|
|
609
|
+
struct Data { u8 val; }
|
|
610
|
+
void main() {
|
|
611
|
+
Data d <- {val: 1};
|
|
612
|
+
i8 x <- 1;
|
|
613
|
+
x <<<- 2;
|
|
614
|
+
}
|
|
615
|
+
`;
|
|
616
|
+
const tree = parse(code);
|
|
617
|
+
const analyzer = new SignedShiftAnalyzer();
|
|
618
|
+
const errors = analyzer.analyze(tree);
|
|
619
|
+
|
|
620
|
+
expect(errors).toHaveLength(1);
|
|
621
|
+
expect(errors[0].code).toBe("E0805");
|
|
622
|
+
expect(errors[0].message).toContain("<<<-");
|
|
623
|
+
});
|
|
624
|
+
});
|
|
414
625
|
});
|