Add column numbers to branch coverage

* compile.c (DECL_BRANCH_BASE, ADD_TRACE_BRANCH_COVERAGE): Add
  column to arguments.

* compile.c (compile_if, compile_case, compile_when, compile_loop, iseq_compile_each0):
  Pass column numbers to macros.

* ext/coverage/coverage.c (branch_coverage): Add column numbers to
  a return value.

* test/coverage/test_coverage.rb: Follow-up these changes.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60362 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
yui-knk 2017-10-22 13:18:40 +00:00
parent 47d939a7c0
commit 755dd9f461
3 changed files with 41 additions and 32 deletions

View File

@ -251,7 +251,7 @@ struct iseq_compile_data_ensure_node_stack {
ADD_INSN2((seq), (line), trace2, INT2FIX(RUBY_EVENT_COVERAGE), INT2FIX(COVERAGE_INDEX_LINES)); \ ADD_INSN2((seq), (line), trace2, INT2FIX(RUBY_EVENT_COVERAGE), INT2FIX(COVERAGE_INDEX_LINES)); \
} \ } \
} while (0) } while (0)
#define DECL_BRANCH_BASE(branches, line, type) \ #define DECL_BRANCH_BASE(branches, line, column, type) \
do { \ do { \
if (ISEQ_COVERAGE(iseq) && \ if (ISEQ_COVERAGE(iseq) && \
ISEQ_BRANCH_COVERAGE(iseq) && \ ISEQ_BRANCH_COVERAGE(iseq) && \
@ -261,9 +261,10 @@ struct iseq_compile_data_ensure_node_stack {
rb_ary_push(structure, branches); \ rb_ary_push(structure, branches); \
rb_ary_push(branches, ID2SYM(rb_intern(type))); \ rb_ary_push(branches, ID2SYM(rb_intern(type))); \
rb_ary_push(branches, INT2FIX(line)); \ rb_ary_push(branches, INT2FIX(line)); \
rb_ary_push(branches, INT2FIX(column)); \
} \ } \
} while (0) } while (0)
#define ADD_TRACE_BRANCH_COVERAGE(seq, line, type, branches) \ #define ADD_TRACE_BRANCH_COVERAGE(seq, line, column, type, branches) \
do { \ do { \
if (ISEQ_COVERAGE(iseq) && \ if (ISEQ_COVERAGE(iseq) && \
ISEQ_BRANCH_COVERAGE(iseq) && \ ISEQ_BRANCH_COVERAGE(iseq) && \
@ -273,6 +274,7 @@ struct iseq_compile_data_ensure_node_stack {
rb_ary_push(counters, INT2FIX(0)); \ rb_ary_push(counters, INT2FIX(0)); \
rb_ary_push(branches, ID2SYM(rb_intern(type))); \ rb_ary_push(branches, ID2SYM(rb_intern(type))); \
rb_ary_push(branches, INT2FIX(line)); \ rb_ary_push(branches, INT2FIX(line)); \
rb_ary_push(branches, INT2FIX(column)); \
rb_ary_push(branches, INT2FIX(counter_idx)); \ rb_ary_push(branches, INT2FIX(counter_idx)); \
ADD_INSN2((seq), (line), trace2, INT2FIX(RUBY_EVENT_COVERAGE), INT2FIX(counter_idx * 16 + COVERAGE_INDEX_BRANCHES)); \ ADD_INSN2((seq), (line), trace2, INT2FIX(RUBY_EVENT_COVERAGE), INT2FIX(counter_idx * 16 + COVERAGE_INDEX_BRANCHES)); \
} \ } \
@ -4258,6 +4260,7 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped, cons
NODE *node_else = type == NODE_IF ? node->nd_else : node->nd_body; NODE *node_else = type == NODE_IF ? node->nd_else : node->nd_body;
const int line = nd_line(node); const int line = nd_line(node);
const int column = nd_column(node);
DECL_ANCHOR(cond_seq); DECL_ANCHOR(cond_seq);
DECL_ANCHOR(then_seq); DECL_ANCHOR(then_seq);
DECL_ANCHOR(else_seq); DECL_ANCHOR(else_seq);
@ -4279,13 +4282,13 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped, cons
ADD_SEQ(ret, cond_seq); ADD_SEQ(ret, cond_seq);
if (then_label->refcnt && else_label->refcnt) { if (then_label->refcnt && else_label->refcnt) {
DECL_BRANCH_BASE(branches, line, type == NODE_IF ? "if" : "unless"); DECL_BRANCH_BASE(branches, line, column, type == NODE_IF ? "if" : "unless");
} }
if (then_label->refcnt) { if (then_label->refcnt) {
ADD_LABEL(ret, then_label); ADD_LABEL(ret, then_label);
if (else_label->refcnt) { if (else_label->refcnt) {
ADD_TRACE_BRANCH_COVERAGE(ret, node_body ? nd_line(node_body) : line, type == NODE_IF ? "then" : "else", branches); ADD_TRACE_BRANCH_COVERAGE(ret, node_body ? nd_line(node_body) : line, node_body ? nd_column(node_body) : column, type == NODE_IF ? "then" : "else", branches);
} }
ADD_SEQ(ret, then_seq); ADD_SEQ(ret, then_seq);
end_label = NEW_LABEL(line); end_label = NEW_LABEL(line);
@ -4295,7 +4298,7 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped, cons
if (else_label->refcnt) { if (else_label->refcnt) {
ADD_LABEL(ret, else_label); ADD_LABEL(ret, else_label);
if (then_label->refcnt) { if (then_label->refcnt) {
ADD_TRACE_BRANCH_COVERAGE(ret, node_else ? nd_line(node_else) : line, type == NODE_IF ? "else" : "then", branches); ADD_TRACE_BRANCH_COVERAGE(ret, node_else ? nd_line(node_else) : line, node_else ? nd_column(node_else) : column, type == NODE_IF ? "else" : "then", branches);
} }
ADD_SEQ(ret, else_seq); ADD_SEQ(ret, else_seq);
} }
@ -4318,7 +4321,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped)
DECL_ANCHOR(cond_seq); DECL_ANCHOR(cond_seq);
int only_special_literals = 1; int only_special_literals = 1;
VALUE literals = rb_hash_new(); VALUE literals = rb_hash_new();
int line; int line, column;
enum node_type type; enum node_type type;
VALUE branches = 0; VALUE branches = 0;
@ -4334,11 +4337,12 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped)
} }
CHECK(COMPILE(head, "case base", node->nd_head)); CHECK(COMPILE(head, "case base", node->nd_head));
DECL_BRANCH_BASE(branches, nd_line(node), "case"); DECL_BRANCH_BASE(branches, nd_line(node), nd_column(node), "case");
node = node->nd_body; node = node->nd_body;
type = nd_type(node); type = nd_type(node);
line = nd_line(node); line = nd_line(node);
column = nd_column(node);
if (type != NODE_WHEN) { if (type != NODE_WHEN) {
COMPILE_ERROR(ERROR_ARGS "NODE_CASE: unexpected node. must be NODE_WHEN, but %s", ruby_node_name(type)); COMPILE_ERROR(ERROR_ARGS "NODE_CASE: unexpected node. must be NODE_WHEN, but %s", ruby_node_name(type));
@ -4356,7 +4360,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped)
l1 = NEW_LABEL(line); l1 = NEW_LABEL(line);
ADD_LABEL(body_seq, l1); ADD_LABEL(body_seq, l1);
ADD_INSN(body_seq, line, pop); ADD_INSN(body_seq, line, pop);
ADD_TRACE_BRANCH_COVERAGE(body_seq, node->nd_body ? nd_line(node->nd_body) : line, "when", branches); ADD_TRACE_BRANCH_COVERAGE(body_seq, node->nd_body ? nd_line(node->nd_body) : line, node->nd_body ? nd_column(node->nd_body) : column, "when", branches);
CHECK(COMPILE_(body_seq, "when body", node->nd_body, popped)); CHECK(COMPILE_(body_seq, "when body", node->nd_body, popped));
ADD_INSNL(body_seq, line, jump, endlabel); ADD_INSNL(body_seq, line, jump, endlabel);
@ -4389,12 +4393,13 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped)
} }
type = nd_type(node); type = nd_type(node);
line = nd_line(node); line = nd_line(node);
column = nd_column(node);
} }
/* else */ /* else */
if (node) { if (node) {
ADD_LABEL(cond_seq, elselabel); ADD_LABEL(cond_seq, elselabel);
ADD_INSN(cond_seq, line, pop); ADD_INSN(cond_seq, line, pop);
ADD_TRACE_BRANCH_COVERAGE(cond_seq, nd_line(node), "else", branches); ADD_TRACE_BRANCH_COVERAGE(cond_seq, nd_line(node), nd_column(node), "else", branches);
CHECK(COMPILE_(cond_seq, "else", node, popped)); CHECK(COMPILE_(cond_seq, "else", node, popped));
ADD_INSNL(cond_seq, line, jump, endlabel); ADD_INSNL(cond_seq, line, jump, endlabel);
} }
@ -4402,7 +4407,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped)
debugs("== else (implicit)\n"); debugs("== else (implicit)\n");
ADD_LABEL(cond_seq, elselabel); ADD_LABEL(cond_seq, elselabel);
ADD_INSN(cond_seq, nd_line(tempnode), pop); ADD_INSN(cond_seq, nd_line(tempnode), pop);
ADD_TRACE_BRANCH_COVERAGE(cond_seq, nd_line(tempnode), "else", branches); ADD_TRACE_BRANCH_COVERAGE(cond_seq, nd_line(tempnode), nd_column(tempnode), "else", branches);
if (!popped) { if (!popped) {
ADD_INSN(cond_seq, nd_line(tempnode), putnil); ADD_INSN(cond_seq, nd_line(tempnode), putnil);
} }
@ -4433,16 +4438,17 @@ compile_when(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped)
DECL_ANCHOR(body_seq); DECL_ANCHOR(body_seq);
VALUE branches = 0; VALUE branches = 0;
DECL_BRANCH_BASE(branches, nd_line(node), "case"); DECL_BRANCH_BASE(branches, nd_line(node), nd_column(node), "case");
INIT_ANCHOR(body_seq); INIT_ANCHOR(body_seq);
endlabel = NEW_LABEL(nd_line(node)); endlabel = NEW_LABEL(nd_line(node));
while (node && nd_type(node) == NODE_WHEN) { while (node && nd_type(node) == NODE_WHEN) {
const int line = nd_line(node); const int line = nd_line(node);
const int column = nd_column(node);
LABEL *l1 = NEW_LABEL(line); LABEL *l1 = NEW_LABEL(line);
ADD_LABEL(body_seq, l1); ADD_LABEL(body_seq, l1);
ADD_TRACE_BRANCH_COVERAGE(body_seq, node->nd_body ? nd_line(node->nd_body) : line, "when", branches); ADD_TRACE_BRANCH_COVERAGE(body_seq, node->nd_body ? nd_line(node->nd_body) : line, node->nd_body ? nd_column(node->nd_body) : column, "when", branches);
CHECK(COMPILE_(body_seq, "when", node->nd_body, popped)); CHECK(COMPILE_(body_seq, "when", node->nd_body, popped));
ADD_INSNL(body_seq, line, jump, endlabel); ADD_INSNL(body_seq, line, jump, endlabel);
@ -4474,7 +4480,7 @@ compile_when(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped)
node = node->nd_next; node = node->nd_next;
} }
/* else */ /* else */
ADD_TRACE_BRANCH_COVERAGE(ret, node ? nd_line(node) : nd_line(orig_node), "else", branches); ADD_TRACE_BRANCH_COVERAGE(ret, node ? nd_line(node) : nd_line(orig_node), node ? nd_column(node) : nd_column(orig_node), "else", branches);
CHECK(COMPILE_(ret, "else", node, popped)); CHECK(COMPILE_(ret, "else", node, popped));
ADD_INSNL(ret, nd_line(orig_node), jump, endlabel); ADD_INSNL(ret, nd_line(orig_node), jump, endlabel);
@ -4487,6 +4493,7 @@ static int
compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped, const enum node_type type) compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped, const enum node_type type)
{ {
const int line = (int)nd_line(node); const int line = (int)nd_line(node);
const int column = nd_column(node);
LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label; LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label; LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label; LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
@ -4522,8 +4529,8 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped, co
if (tmp_label) ADD_LABEL(ret, tmp_label); if (tmp_label) ADD_LABEL(ret, tmp_label);
ADD_LABEL(ret, redo_label); ADD_LABEL(ret, redo_label);
DECL_BRANCH_BASE(branches, line, type == NODE_WHILE ? "while" : "until"); DECL_BRANCH_BASE(branches, line, column, type == NODE_WHILE ? "while" : "until");
ADD_TRACE_BRANCH_COVERAGE(ret, node->nd_body ? nd_line(node->nd_body) : line, "body", branches); ADD_TRACE_BRANCH_COVERAGE(ret, node->nd_body ? nd_line(node->nd_body) : line, node->nd_body ? nd_column(node->nd_body) : column, "body", branches);
CHECK(COMPILE_POPPED(ret, "while body", node->nd_body)); CHECK(COMPILE_POPPED(ret, "while body", node->nd_body));
ADD_LABEL(ret, next_label); /* next */ ADD_LABEL(ret, next_label); /* next */
@ -5723,10 +5730,10 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popp
else_label = NEW_LABEL(line); else_label = NEW_LABEL(line);
end_label = NEW_LABEL(line); end_label = NEW_LABEL(line);
DECL_BRANCH_BASE(branches, nd_line(node), "&."); DECL_BRANCH_BASE(branches, nd_line(node), nd_column(node), "&.");
ADD_INSN(recv, line, dup); ADD_INSN(recv, line, dup);
ADD_INSNL(recv, line, branchnil, else_label); ADD_INSNL(recv, line, branchnil, else_label);
ADD_TRACE_BRANCH_COVERAGE(recv, nd_line(node), "then", branches); ADD_TRACE_BRANCH_COVERAGE(recv, nd_line(node), nd_column(node), "then", branches);
} }
} }
else if (type == NODE_FCALL || type == NODE_VCALL) { else if (type == NODE_FCALL || type == NODE_VCALL) {
@ -5761,7 +5768,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popp
if (else_label && end_label) { if (else_label && end_label) {
ADD_INSNL(ret, line, jump, end_label); ADD_INSNL(ret, line, jump, end_label);
ADD_LABEL(ret, else_label); ADD_LABEL(ret, else_label);
ADD_TRACE_BRANCH_COVERAGE(ret, nd_line(node), "else", branches); ADD_TRACE_BRANCH_COVERAGE(ret, nd_line(node), nd_column(node), "else", branches);
ADD_LABEL(ret, end_label); ADD_LABEL(ret, end_label);
} }
if (popped) { if (popped) {

View File

@ -82,13 +82,15 @@ branch_coverage(VALUE branches)
VALUE branches = RARRAY_AREF(structure, i); VALUE branches = RARRAY_AREF(structure, i);
VALUE base_type = RARRAY_AREF(branches, 0); VALUE base_type = RARRAY_AREF(branches, 0);
VALUE base_lineno = RARRAY_AREF(branches, 1); VALUE base_lineno = RARRAY_AREF(branches, 1);
VALUE base_column = RARRAY_AREF(branches, 2);
VALUE children = rb_hash_new(); VALUE children = rb_hash_new();
rb_hash_aset(ret, rb_ary_new_from_args(3, base_type, LONG2FIX(id++), base_lineno), children); rb_hash_aset(ret, rb_ary_new_from_args(4, base_type, LONG2FIX(id++), base_lineno, base_column), children);
for (j = 2; j < RARRAY_LEN(branches); j += 3) { for (j = 3; j < RARRAY_LEN(branches); j += 4) {
VALUE target_label = RARRAY_AREF(branches, j); VALUE target_label = RARRAY_AREF(branches, j);
VALUE target_lineno = RARRAY_AREF(branches, j + 1); VALUE target_lineno = RARRAY_AREF(branches, j + 1);
int idx = FIX2INT(RARRAY_AREF(branches, j + 2)); VALUE target_column = RARRAY_AREF(branches, j + 2);
rb_hash_aset(children, rb_ary_new_from_args(3, target_label, LONG2FIX(id++), target_lineno), RARRAY_AREF(counters, idx)); int idx = FIX2INT(RARRAY_AREF(branches, j + 3));
rb_hash_aset(children, rb_ary_new_from_args(4, target_label, LONG2FIX(id++), target_lineno, target_column), RARRAY_AREF(counters, idx));
} }
} }

View File

@ -197,8 +197,8 @@ class TestCoverage < Test::Unit::TestCase
def test_branch_coverage_for_if_statement def test_branch_coverage_for_if_statement
result = { result = {
:branches => { :branches => {
[:if , 0, 2] => {[:then, 1, 3]=>2, [:else, 2, 5]=>1}, [:if , 0, 2, 8] => {[:then, 1, 3, 10]=>2, [:else, 2, 5, 10]=>1},
[:unless, 3, 8] => {[:else, 4, 11]=>2, [:then, 5, 9]=>1}, [:unless, 3, 8, 8] => {[:else, 4, 11, 10]=>2, [:then, 5, 9, 10]=>1},
} }
} }
assert_coverage(<<-"end;", { branches: true }, result) assert_coverage(<<-"end;", { branches: true }, result)
@ -225,8 +225,8 @@ class TestCoverage < Test::Unit::TestCase
def test_branch_coverage_for_while_statement def test_branch_coverage_for_while_statement
result = { result = {
:branches => { :branches => {
[:while, 0, 2] => {[:body, 1, 3]=> 3}, [:while, 0, 2, 6] => {[:body, 1, 3, 8]=> 3},
[:until, 2, 5] => {[:body, 3, 6]=>10}, [:until, 2, 5, 6] => {[:body, 3, 6, 8]=>10},
} }
} }
assert_coverage(<<-"end;", { branches: true }, result) assert_coverage(<<-"end;", { branches: true }, result)
@ -243,10 +243,10 @@ class TestCoverage < Test::Unit::TestCase
def test_branch_coverage_for_case_statement def test_branch_coverage_for_case_statement
result = { result = {
:branches => { :branches => {
[:case, 0, 2] => {[:when, 1, 4]=>2, [:when, 2, 6]=>0, [:else, 3, 2]=>1}, [:case, 0, 2, 8] => {[:when, 1, 4, 10]=>2, [:when, 2, 6, 10]=>0, [:else, 3, 2, 8]=>1},
[:case, 4, 9] => {[:when, 5, 11]=>2, [:when, 6, 13]=>0, [:else, 7, 9]=>1}, [:case, 4, 9, 8] => {[:when, 5, 11, 10]=>2, [:when, 6, 13, 10]=>0, [:else, 7, 9, 8]=>1},
[:case, 8, 16] => {[:when, 9, 18]=>2, [:when, 10, 20]=>0, [:else, 11, 22]=>1}, [:case, 8, 16, 8] => {[:when, 9, 18, 10]=>2, [:when, 10, 20, 10]=>0, [:else, 11, 22, 10]=>1},
[:case, 12, 25] => {[:when, 13, 27]=>2, [:when, 14, 29]=>0, [:else, 15, 31]=>1}, [:case, 12, 25, 8] => {[:when, 13, 27, 10]=>2, [:when, 14, 29, 10]=>0, [:else, 15, 31, 10]=>1},
} }
} }
assert_coverage(<<-"end;", { branches: true }, result) assert_coverage(<<-"end;", { branches: true }, result)
@ -293,8 +293,8 @@ class TestCoverage < Test::Unit::TestCase
def test_branch_coverage_for_safe_method_invocation def test_branch_coverage_for_safe_method_invocation
result = { result = {
:branches=>{ :branches=>{
[:"&.", 0, 3] => {[:then, 1, 3]=>1, [:else, 2, 3]=>0}, [:"&.", 0, 3, 6] => {[:then, 1, 3, 6]=>1, [:else, 2, 3, 6]=>0},
[:"&.", 3, 4] => {[:then, 4, 4]=>0, [:else, 5, 4]=>1}, [:"&.", 3, 4, 6] => {[:then, 4, 4, 6]=>0, [:else, 5, 4, 6]=>1},
} }
} }
assert_coverage(<<-"end;", { branches: true }, result) assert_coverage(<<-"end;", { branches: true }, result)