Coverage Of Null Case Alternatives

From OC Systems Wiki!
Jump to: navigation, search

How do I interpret lines counts from coverage on an Ada Null case alternatives?

Consider how the compiler emits code for these ‘null’ alternatives—there are no instructions to execute so the action for that value of the case selector can be to just jump to statement following the case statement, though the compiler could just generate jumps to null alternatives which then jump to the end of the case statement.

The GNAT compiler generates branches to null alternatives, as seen at lines 13 and 22 in this example:

main.adb:
Hit Line  Source
--- ----  ------ - -
    0001: 
    0002: with Text_Io;
    0003: 
    0004: procedure Main is
    0005: 
    0006:    subtype S is Integer range 1..4;
    0007: 
 20 0008:    function Choose(I : S) return Integer is
 20 0009:       Result : Integer := 1;
    0010:    begin
 20 0011:       case I is
    0012:       when 1 => 
  5 0013:          null;
    0014: 
    0015:       when 2 => 
  5 0016:          Result := Result * 2;
    0017: 
    0018:       when 3 => 
  5 0019:          Result := Result * 3;
    0020: 
    0021:       when 4 => 
  5 0022:          null;
 20 0023:       end case;
 20 0024:       return Result;
 20 0025:    end Choose;
    0026: 
    0027: begin
    0028:    for I in Integer Range 1..5 Loop
    0029:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(1)));      
    0030:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(2)));      
    0031:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(3)));      
    0032:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(4)));      
    0033:    end loop;
    0034: end Main;
    0035: 

The PowerAda compiler generates jumps to the end of the case statement rather than to the null alternatives as seen at lines 13 and 22 in the same example:

main.adb:
Hit Line  Source
--- ----  ------ - -
    0001: 
    0002: with Text_Io;
    0003: 
    0004: procedure Main is
    0005: 
    0006:    subtype S is Integer range 1..4;
    0007: 
    0008:    function Choose(I : S) return Integer is
 20 0009:       Result : Integer := 1;
    0010:    begin
 20 0011:       case I is
    0012:       when 1 => 
  0 0013:          null;
    0014: 
    0015:       when 2 => 
  5 0016:          Result := Result * 2;
    0017: 
    0018:       when 3 => 
  5 0019:          Result := Result * 3;
    0020: 
    0021:       when 4 => 
    0022:          null;
    0023:       end case;
 20 0024:       return Result;
 20 0025:    end Choose;
    0026: 
    0027: begin
    0028:    for I in Integer Range 1..5 Loop
    0029:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(1)));      
    0030:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(2)));      
    0031:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(3)));      
    0032:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(4)));      
    0033:    end loop;
    0034: end Main;
    0035: 

The 1 alternative (line 13) seems to generate line information for the null alternative, but it is never executed, while the 4 alternative (line 22) is skipped entirely.

For case statements with null alternatives you should check the count on the selector (line11) and see that the counts on the non-null alternatives added up to less than the count for the selector.

When the case statements is nested within another control construct the code generated by the compiler may change line counts.

The GNAT compiler still generates branches to null alternatives, as seen at lines 14 and 23 in this example:

main.adb:
Hit Line  Source
--- ----  ------ - -
    0001: 
    0002: with Text_Io;
    0003: 
    0004: procedure Main is
    0005: 
    0006:    subtype S is Integer range 1..4;
    0007: 
 20 0008:    function Choose(B : Boolean; I : S) return Integer is
 20 0009:       Result : Integer := 1;
    0010:    begin
 20 0011:       if B then
 20 0012:          case I is
    0013:          when 1 => 
  5 0014:             null;
    0015:    
    0016:          when 2 => 
  5 0017:             Result := Result * 2;
    0018:    
    0019:          when 3 => 
  5 0020:             Result := Result * 3;
    0021:    
    0022:          when 4 => 
  5 0023:             null;
  5 0024:          end case;
    0025:       else
  0 0026:          Result := 0;
    0027:       end if;
 20 0028:       return Result;
 20 0029:    end Choose;
    0030: 
    0031: begin
    0032:    for I in Integer Range 1..5 Loop
    0033:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 1)));      
    0034:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 2)));      
    0035:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 3)));      
    0036:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 4)));      
    0037:    end loop;
    0038: end Main;
    0039: 

The PowerAda compiler generates jumps to the end of the case statement rather than to the null alternatives as seen at lines 14 and 23 in the same example, but the end of the case statement seems to be marked at line 23 instead of line 24:

main.adb:
Hit Line  Source
--- ----  ------ - -
    0001: 
    0002: with Text_Io;
    0003: 
    0004: procedure Main is
    0005: 
    0006:    subtype S is Integer range 1..4;
    0007: 
    0008:    function Choose(B : Boolean; I : S) return Integer is
 20 0009:       Result : Integer := 1;
    0010:    begin
 20 0011:       if B then
 20 0012:          case I is
    0013:          when 1 => 
  0 0014:             null;
    0015:    
    0016:          when 2 => 
  5 0017:             Result := Result * 2;
    0018:    
    0019:          when 3 => 
  5 0020:             Result := Result * 3;
    0021:    
    0022:          when 4 => 
 20 0023:             null;
    0024:          end case;
    0025:       else
  0 0026:          Result := 0;
    0027:       end if;
 20 0028:       return Result;
 20 0029:    end Choose;
    0030: 
    0031: begin
    0032:    for I in Integer Range 1..5 Loop
    0033:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 1)));      
    0034:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 2)));      
    0035:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 3)));      
    0036:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 4)));      
    0037:    end loop;
    0038: end Main;
    0039: 

So again, for Gnat we expect non-zero line counts for null alternatives that are selected, and for PowerAda we expect no line count or a line count of 0 for null alternatives, except that a null alternative at the end of a case statement which would show the same line count as the selector.