Skip to content

Commit

Permalink
Disallow parent selectors in selector-append (#2760)
Browse files Browse the repository at this point in the history
Now correctly errors on inputs such as this one instead of crashing (see #2663):

```scss
div {
  a: selector-append('.menu', 'li', '&');
}
```

```
Error: Parent selectors aren't allowed here.
        on line 3:37 of ../../../tmp/test.scss, in function `selector-append`
        from line 3:6 of ../../../tmp/test.scss
>>   e: selector-append('.menu', 'li', '&');

   ------------------------------------^
```
  • Loading branch information
glebm authored and xzyfer committed Nov 25, 2018
1 parent 5c2501e commit c93f058
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 18 deletions.
5 changes: 4 additions & 1 deletion src/fn_selectors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ namespace Sass {
str->quote_mark(0);
}
std::string exp_src = exp->to_string();
Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces);
Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces,
exp->pstate(), pstate.src,
/*allow_parent=*/false);

parsedSelectors.push_back(sel);
}

Expand Down
8 changes: 4 additions & 4 deletions src/fn_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,27 +121,27 @@ namespace Sass {
std::stringstream msg;
msg << argname << ": null is not a valid selector: it must be a string,\n";
msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'";
error(msg.str(), pstate, traces);
error(msg.str(), exp->pstate(), traces);
}
if (String_Constant_Ptr str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
std::string exp_src = exp->to_string(ctx.c_options);
return Parser::parse_selector(exp_src.c_str(), ctx, traces);
return Parser::parse_selector(exp_src.c_str(), ctx, traces, exp->pstate(), pstate.src);
}

Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) {
Expression_Obj exp = ARG(argname, Expression);
if (exp->concrete_type() == Expression::NULL_VAL) {
std::stringstream msg;
msg << argname << ": null is not a string for `" << function_name(sig) << "'";
error(msg.str(), pstate, traces);
error(msg.str(), exp->pstate(), traces);
}
if (String_Constant_Ptr str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
std::string exp_src = exp->to_string(ctx.c_options);
Selector_List_Obj sel_list = Parser::parse_selector(exp_src.c_str(), ctx, traces);
Selector_List_Obj sel_list = Parser::parse_selector(exp_src.c_str(), ctx, traces, exp->pstate(), pstate.src);
if (sel_list->length() == 0) return {};
Complex_Selector_Obj first = sel_list->first();
if (!first->tail()) return first->head();
Expand Down
14 changes: 7 additions & 7 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ namespace Sass {
using namespace Constants;
using namespace Prelexer;

Parser Parser::from_c_str(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source)
Parser Parser::from_c_str(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source, bool allow_parent)
{
pstate.offset.column = 0;
pstate.offset.line = 0;
Parser p(ctx, pstate, traces);
Parser p(ctx, pstate, traces, allow_parent);
p.source = source ? source : beg;
p.position = beg ? beg : p.source;
p.end = p.position + strlen(p.position);
Expand All @@ -44,11 +44,11 @@ namespace Sass {
return p;
}

Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, Backtraces traces, ParserState pstate, const char* source)
Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, Backtraces traces, ParserState pstate, const char* source, bool allow_parent)
{
pstate.offset.column = 0;
pstate.offset.line = 0;
Parser p(ctx, pstate, traces);
Parser p(ctx, pstate, traces, allow_parent);
p.source = source ? source : beg;
p.position = beg ? beg : p.source;
p.end = end ? end : p.position + strlen(p.position);
Expand All @@ -66,10 +66,9 @@ namespace Sass {
pstate.offset.line = 0;
}

Selector_List_Obj Parser::parse_selector(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source)
Selector_List_Obj Parser::parse_selector(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source, bool allow_parent)
{
Parser p = Parser::from_c_str(beg, ctx, traces, pstate, source);
// ToDo: ruby sass errors on parent references
Parser p = Parser::from_c_str(beg, ctx, traces, pstate, source, allow_parent);
// ToDo: remap the source-map entries somehow
return p.parse_selector_list(false);
}
Expand Down Expand Up @@ -818,6 +817,7 @@ namespace Sass {
// parse parent selector
else if (lex< exactly<'&'> >(false))
{
if (!allow_parent) error("Parent selectors aren't allowed here.");
// this produces a linefeed!?
seq->has_parent_reference(true);
seq->append(SASS_MEMORY_NEW(Parent_Selector, pstate));
Expand Down
13 changes: 7 additions & 6 deletions src/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,24 @@ namespace Sass {
Backtraces traces;
size_t indentation;
size_t nestings;
bool allow_parent;

Token lexed;

Parser(Context& ctx, const ParserState& pstate, Backtraces traces)
Parser(Context& ctx, const ParserState& pstate, Backtraces traces, bool allow_parent = true)
: ParserState(pstate), ctx(ctx), block_stack(), stack(0), last_media_block(),
source(0), position(0), end(0), before_token(pstate), after_token(pstate),
pstate(pstate), traces(traces), indentation(0), nestings(0)
pstate(pstate), traces(traces), indentation(0), nestings(0), allow_parent(allow_parent)
{
stack.push_back(Scope::Root);
}

// static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]"));
static Parser from_c_str(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);
static Parser from_c_str(const char* beg, const char* end, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);
static Parser from_token(Token t, Context& ctx, Backtraces, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0);
static Parser from_c_str(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = nullptr, bool allow_parent = true);
static Parser from_c_str(const char* beg, const char* end, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = nullptr, bool allow_parent = true);
static Parser from_token(Token t, Context& ctx, Backtraces, ParserState pstate = ParserState("[TOKEN]"), const char* source = nullptr);
// special static parsers to convert strings into certain selectors
static Selector_List_Obj parse_selector(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0);
static Selector_List_Obj parse_selector(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[SELECTOR]"), const char* source = nullptr, bool allow_parent = true);

#ifdef __clang__

Expand Down

0 comments on commit c93f058

Please sign in to comment.