mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Introduce NODE_UNLESS for branch coverage
`unless` statement was a syntactic sugar for `if` statement, which made the result of branch coverage hard to understand. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59889 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
78cf46078e
commit
3c8c17d30d
5 changed files with 43 additions and 10 deletions
18
compile.c
18
compile.c
|
@ -4178,8 +4178,11 @@ number_literal_p(NODE *n)
|
|||
}
|
||||
|
||||
static int
|
||||
compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped)
|
||||
compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped, const enum node_type type)
|
||||
{
|
||||
NODE *node_body = type == NODE_IF ? node->nd_body : node->nd_else;
|
||||
NODE *node_else = type == NODE_IF ? node->nd_else : node->nd_body;
|
||||
|
||||
const int line = nd_line(node);
|
||||
DECL_ANCHOR(cond_seq);
|
||||
DECL_ANCHOR(then_seq);
|
||||
|
@ -4196,19 +4199,19 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped)
|
|||
|
||||
compile_branch_condition(iseq, cond_seq, node->nd_cond,
|
||||
then_label, else_label);
|
||||
CHECK(COMPILE_(then_seq, "then", node->nd_body, popped));
|
||||
CHECK(COMPILE_(else_seq, "else", node->nd_else, popped));
|
||||
CHECK(COMPILE_(then_seq, "then", node_body, popped));
|
||||
CHECK(COMPILE_(else_seq, "else", node_else, popped));
|
||||
|
||||
ADD_SEQ(ret, cond_seq);
|
||||
|
||||
if (then_label->refcnt && else_label->refcnt) {
|
||||
DECL_BRANCH_BASE(branches, line, "if");
|
||||
DECL_BRANCH_BASE(branches, line, type == NODE_IF ? "if" : "unless");
|
||||
}
|
||||
|
||||
if (then_label->refcnt) {
|
||||
ADD_LABEL(ret, then_label);
|
||||
if (else_label->refcnt) {
|
||||
ADD_TRACE_BRANCH_COVERAGE(ret, node->nd_body ? nd_line(node->nd_body) : line, "then", branches);
|
||||
ADD_TRACE_BRANCH_COVERAGE(ret, node_body ? nd_line(node_body) : line, type == NODE_IF ? "then" : "else", branches);
|
||||
}
|
||||
ADD_SEQ(ret, then_seq);
|
||||
end_label = NEW_LABEL(line);
|
||||
|
@ -4218,7 +4221,7 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped)
|
|||
if (else_label->refcnt) {
|
||||
ADD_LABEL(ret, else_label);
|
||||
if (then_label->refcnt) {
|
||||
ADD_TRACE_BRANCH_COVERAGE(ret, node->nd_else ? nd_line(node->nd_else) : line, "else", branches);
|
||||
ADD_TRACE_BRANCH_COVERAGE(ret, node_else ? nd_line(node_else) : line, type == NODE_IF ? "else" : "then", branches);
|
||||
}
|
||||
ADD_SEQ(ret, else_seq);
|
||||
}
|
||||
|
@ -4936,7 +4939,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popp
|
|||
break;
|
||||
}
|
||||
case NODE_IF:
|
||||
CHECK(compile_if(iseq, ret, node, popped));
|
||||
case NODE_UNLESS:
|
||||
CHECK(compile_if(iseq, ret, node, popped, type));
|
||||
break;
|
||||
case NODE_CASE:
|
||||
CHECK(compile_case(iseq, ret, node, popped));
|
||||
|
|
10
node.c
10
node.c
|
@ -200,6 +200,16 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
|
|||
F_NODE(nd_else, "else clause");
|
||||
break;
|
||||
|
||||
case NODE_UNLESS:
|
||||
ANN("unless statement");
|
||||
ANN("format: unless [nd_cond] then [nd_body] else [nd_else] end");
|
||||
ANN("example: unless x == 1 then foo else bar end");
|
||||
F_NODE(nd_cond, "condition expr");
|
||||
F_NODE(nd_body, "then clause");
|
||||
LAST_NODE;
|
||||
F_NODE(nd_else, "else clause");
|
||||
break;
|
||||
|
||||
case NODE_CASE:
|
||||
ANN("case statement");
|
||||
ANN("format: case [nd_head]; [nd_body]; end");
|
||||
|
|
4
node.h
4
node.h
|
@ -26,6 +26,8 @@ enum node_type {
|
|||
#define NODE_BLOCK NODE_BLOCK
|
||||
NODE_IF,
|
||||
#define NODE_IF NODE_IF
|
||||
NODE_UNLESS,
|
||||
#define NODE_UNLESS NODE_UNLESS
|
||||
NODE_CASE,
|
||||
#define NODE_CASE NODE_CASE
|
||||
NODE_WHEN,
|
||||
|
@ -362,7 +364,7 @@ typedef struct RNode {
|
|||
#define NEW_SCOPE(a,b) NEW_NODE(NODE_SCOPE,local_tbl(),b,a)
|
||||
#define NEW_BLOCK(a) NEW_NODE(NODE_BLOCK,a,0,0)
|
||||
#define NEW_IF(c,t,e) NEW_NODE(NODE_IF,c,t,e)
|
||||
#define NEW_UNLESS(c,t,e) NEW_IF(c,e,t)
|
||||
#define NEW_UNLESS(c,t,e) NEW_NODE(NODE_UNLESS,c,t,e)
|
||||
#define NEW_CASE(h,b) NEW_NODE(NODE_CASE,h,b,0)
|
||||
#define NEW_WHEN(c,t,e) NEW_NODE(NODE_WHEN,c,t,e)
|
||||
#define NEW_OPT_N(b) NEW_NODE(NODE_OPT_N,0,b,0)
|
||||
|
|
13
parse.y
13
parse.y
|
@ -352,7 +352,8 @@ static NODE *cond_gen(struct parser_params*,NODE*,int);
|
|||
#define new_nil() NEW_NIL()
|
||||
static NODE *new_if_gen(struct parser_params*,NODE*,NODE*,NODE*);
|
||||
#define new_if(cc,left,right) new_if_gen(parser, (cc), (left), (right))
|
||||
#define new_unless(cc,left,right) new_if_gen(parser, (cc), (right), (left))
|
||||
static NODE *new_unless_gen(struct parser_params*,NODE*,NODE*,NODE*);
|
||||
#define new_unless(cc,left,right) new_unless_gen(parser, (cc), (left), (right))
|
||||
static NODE *logop_gen(struct parser_params*,enum node_type,NODE*,NODE*);
|
||||
#define logop(id,node1,node2) \
|
||||
logop_gen(parser, ((id)==idAND||(id)==idANDOP)?NODE_AND:NODE_OR, \
|
||||
|
@ -9533,6 +9534,7 @@ value_expr_gen(struct parser_params *parser, NODE *node)
|
|||
break;
|
||||
|
||||
case NODE_IF:
|
||||
case NODE_UNLESS:
|
||||
if (!node->nd_body) {
|
||||
node = node->nd_else;
|
||||
break;
|
||||
|
@ -9711,6 +9713,7 @@ reduce_nodes_gen(struct parser_params *parser, NODE **body)
|
|||
body = &node->nd_end->nd_head;
|
||||
break;
|
||||
case NODE_IF:
|
||||
case NODE_UNLESS:
|
||||
if (subnodes(nd_body, nd_else)) break;
|
||||
return;
|
||||
case NODE_CASE:
|
||||
|
@ -9913,6 +9916,14 @@ new_if_gen(struct parser_params *parser, NODE *cc, NODE *left, NODE *right)
|
|||
return newline_node(NEW_IF(cc, left, right));
|
||||
}
|
||||
|
||||
static NODE*
|
||||
new_unless_gen(struct parser_params *parser, NODE *cc, NODE *left, NODE *right)
|
||||
{
|
||||
if (!cc) return right;
|
||||
cc = cond0(parser, cc, FALSE);
|
||||
return newline_node(NEW_UNLESS(cc, left, right));
|
||||
}
|
||||
|
||||
static NODE*
|
||||
logop_gen(struct parser_params *parser, enum node_type type, NODE *left, NODE *right)
|
||||
{
|
||||
|
|
|
@ -186,13 +186,19 @@ class TestCoverage < Test::Unit::TestCase
|
|||
f.puts ' else'
|
||||
f.puts ' 1'
|
||||
f.puts ' end'
|
||||
f.puts ''
|
||||
f.puts ' unless x == 0'
|
||||
f.puts ' 0'
|
||||
f.puts ' else'
|
||||
f.puts ' 1'
|
||||
f.puts ' end'
|
||||
f.puts 'end'
|
||||
f.puts 'foo(0)'
|
||||
f.puts 'foo(0)'
|
||||
f.puts 'foo(1)'
|
||||
end
|
||||
|
||||
assert_in_out_err(%w[-W0 -rcoverage], <<-"end;", ["{:branches=>{[:if, 0, 2]=>{[:then, 1, 3]=>2, [:else, 2, 5]=>1}}}"], [])
|
||||
assert_in_out_err(%w[-W0 -rcoverage], <<-"end;", ["{:branches=>{[:if, 0, 2]=>{[:then, 1, 3]=>2, [:else, 2, 5]=>1}, [:unless, 3, 8]=>{[:else, 4, 11]=>2, [:then, 5, 9]=>1}}}"], [])
|
||||
ENV["COVERAGE_EXPERIMENTAL_MODE"] = "true"
|
||||
Coverage.start(branches: true)
|
||||
tmp = Dir.pwd
|
||||
|
|
Loading…
Reference in a new issue