Coverage Of Null If Alternatives

From OC Systems Wiki!
Jump to: navigation, search

How do I interpret coverage line counts for null if alternatives?

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

The GNAT compiler does both: the null alternative on line 12 is counted and the null alternative on line 21 is not 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:       if I = 1 then
  5 0012:          null;
    0013:   
 15 0014:       elsif I = 2 then 
  5 0015:          Result := Result * 2;
    0016:  
 10 0017:       elsif I = 3 then
  5 0018:          Result := Result * 3;
    0019: 
  5 0020:       elsif I = 4 then
    0021:          null;
    0022:       end if;
 20 0023:       return Result;
 20 0024:    end Choose;
    0025: 
    0026: begin
    0027:    for I in Integer Range 1..5 Loop
    0028:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(1)));      
    0029:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(2)));      
    0030:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(3)));      
    0031:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(4)));      
    0032:    end loop;
    0033: end Main;
    0034: 

The PowerAda compiler does the same thing (see lines 12 and 21) 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:       if I = 1 then
  5 0012:          null;
    0013:   
 15 0014:       elsif I = 2 then 
  5 0015:          Result := Result * 2;
    0016:  
 10 0017:       elsif I = 3 then
  5 0018:          Result := Result * 3;
    0019: 
  5 0020:       elsif I = 4 then
    0021:          null;
    0022:       end if;
 20 0023:       return Result;
 20 0024:    end Choose;
    0025: 
    0026: begin
    0027:    for I in Integer Range 1..5 Loop
    0028:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(1)));      
    0029:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(2)));      
    0030:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(3)));      
    0031:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(4)));      
    0032:    end loop;
    0033: end Main;
    0034: 

When the if statement with null alternatives is nested on another control structure (like an if statement) the compiler may generate different code.

The GNAT compiler does not generate different code so the line counts are the same as the previous 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:          if I = 1 then
  5 0013:             null;
    0014:    
 15 0015:          elsif I = 2 then 
  5 0016:             Result := Result * 2;
    0017:    
 10 0018:          elsif I = 3 then
  5 0019:             Result := Result * 3;
    0020:    
  5 0021:          elsif I = 4 then
    0022:             null;
    0023:          end if;
    0024:       else
  0 0025:          Result := 0;
    0026:       end if;
 20 0027:       return Result;
 20 0028:    end Choose;
    0029: 
    0030: begin
    0031:    for I in Integer Range 1..5 Loop
    0032:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 1)));      
    0033:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 2)));      
    0034:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 3)));      
    0035:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 4)));      
    0036:    end loop;
    0037: end Main;
    0038: 

The PowerAda compiler associates line 22 (the null alternative) with the end of the nested if statement (rather than line 23, the actual end):

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:          if I = 1 then
  5 0013:             null;
    0014:    
 15 0015:          elsif I = 2 then 
  5 0016:             Result := Result * 2;
    0017:    
 10 0018:          elsif I = 3 then
  5 0019:             Result := Result * 3;
    0020:    
  5 0021:          elsif I = 4 then
 20 0022:             null;
    0023:          end if;
    0024:       else
  0 0025:          Result := 0;
    0026:       end if;
 20 0027:       return Result;
 20 0028:    end Choose;
    0029: 
    0030: begin
    0031:    for I in Integer Range 1..5 Loop
    0032:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 1)));      
    0033:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 2)));      
    0034:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 3)));      
    0035:       Text_Io.Put_Line("Result is " & Integer'Image(Choose(true, 4)));      
    0036:    end loop;
    0037: end Main;
    0038: 

So for if statements with null alternatives you can rely on the line counts if the alternative is not the last, and you should use some judgment for null alternatives that are the last.