Skip to content

Commit 117730e

Browse files
committed
Merge branch 'eyraud/merge_master_into_edge' into 'master'
Merge edge into master See merge request eng/cov/gnatcoverage!276 Ref: eng/shared/release#206
2 parents 3586d13 + e0c80ad commit 117730e

File tree

7 files changed

+210
-9
lines changed

7 files changed

+210
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <initializer_list>
2+
3+
int
4+
main (void)
5+
{
6+
int sum = 0; // # init
7+
for (auto i : { 1, 2, 3 }) // # for-range
8+
{
9+
sum += i; // # for-body
10+
}
11+
return 0;
12+
}
13+
14+
//# test_for_range.cpp
15+
//
16+
// /init/ l+ ## 0
17+
// /for-range/ l+ ## 0
18+
// /for-body/ l+ ## 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""
2+
Test that gnatcov correctly instruments a for range with an initializer as the
3+
range expression and an initialization statement, e.g.
4+
```
5+
for (int i = 0; auto j : {1, 2}){}
6+
```
7+
"""
8+
9+
from SCOV.tc import TestCase
10+
from SUITE.context import thistest
11+
12+
TestCase().run()
13+
thistest.result()

tools/gnatcov/clang-extensions.adb

+23
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,29 @@ package body Clang.Extensions is
164164
CX_Rewriter_Insert_Text_Before_Token_C (Rew, Loc, Insert & ASCII.NUL);
165165
end CX_Rewriter_Insert_Text_Before_Token;
166166

167+
------------------------------------
168+
-- CX_Rewriter_Get_Rewritten_Text --
169+
------------------------------------
170+
171+
function CX_Rewriter_Get_Rewritten_Text
172+
(Rew : Rewriter_T;
173+
R : Source_Range_T) return String
174+
is
175+
function CX_Rewriter_Get_Rewritten_Text
176+
(Rew : Rewriter_T;
177+
R : Source_Range_T) return String_T
178+
with
179+
Import, Convention => C,
180+
External_Name => "clang_CXRewriter_getRewrittenText";
181+
182+
Rewritten_Text_C : constant String_T :=
183+
CX_Rewriter_Get_Rewritten_Text (Rew, R);
184+
Rewritten_Text : constant String := Get_C_String (Rewritten_Text_C);
185+
begin
186+
Dispose_String (Rewritten_Text_C);
187+
return Rewritten_Text;
188+
end CX_Rewriter_Get_Rewritten_Text;
189+
167190
-----------------------
168191
-- Spelling_Location --
169192
-----------------------

tools/gnatcov/clang-extensions.ads

+6
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ package Clang.Extensions is
128128
-- Insert the text Insert before the token at the given location, and after
129129
-- any previously inserted string (at the same location).
130130

131+
function CX_Rewriter_Get_Rewritten_Text
132+
(Rew : Rewriter_T;
133+
R : Source_Range_T) return String
134+
with Inline;
135+
-- Return the rewritten text for the given source range.
136+
131137
function Get_Cursor_TU (C : Cursor_T) return Translation_Unit_T
132138
with
133139
Import, Convention => C,

tools/gnatcov/clang-wrapper.cc

+12-3
Original file line numberDiff line numberDiff line change
@@ -705,10 +705,19 @@ clang_CXRewriter_insertTextBeforeToken (CXRewriter Rew, CXSourceLocation Loc,
705705
R.InsertTextAfter (Prv, Insert);
706706
}
707707

708-
/* Wrappers around source location analysis functions. */
708+
extern "C" CXString
709+
clang_CXRewriter_getRewrittenText (CXRewriter Rew, CXSourceRange Rng)
710+
{
711+
assert (Rew);
712+
Rewriter &R = *reinterpret_cast<Rewriter *> (Rew);
713+
SourceRange SR = translateCXSourceRange (Rng);
714+
return createDup (R.getRewrittenText (SR).c_str ());
715+
}
709716

710-
extern "C" unsigned
711-
clang_isMacroLocation (CXSourceLocation Loc)
717+
/* Wrappers around source location analysis functions. */
718+
719+
extern "C" unsigned
720+
clang_isMacroLocation (CXSourceLocation Loc)
712721
{
713722
const SourceLocation SLoc = translateSourceLocation (Loc);
714723
return SLoc.isMacroID () ? 1 : 0;

tools/gnatcov/instrument-c.adb

+126-6
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ with Coverage; use Coverage;
4040
with Coverage_Options;
4141
with Hex_Images; use Hex_Images;
4242
with Inputs; use Inputs;
43-
with Instrument.C_Utils; use Instrument.C_Utils;
4443
with Outputs; use Outputs;
4544
with Paths; use Paths;
4645
with SCOs;
@@ -221,6 +220,11 @@ package body Instrument.C is
221220
Loc : Source_Location_T;
222221
Text : String);
223222

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+
224228
Record_PP_Info_Pass : aliased Record_PP_Info_Pass_Kind;
225229
Instrument_Pass : aliased Instrument_Pass_Kind;
226230

@@ -281,6 +285,54 @@ package body Instrument.C is
281285
function Insert_MCDC_State
282286
(UIC : in out C_Unit_Inst_Context'Class; Name : String) return String;
283287

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+
284336
----------------------------
285337
-- Source level rewriting --
286338
----------------------------
@@ -1030,6 +1082,18 @@ package body Instrument.C is
10301082
end if;
10311083
end Insert_Text_After;
10321084

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+
10331097
--------------------------
10341098
-- Instrument_Statement --
10351099
--------------------------
@@ -1306,6 +1370,43 @@ package body Instrument.C is
13061370
return Name;
13071371
end Insert_MCDC_State;
13081372

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+
13091410
type SC_Entry is record
13101411
N : Cursor_T;
13111412
-- Original statement node, used to compute macro expansion information
@@ -2134,14 +2235,28 @@ package body Instrument.C is
21342235
-- the range declaration initialization expression. Like all
21352236
-- statements, they can contain nested decisions.
21362237

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
21402255

21412256
Extend_Statement_Sequence
21422257
(For_Range_Decl, ' ',
2143-
Insertion_N => For_Range_Decl,
2144-
Instr_Scheme => Instr_Expr);
2258+
Insertion_N => N,
2259+
Instr_Scheme => Instr_Stmt);
21452260
Process_Decisions_Defer (For_Range_Decl, 'X');
21462261

21472262
Set_Statement_Entry;
@@ -3315,6 +3430,11 @@ package body Instrument.C is
33153430
end loop;
33163431
end;
33173432

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+
33183438
-- We import the extern declaration of symbols instead of including the
33193439
-- header where they are defined.
33203440
--

tools/gnatcov/instrument-c.ads

+12
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ with Clang.Rewrite; use Clang.Rewrite;
3232

3333
with Diagnostics; use Diagnostics;
3434
with Files_Table; use Files_Table;
35+
with Instrument.C_Utils; use Instrument.C_Utils;
3536
with Instrument.Common; use Instrument.Common;
3637
with Slocs; use Slocs;
3738

@@ -381,6 +382,11 @@ package Instrument.C is
381382
Disable_Instrumentation : Boolean := False;
382383
-- Set to True to deactivate instrumentation and prevent any code
383384
-- rewriting.
385+
386+
Instrumented_CXX_For_Ranges : Cursor_Vectors.Vector;
387+
-- List of instrumented for ranges. For an explanation of why we need
388+
-- to store these, see the documentation of the Fix_CXX_For_Ranges
389+
-- subprogram.
384390
end record;
385391

386392
type C_Source_Rewriter is tagged limited private;
@@ -472,6 +478,12 @@ private
472478
Msg : String;
473479
Kind : Report_Kind := Diagnostics.Warning) is null;
474480

481+
procedure Register_CXX_For_Range
482+
(Pass : Pass_Kind;
483+
UIC : in out C_Unit_Inst_Context'Class;
484+
N : Cursor_T) is null;
485+
-- See the documentation of Fix_CXX_For_Ranges
486+
475487
type C_Source_Rewriter is limited new Ada.Finalization.Limited_Controlled
476488
with record
477489
CIdx : Index_T;

0 commit comments

Comments
 (0)