@@ -40,7 +40,6 @@ with Coverage; use Coverage;
40
40
with Coverage_Options ;
41
41
with Hex_Images ; use Hex_Images;
42
42
with Inputs ; use Inputs;
43
- with Instrument.C_Utils ; use Instrument.C_Utils;
44
43
with Outputs ; use Outputs;
45
44
with Paths ; use Paths;
46
45
with SCOs ;
@@ -221,6 +220,11 @@ package body Instrument.C is
221
220
Loc : Source_Location_T;
222
221
Text : String);
223
222
223
+ overriding procedure Register_CXX_For_Range
224
+ (Pass : Instrument_Pass_Kind;
225
+ UIC : in out C_Unit_Inst_Context'Class;
226
+ N : Cursor_T);
227
+
224
228
Record_PP_Info_Pass : aliased Record_PP_Info_Pass_Kind;
225
229
Instrument_Pass : aliased Instrument_Pass_Kind;
226
230
@@ -281,6 +285,54 @@ package body Instrument.C is
281
285
function Insert_MCDC_State
282
286
(UIC : in out C_Unit_Inst_Context'Class; Name : String) return String;
283
287
288
+ procedure Fix_CXX_For_Ranges (UIC : in out C_Unit_Inst_Context);
289
+ -- This procedure fixes C++ for ranges that were purposefully instrumented
290
+ -- wrongly. To instrument a C++ for range, we actually need to turn
291
+ --
292
+ -- for (int i = 0; auto j : {1, 2}) {}
293
+ --
294
+ -- into
295
+ --
296
+ -- {
297
+ -- witness_i(); int i = 0;
298
+ -- witness_j();
299
+ -- for (auto j : {1, 2}) {}
300
+ -- }
301
+ --
302
+ -- We introduce an outer scope to leave the initializer declaration
303
+ -- lifetime unchanged, and to be able to easily instrument both the
304
+ -- initializer declaration and the range expression.
305
+ --
306
+ -- Note that this is valid because you can't goto inside the loop
307
+ -- (and thus skip the execution of witness_j) as it is
308
+ -- forbidden to bypass declarations with initialization in C++,
309
+ -- which "auto j : {1, 2}" is.
310
+ --
311
+ -- Though we can't do that all at once, as this changes the shape as the
312
+ -- AST. As we rely on the AST node location to instrument statements and
313
+ -- decisions, we would be instrumenting the decision at the wrong place,
314
+ -- as we would instrument the initialization statement by moving it.
315
+ --
316
+ -- Thus, we do the normal instrumentation process, which will yield an
317
+ -- unvalid C++ sequence, that we can easily fix in this procedure, by
318
+ -- moving around the rewritten code.
319
+ --
320
+ -- The for ranges at the entry of this procedure will have been
321
+ -- instrumented as followed (the added code is identified by <>):
322
+ --
323
+ -- <witness_j();> for (<witness_i();> int i = 0; auto j : {1, 2}) {}<}>
324
+ --
325
+ -- Two things to note here: the witness_j is executed before "int i = 0;"
326
+ -- (which is wrong), and there is an unmatched closing brace.
327
+ --
328
+ -- To fix this, we actually need to move both the added code, _and_ the
329
+ -- initializer statement before the <witness_j();>, and insert an opening
330
+ -- brace before:
331
+ --
332
+ -- <{><witness_i();> int i = 0; <witness_j();> for (auto j : {1, 2}) {}<}>
333
+ --
334
+ -- and now we have valid, and correctly instrumented code.
335
+
284
336
-- --------------------------
285
337
-- Source level rewriting --
286
338
-- --------------------------
@@ -1030,6 +1082,18 @@ package body Instrument.C is
1030
1082
end if ;
1031
1083
end Insert_Text_After ;
1032
1084
1085
+ -- --------------------------
1086
+ -- Register_CXX_For_Range --
1087
+ -- --------------------------
1088
+
1089
+ overriding procedure Register_CXX_For_Range
1090
+ (Pass : Instrument_Pass_Kind;
1091
+ UIC : in out C_Unit_Inst_Context'Class;
1092
+ N : Cursor_T) is
1093
+ begin
1094
+ UIC.Instrumented_CXX_For_Ranges.Append (N);
1095
+ end Register_CXX_For_Range ;
1096
+
1033
1097
-- ------------------------
1034
1098
-- Instrument_Statement --
1035
1099
-- ------------------------
@@ -1306,6 +1370,43 @@ package body Instrument.C is
1306
1370
return Name;
1307
1371
end Insert_MCDC_State ;
1308
1372
1373
+ -- ----------------------
1374
+ -- Fix_CXX_For_Ranges --
1375
+ -- ----------------------
1376
+
1377
+ procedure Fix_CXX_For_Ranges (UIC : in out C_Unit_Inst_Context) is
1378
+ begin
1379
+ for N of UIC.Instrumented_CXX_For_Ranges loop
1380
+ declare
1381
+ Loc : constant Source_Location_T := Start_Sloc (N);
1382
+ For_Init_Stmt : constant Cursor_T := Get_For_Init (N);
1383
+ For_Init_Rng : constant Source_Range_T :=
1384
+ Get_Cursor_Extent (For_Init_Stmt);
1385
+ begin
1386
+ -- Get the rewritten text for the initializing statement and
1387
+ -- move it before any rewritten text before the for statement.
1388
+
1389
+ CX_Rewriter_Insert_Text_Before
1390
+ (UIC.Rewriter,
1391
+ Loc,
1392
+ CX_Rewriter_Get_Rewritten_Text (UIC.Rewriter, For_Init_Rng));
1393
+
1394
+ -- Open the outer scope. It was closed during the instrumentation
1395
+ -- process, so we do not need to take care of that.
1396
+
1397
+ CX_Rewriter_Insert_Text_Before (UIC.Rewriter, Loc, " {" );
1398
+ CX_Rewriter_Remove_Text (UIC.Rewriter, For_Init_Rng);
1399
+
1400
+ -- for (; auto i : {1, 2}) is invalid C++ code so insert a dummy
1401
+ -- initializing expression to avoid the null statement here, as
1402
+ -- it is easier than trying to move around the comma.
1403
+
1404
+ CX_Rewriter_Insert_Text_Before
1405
+ (UIC.Rewriter, Start_Sloc (For_Init_Stmt), " true" );
1406
+ end ;
1407
+ end loop ;
1408
+ end Fix_CXX_For_Ranges ;
1409
+
1309
1410
type SC_Entry is record
1310
1411
N : Cursor_T;
1311
1412
-- Original statement node, used to compute macro expansion information
@@ -2134,14 +2235,28 @@ package body Instrument.C is
2134
2235
-- the range declaration initialization expression. Like all
2135
2236
-- statements, they can contain nested decisions.
2136
2237
2137
- Extend_Statement_Sequence
2138
- (For_Init_Stmt, ' ' , Insertion_N => N);
2139
- Process_Decisions_Defer (For_Init_Stmt, ' X' );
2238
+ if not Is_Null (For_Init_Stmt) then
2239
+
2240
+ -- See Fix_CXX_For_Ranges for an explanation of the
2241
+ -- below code.
2242
+
2243
+ Extend_Statement_Sequence
2244
+ (For_Init_Stmt, ' ' , Insertion_N => For_Init_Stmt);
2245
+ Process_Decisions_Defer (For_Init_Stmt, ' X' );
2246
+
2247
+ -- Preemptively end the introduced outer scope as it is
2248
+ -- easier done when traversing the AST.
2249
+
2250
+ Append (Trailing_Braces, ' }' );
2251
+ UIC.Pass.Register_CXX_For_Range (UIC, N);
2252
+ end if ;
2253
+
2254
+ -- Instrument the range as mentioned above
2140
2255
2141
2256
Extend_Statement_Sequence
2142
2257
(For_Range_Decl, ' ' ,
2143
- Insertion_N => For_Range_Decl ,
2144
- Instr_Scheme => Instr_Expr );
2258
+ Insertion_N => N ,
2259
+ Instr_Scheme => Instr_Stmt );
2145
2260
Process_Decisions_Defer (For_Range_Decl, ' X' );
2146
2261
2147
2262
Set_Statement_Entry;
@@ -3315,6 +3430,11 @@ package body Instrument.C is
3315
3430
end loop ;
3316
3431
end ;
3317
3432
3433
+ -- Once everything has been instrumented, fixup the for ranges. See the
3434
+ -- documentation of Fix_CXX_For_Ranges.
3435
+
3436
+ Fix_CXX_For_Ranges (UIC);
3437
+
3318
3438
-- We import the extern declaration of symbols instead of including the
3319
3439
-- header where they are defined.
3320
3440
--
0 commit comments