Skip to content

Commit

Permalink
Fix extracting docstring from constructors (ponylang#2586)
Browse files Browse the repository at this point in the history
from classes or actors with initialized fields
  • Loading branch information
mfelsche authored and jemc committed Mar 21, 2018
1 parent 98bbbbb commit a8251ab
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 27 deletions.
49 changes: 26 additions & 23 deletions src/libponyc/pass/sugar.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,30 @@ static ast_result_t sugar_module(pass_opt_t* opt, ast_t* ast)
}


static void sugar_docstring(ast_t* ast)
{
pony_assert(ast != NULL);

AST_GET_CHILDREN(ast, cap, id, type_params, params, return_type,
error, body, docstring);

if(ast_id(docstring) == TK_NONE)
{
ast_t* first = ast_child(body);

// First expression in body is a docstring if it is a string literal and
// there are any other expressions in the body sequence
if((first != NULL) &&
(ast_id(first) == TK_STRING) &&
(ast_sibling(first) != NULL))
{
ast_pop(body);
ast_replace(&docstring, first);
}
}
}


static ast_result_t sugar_entity(pass_opt_t* opt, ast_t* ast, bool add_create,
token_id def_def_cap)
{
Expand Down Expand Up @@ -216,6 +240,8 @@ static ast_result_t sugar_entity(pass_opt_t* opt, ast_t* ast, bool add_create,

pony_assert(ast_id(n_body) == TK_SEQ);

sugar_docstring(member);

ast_t* init = ast_child(init_seq);

while(init != NULL)
Expand Down Expand Up @@ -257,29 +283,6 @@ static ast_result_t sugar_typeparam(ast_t* ast)
}


static void sugar_docstring(ast_t* ast)
{
pony_assert(ast != NULL);

AST_GET_CHILDREN(ast, cap, id, type_params, params, return_type,
error, body, docstring);

if(ast_id(docstring) == TK_NONE)
{
ast_t* first = ast_child(body);

// First expression in body is a docstring if it is a string literal and
// there are any other expressions in the body sequence
if((first != NULL) &&
(ast_id(first) == TK_STRING) &&
(ast_sibling(first) != NULL))
{
ast_pop(body);
ast_replace(&docstring, first);
}
}
}


static ast_result_t sugar_new(pass_opt_t* opt, ast_t* ast)
{
Expand Down
169 changes: 165 additions & 4 deletions test/libponyc/sugar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,21 +121,182 @@ TEST_F(SugarTest, ClassWithoutDefCap)
}


TEST_F(SugarTest, ActorWithInitialisedField)
TEST_F(SugarTest, ClassWithInitializedField)
{
// Create constructor should be added
const char* short_form =
"actor Foo";
"class Foo\n"
" let x: U8 = 1\n";

const char* full_form =
"use \"builtin\"\n"
"class ref Foo\n"
" let x: U8\n"
" new iso create(): Foo iso^ =>\n"
" x = 1\n"
" true\n";
TEST_EQUIV(short_form, full_form);
}

TEST_F(SugarTest, ClassWithInitializedFieldAndManyConstructors)
{
const char* short_form =
"class Foo\n"
" let x: U8 = 1\n"
" \n"
" new create1() => 1\n"
" new create2() => 2\n";
const char* full_form =
"use \"builtin\"\n"
"class ref Foo\n"
" let x: U8\n"
" \n"
" new ref create1(): Foo ref^ =>\n"
" x = 1\n"
" 1\n"
" \n"
" new ref create2(): Foo ref^ =>\n"
" x = 1\n"
" 2\n";

TEST_EQUIV(short_form, full_form);
}

TEST_F(SugarTest, ClassWithInitializedFieldsAndDocString)
{
const char* short_form =
"class Foo\n"
" let x: U8 = 1\n"
" \n"
" new create() =>\n"
" \"\"\"\n"
" constructor docstring\n"
" \"\"\"\n"
" None\n";

TEST_COMPILE(short_form);
ast_t* foo = ast_childlast(module);
ASSERT_NE(ast_id(foo), TK_NONE);

AST_GET_CHILDREN(foo, id, typeparams, defcap, traits, members);

ast_t* member = ast_child(members);
while(member != NULL)
{
switch(ast_id(member))
{
case TK_NEW:
{
AST_GET_CHILDREN(member, cap, id, type_params, params, return_type,
error, body, docstring);

ASSERT_EQ(ast_id(docstring), TK_STRING) <<
"docstring has not been extracted from the constructor body";
ASSERT_STREQ(ast_name(docstring), "constructor docstring\n") <<
"docstring has not been extracted correctly";

ASSERT_EQ(ast_childcount(body), 2) <<
"docstring has not been purged from the iconstructor body";
return;
}
default:
{}
}
member = ast_sibling(member);
}
FAIL() << "no constructor found";
}


TEST_F(SugarTest, ActorWithInitializedField)
{
// initializer should be added to every constructor
const char* short_form =
"actor Foo\n"
" let x: U8 = 1\n";

const char* full_form =
"use \"builtin\"\n"
"actor tag Foo\n"
" new tag create(): Foo tag^ => true";
" let x: U8\n"
" new tag create(): Foo tag^ =>\n"
" x = 1\n"
" true\n";

TEST_EQUIV(short_form, full_form);
}


TEST_F(SugarTest, ActorWithInitializedFieldAndManyConstructors)
{
const char* short_form =
"actor Foo\n"
" let x: U8 = 1\n"
" \n"
" new create1() => 1\n"
" new create2() => 2\n";
const char* full_form =
"use \"builtin\"\n"
"actor tag Foo\n"
" let x: U8\n"
" \n"
" new tag create1(): Foo tag^ =>\n"
" x = 1\n"
" 1\n"
" \n"
" new tag create2(): Foo tag^ =>\n"
" x = 1\n"
" 2\n";

TEST_EQUIV(short_form, full_form);
}


TEST_F(SugarTest, ActorWithInitializedFieldsAndDocString)
{
const char* short_form =
"actor Foo\n"
" let x: U8 = 1\n"
" \n"
" new create() =>\n"
" \"\"\"\n"
" constructor docstring\n"
" \"\"\"\n"
" None\n";

TEST_COMPILE(short_form);
ast_t* foo = ast_childlast(module);
ASSERT_NE(ast_id(foo), TK_NONE);

AST_GET_CHILDREN(foo, id, typeparams, defcap, traits, members);

ast_t* member = ast_child(members);
while(member != NULL)
{
switch(ast_id(member))
{
case TK_NEW:
{
AST_GET_CHILDREN(member, cap, id, type_params, params, return_type,
error, body, docstring);

ASSERT_EQ(ast_id(docstring), TK_STRING) <<
"docstring has not been extracted from the constructor body";
ASSERT_STREQ(ast_name(docstring), "constructor docstring\n") <<
"docstring has not been extracted correctly";

ASSERT_EQ(ast_childcount(body), 2) <<
"docstring has not been purged from the iconstructor body";
return;
}
default:
{}
}
member = ast_sibling(member);
}
FAIL() << "no constructor found";
}


TEST_F(SugarTest, ActorWithCreateConstructor)
{
// Create constructor should not be added if it's already there
Expand Down

0 comments on commit a8251ab

Please sign in to comment.