Coverage Of Null If Alternatives
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.