3535# - enable MC/DC instrumentation in your compile/link steps, and
3636# - pass the '--mcdc-coverage' flag to llvm2lcov
3737#
38+ # You can also use LLVM/21 or newer to generate MC/DC data more cleanly.
39+ #
3840# See 'llvm2lcov --help' for more usage information
3941#
4042# See the LLVM documentation for more information on flags and compilation options.
4143
4244use strict;
45+ use version;
4346use warnings;
4447require Exporter;
4548
@@ -131,6 +134,7 @@ sub parse
131134 unless (defined ($json ) &&
132135 exists ($json -> {data }) &&
133136 ' ARRAY' eq ref ($json -> {data }));
137+ my $json_version = version-> parse($json -> {version });
134138
135139 lcovutil::info(" read $jsonFile \n " );
136140
@@ -156,8 +160,6 @@ sub parse
156160 if (defined ($version ) && $version ne " " );
157161
158162 my $lineData = $fileInfo -> test($testname );
159- my $mcdcData = $fileInfo -> testcase_mcdc($testname )
160- if $lcovutil::mcdc_coverage ;
161163
162164 my $summary = $f -> {summary };
163165 my $branches = $f -> {branches };
@@ -235,7 +237,8 @@ sub parse
235237 $currentLine = $isRegionEntry ? $line : $line + 1;
236238 }
237239 }
238- if ($mcdc ) {
240+ if ($mcdc && $json_version < version-> parse(" 3.0.1" )) {
241+ my $mcdcData = $fileInfo -> testcase_mcdc($testname );
239242 my @mcdcBranches ; # array (start line, start column, expression)
240243 foreach my $branch (@$branches ) {
241244 die (" unexpected branch data" )
@@ -316,12 +319,21 @@ sub parse
316319 my $count = $f -> {count };
317320 my $regions = $f -> {regions }; # startline/col, endline/col/
318321 my $branches = $f -> {branches };
322+ # The version "3.0.1" adds fileId to mcdc.
323+ # This allows using MC/DC branches from expansions for placing MC/DC entries defined in expansions to expansions call sites.
324+ my $mcdc = $f -> {mcdc_records }
325+ if ($lcovutil::mcdc_coverage &&
326+ $json_version >= version-> parse(" 3.0.1" ) &&
327+ exists ($f -> {mcdc_records }));
319328
320329 my $functionMap = $info -> testfnc($testname );
321330 # use branch data to derive MC/DC expression - so need
322331 # it, even if user didn't ask
323332 my $branchData = $info -> testbr($testname )
324- if $lcovutil::br_coverage ;
333+ if $lcovutil::br_coverage || $mcdc ;
334+ my $mcdcData = $info -> testcase_mcdc($testname )
335+ if ($json_version >= version-> parse(" 3.0.1" ) &&
336+ $lcovutil::mcdc_coverage );
325337 my $startLine = $regions -> [0]-> [0]; # startline of first region
326338 my $endline = $regions -> [0]-> [2]; # endline of last region
327339 if ($lcovutil::func_coverage ) {
@@ -331,6 +343,10 @@ sub parse
331343 unless defined ($functionMap -> findName($name ));
332344 $functionMap -> add_count($name , $count );
333345 }
346+
347+ my @mcdcBranches ; # array (fileId, start line, start column, expression)
348+ my %expanded_mcdcBranches ; # hash of branch's fileId -> branch's start line
349+
334350 if ($branchData ) {
335351 my $funcBranchData = BranchData-> new();
336352 my $regionIdx = 0;
@@ -379,26 +395,99 @@ sub parse
379395 ++$regionIdx ;
380396 }
381397 }
382- # Processed branch on the same line doesn't have to be the previous.
383- my $brEntry = $funcBranchData -> value($line );
384- my $branchIdx =
385- !defined ($brEntry ) ? 0 :
386- scalar (@{$brEntry -> getBlock(0)});
387- my $br =
388- BranchBlock-> new($branchIdx , $trueCount ,
389- !defined ($expr ) ? $branchIdx :
390- " (" . $expr . " ) == True" );
391- $funcBranchData -> append($line , 0, $br , $filename );
392-
393- ++$branchIdx ;
394- $br =
395- BranchBlock-> new($branchIdx , $falseCount ,
396- !defined ($expr ) ? $branchIdx :
397- " (" . $expr . " ) == False" );
398- $funcBranchData -> append($line , 0, $br , $filename );
398+ $fileId = $b -> [6];
399+ # Consider only branches of "MCDCBranchRegion" kind.
400+ if ($mcdc &&
401+ $kind == 6 &&
402+ !defined ($expanded_mcdcBranches {$fileId })) {
403+ if ($fileId &&
404+ scalar (@mcdcBranches ) &&
405+ $fileId == $mcdcBranches [-1]-> [0]) {
406+ pop (@mcdcBranches );
407+ $expanded_mcdcBranches {$fileId } = $line ;
408+ } else {
409+ push (@mcdcBranches ,
410+ [$fileId , $line , $col , $expr ]);
411+ }
412+ }
413+
414+ if ($lcovutil::br_coverage ) {
415+ # Processed branch on the same line doesn't have to be the previous.
416+ my $brEntry = $funcBranchData -> value($line );
417+ my $branchIdx =
418+ !defined ($brEntry ) ? 0 :
419+ scalar (@{$brEntry -> getBlock(0)});
420+ my $br =
421+ BranchBlock-> new($branchIdx , $trueCount ,
422+ !defined ($expr ) ? $branchIdx :
423+ " (" . $expr . " ) == True" );
424+ $funcBranchData -> append($line , 0, $br , $filename );
425+
426+ ++$branchIdx ;
427+ $br =
428+ BranchBlock-> new($branchIdx , $falseCount ,
429+ !defined ($expr ) ? $branchIdx :
430+ " (" . $expr . " ) == False" );
431+ $funcBranchData -> append($line , 0, $br , $filename );
432+ }
433+ }
434+ $branchData -> union($funcBranchData )
435+ if $lcovutil::br_coverage ;
436+ }
437+ if ($mcdc ) {
438+ foreach my $m (@$mcdc ) {
439+ die (" unexpected MC/DC data" ) unless scalar (@$m ) == 10;
440+ my ($line , $col , $endLine , $endCol ,
441+ $trueCount , $falseCount , $fileId , $expandedId ,
442+ $kind , $cov ) = @$m ;
443+ die (" unexpected MC/DC cov" )
444+ unless ' ARRAY' eq ref ($cov );
445+ my $expr ;
446+ my @brExprs ;
447+ if ($fileId == $expandedId ) {
448+ foreach my $branch (@mcdcBranches ) {
449+ my ($brFileId , $brLine , $brCol , $brExpr ) =
450+ @$branch ;
451+ if (($brLine > $line ||
452+ ($brLine == $line && $brCol >= $col ))
453+ &&
454+ ($brLine < $endLine ||
455+ ($brLine == $endLine && $brCol <= $endCol ))
456+ ) {
457+ push (@brExprs , [$brLine , $brCol , $brExpr ]);
458+ }
459+ }
460+ @brExprs = sort {$a -> [0] <=> $b -> [0] ||
461+ $a -> [1] <=> $b -> [1]
462+ } @brExprs ;
463+ $expr =
464+ $srcReader -> getExpr($line , $col ,
465+ $endLine , $endCol )
466+ if $srcReader -> notEmpty();
467+ } else {
468+ $line = $expanded_mcdcBranches {$fileId };
469+ }
470+ my $current_mcdc =
471+ $mcdcData -> new_mcdc($mcdcData , $line );
472+ my $groupSize = scalar (@$cov );
473+ my $idx = 0;
474+ foreach my $c (@$cov ) {
475+ my $brExpr = $brExprs [$idx ]-> [2]
476+ if ($fileId == $expandedId &&
477+ $idx < scalar (@brExprs ));
478+ my $fullExpr = defined ($brExpr ) &&
479+ defined ($expr ) ? " '$brExpr ' in '$expr '" : $idx ;
480+ $current_mcdc -> insertExpr($filename , $groupSize , 0,
481+ $c , $idx , $fullExpr );
482+ $current_mcdc -> insertExpr($filename , $groupSize , 1,
483+ $c , $idx , $fullExpr );
484+ ++$idx ;
485+ }
486+ $mcdcData -> close_mcdcBlock($current_mcdc );
399487 }
400- $branchData -> union($funcBranchData );
401488 }
489+ $info -> testbr()-> remove($testname )
490+ if $mcdc && !$lcovutil::br_coverage ;
402491 }
403492 }
404493 lcovutil::info(2, " finished $jsonFile \n " );
0 commit comments