Skip to content

Commit 82c7dd3

Browse files
committed
Merge pull request #1815 from xzyfer/feat/rebind
Fix multiple issues with function parameter binding
2 parents 50ebca0 + 1b43017 commit 82c7dd3

File tree

4 files changed

+109
-43
lines changed

4 files changed

+109
-43
lines changed

src/ast.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,6 +1378,36 @@ namespace Sass {
13781378
}
13791379
}
13801380

1381+
Argument* Arguments::get_rest_argument()
1382+
{
1383+
Argument* arg = 0;
1384+
if (this->has_rest_argument()) {
1385+
for (auto a : this->elements()) {
1386+
if (a->is_rest_argument()) {
1387+
arg = a;
1388+
break;
1389+
}
1390+
}
1391+
}
1392+
1393+
return arg;
1394+
}
1395+
1396+
Argument* Arguments::get_keyword_argument()
1397+
{
1398+
Argument* arg = 0;
1399+
if (this->has_keyword_argument()) {
1400+
for (auto a : this->elements()) {
1401+
if (a->is_keyword_argument()) {
1402+
arg = a;
1403+
break;
1404+
}
1405+
}
1406+
}
1407+
1408+
return arg;
1409+
}
1410+
13811411
void Arguments::adjust_after_pushing(Argument* a)
13821412
{
13831413
if (!a->name().empty()) {

src/ast.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,10 @@ namespace Sass {
11131113
has_rest_argument_(false),
11141114
has_keyword_argument_(false)
11151115
{ }
1116+
1117+
Argument* get_rest_argument();
1118+
Argument* get_keyword_argument();
1119+
11161120
ATTACH_OPERATIONS()
11171121
};
11181122

src/bind.cpp

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace Sass {
1212
void bind(std::string type, std::string name, Parameters* ps, Arguments* as, Context* ctx, Env* env, Eval* eval)
1313
{
1414
std::string callee(type + " " + name);
15+
1516
Listize listize(*ctx);
1617
std::map<std::string, Parameter*> param_map;
1718

@@ -60,18 +61,9 @@ namespace Sass {
6061
if (p->is_rest_parameter()) {
6162
// The next argument by coincidence provides a rest argument
6263
if (a->is_rest_argument()) {
64+
6365
// We should always get a list for rest arguments
6466
if (List* rest = dynamic_cast<List*>(a->value())) {
65-
// arg contains a list
66-
List* args = rest;
67-
// make sure it's an arglist
68-
if (rest->is_arglist()) {
69-
// can pass it through as it was
70-
env->local_frame()[p->name()] = args;
71-
}
72-
// create a new list and wrap each item as an argument
73-
// otherwise we will not be able to fetch it again
74-
else {
7567
// create a new list object for wrapped items
7668
List* arglist = SASS_MEMORY_NEW(ctx->mem, List,
7769
p->pstate(),
@@ -80,17 +72,20 @@ namespace Sass {
8072
true);
8173
// wrap each item from list as an argument
8274
for (Expression* item : rest->elements()) {
83-
(*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,
84-
item->pstate(),
85-
item,
86-
"",
87-
false,
88-
false);
75+
if (Argument* arg = dynamic_cast<Argument*>(item)) {
76+
(*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg);
77+
} else {
78+
(*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,
79+
item->pstate(),
80+
item,
81+
"",
82+
false,
83+
false);
84+
}
8985
}
9086
// assign new arglist to environment
9187
env->local_frame()[p->name()] = arglist;
9288
}
93-
}
9489
// invalid state
9590
else {
9691
throw std::runtime_error("invalid state");
@@ -127,27 +122,27 @@ namespace Sass {
127122
List* ls = dynamic_cast<List*>(a->value());
128123
// skip any list completely if empty
129124
if (ls && ls->empty() && a->is_rest_argument()) continue;
130-
// flatten all nested arglists
131-
if (ls && ls->is_arglist()) {
132-
for (size_t i = 0, L = ls->size(); i < L; ++i) {
133-
// already have a wrapped argument
134-
if (Argument* arg = dynamic_cast<Argument*>((*ls)[i])) {
135-
(*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg);
136-
}
137-
// wrap all other value types into Argument
138-
else {
125+
126+
if (Argument* arg = dynamic_cast<Argument*>(a->value())) {
127+
(*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg);
128+
}
129+
// check if we have rest argument
130+
else if (a->is_rest_argument()) {
131+
// preserve the list separator from rest args
132+
if (List* rest = dynamic_cast<List*>(a->value())) {
133+
arglist->separator(rest->separator());
134+
135+
for (size_t i = 0, L = rest->size(); i < L; ++i) {
139136
(*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,
140-
(*ls)[i]->pstate(),
141-
(*ls)[i],
137+
(*rest)[i]->pstate(),
138+
(*rest)[i],
142139
"",
143140
false,
144141
false);
145142
}
146143
}
147-
}
148-
// already have a wrapped argument
149-
else if (Argument* arg = dynamic_cast<Argument*>(a->value())) {
150-
(*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg);
144+
// no more arguments
145+
break;
151146
}
152147
// wrap all other value types into Argument
153148
else {
@@ -158,15 +153,6 @@ namespace Sass {
158153
false,
159154
false);
160155
}
161-
// check if we have rest argument
162-
if (a->is_rest_argument()) {
163-
// preserve the list separator from rest args
164-
if (List* rest = dynamic_cast<List*>(a->value())) {
165-
arglist->separator(rest->separator());
166-
}
167-
// no more arguments
168-
break;
169-
}
170156
}
171157
// assign new arglist to environment
172158
env->local_frame()[p->name()] = arglist;
@@ -185,14 +171,18 @@ namespace Sass {
185171
if (!arglist->length()) {
186172
break;
187173
} else {
188-
if (arglist->length() + ia > LP && !ps->has_rest_parameter()) {
174+
if (arglist->length() > LP - ip && !ps->has_rest_parameter()) {
189175
int arg_count = (arglist->length() + LA - 1);
190176
std::stringstream msg;
191177
msg << callee << " takes " << LP;
192178
msg << (LP == 1 ? " argument" : " arguments");
193179
msg << " but " << arg_count;
194180
msg << (arg_count == 1 ? " was passed" : " were passed.");
195181
deprecated_bind(msg.str(), as->pstate());
182+
183+
while (arglist->length() > LP - ip) {
184+
arglist->elements().erase(arglist->elements().end() - 1);
185+
}
196186
}
197187
}
198188
// otherwise move one of the rest args into the param, converting to argument if necessary
@@ -209,6 +199,7 @@ namespace Sass {
209199
if (!arglist->length() || (!arglist->is_arglist() && ip + 1 == LP)) {
210200
++ia;
211201
}
202+
212203
} else if (a->is_keyword_argument()) {
213204
Map* argmap = static_cast<Map*>(a->value());
214205

src/eval.cpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1185,8 +1185,49 @@ namespace Sass {
11851185
{
11861186
Arguments* aa = SASS_MEMORY_NEW(ctx.mem, Arguments, a->pstate());
11871187
for (size_t i = 0, L = a->length(); i < L; ++i) {
1188-
*aa << static_cast<Argument*>((*a)[i]->perform(this));
1188+
Argument* arg = static_cast<Argument*>((*a)[i]->perform(this));
1189+
if (!(arg->is_rest_argument() || arg->is_keyword_argument())) {
1190+
*aa << arg;
1191+
}
11891192
}
1193+
1194+
if (a->has_rest_argument()) {
1195+
Expression* splat = static_cast<Argument*>(
1196+
a->get_rest_argument()->perform(this)
1197+
)->value()->perform(this);
1198+
1199+
Sass_Separator separator = SASS_COMMA;
1200+
List* ls = dynamic_cast<List*>(splat);
1201+
Map* ms = dynamic_cast<Map*>(splat);
1202+
1203+
List* arglist = SASS_MEMORY_NEW(ctx.mem, List,
1204+
splat->pstate(),
1205+
0,
1206+
ls ? ls->separator() : separator,
1207+
true);
1208+
1209+
if (ls && ls->is_arglist()) {
1210+
for (auto as : *ls) *arglist << as;
1211+
} else if (ms) {
1212+
*aa << SASS_MEMORY_NEW(ctx.mem, Argument, splat->pstate(), ms, "", false, true);
1213+
} else if (ls) {
1214+
for (auto as : *ls) *arglist << as;
1215+
} else {
1216+
*arglist << splat;
1217+
}
1218+
if (arglist->length()) {
1219+
*aa << SASS_MEMORY_NEW(ctx.mem, Argument, splat->pstate(), arglist, "", true);
1220+
}
1221+
}
1222+
1223+
if (a->has_keyword_argument()) {
1224+
Expression* kwarg = static_cast<Argument*>(
1225+
a->get_keyword_argument()->perform(this)
1226+
)->value()->perform(this);
1227+
1228+
*aa << SASS_MEMORY_NEW(ctx.mem, Argument, kwarg->pstate(), kwarg, "", false, true);
1229+
}
1230+
11901231
return aa;
11911232
}
11921233

0 commit comments

Comments
 (0)