Skip to content

Commit

Permalink
Merge pull request #2052 from mgreter/bugfix/extend-recursion
Browse files Browse the repository at this point in the history
Fix issue with extend and recursions
  • Loading branch information
mgreter committed Apr 29, 2016
2 parents 4d0bc51 + d83f879 commit 2f6e81a
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 16 deletions.
27 changes: 27 additions & 0 deletions src/ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1889,6 +1889,9 @@ namespace Sass {
virtual void set_media_block(Media_Block* mb) {
media_block(mb);
}
virtual bool has_wrapped_selector() {
return false;
}
};
inline Selector::~Selector() { }

Expand Down Expand Up @@ -2185,6 +2188,10 @@ namespace Sass {
}
return hash_;
}
virtual bool has_wrapped_selector()
{
return true;
}
virtual unsigned long specificity()
{
return selector_ ? selector_->specificity() : 0;
Expand Down Expand Up @@ -2270,6 +2277,15 @@ namespace Sass {
return sum;
}

virtual bool has_wrapped_selector()
{
if (length() == 0) return false;
if (Simple_Selector* ss = elements().front()) {
if (ss->has_wrapped_selector()) return true;
}
return false;
}

bool is_empty_reference()
{
return length() == 1 &&
Expand Down Expand Up @@ -2398,6 +2414,11 @@ namespace Sass {
if (tail_) tail_->set_media_block(mb);
if (head_) head_->set_media_block(mb);
}
virtual bool has_wrapped_selector() {
if (head_ && head_->has_wrapped_selector()) return true;
if (tail_ && tail_->has_wrapped_selector()) return true;
return false;
}
bool operator<(const Complex_Selector& rhs) const;
bool operator==(const Complex_Selector& rhs) const;
inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); }
Expand Down Expand Up @@ -2506,6 +2527,12 @@ namespace Sass {
cs->set_media_block(mb);
}
}
virtual bool has_wrapped_selector() {
for (Complex_Selector* cs : elements()) {
if (cs->has_wrapped_selector()) return true;
}
return false;
}
Selector_List* clone(Context&) const; // does not clone Compound_Selector*s
Selector_List* cloneFully(Context&) const; // clones Compound_Selector*s
virtual bool operator==(const Selector& rhs) const;
Expand Down
49 changes: 33 additions & 16 deletions src/extend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1711,7 +1711,8 @@ namespace Sass {
static bool complexSelectorHasExtension(
Complex_Selector* pComplexSelector,
Context& ctx,
ExtensionSubsetMap& subset_map) {
ExtensionSubsetMap& subset_map,
std::set<Compound_Selector>& seen) {

bool hasExtension = false;

Expand All @@ -1721,16 +1722,18 @@ namespace Sass {
Compound_Selector* pHead = pIter->head();

if (pHead) {
for (Simple_Selector* pSimple : *pHead) {
if (Wrapped_Selector* ws = dynamic_cast<Wrapped_Selector*>(pSimple)) {
if (Selector_List* sl = dynamic_cast<Selector_List*>(ws->selector())) {
for (Complex_Selector* cs : sl->elements()) {
while (cs) {
if (complexSelectorHasExtension(cs, ctx, subset_map)) {
hasExtension = true;
break;
if (seen.find(*pHead) == seen.end()) {
for (Simple_Selector* pSimple : *pHead) {
if (Wrapped_Selector* ws = dynamic_cast<Wrapped_Selector*>(pSimple)) {
if (Selector_List* sl = dynamic_cast<Selector_List*>(ws->selector())) {
for (Complex_Selector* cs : sl->elements()) {
while (cs) {
if (complexSelectorHasExtension(cs, ctx, subset_map, seen)) {
hasExtension = true;
break;
}
cs = cs->tail();
}
cs = cs->tail();
}
}
}
Expand Down Expand Up @@ -1907,6 +1910,14 @@ namespace Sass {
This is the equivalent of ruby's CommaSequence.do_extend.
*/
Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething) {
std::set<Compound_Selector> seen;
return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething, seen);
}

/*
This is the equivalent of ruby's CommaSequence.do_extend.
*/
Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething, std::set<Compound_Selector>& seen) {

Selector_List* pNewSelectors = SASS_MEMORY_NEW(ctx.mem, Selector_List, pSelectorList->pstate(), pSelectorList->length());

Expand All @@ -1920,19 +1931,18 @@ namespace Sass {
// run through the extend code (which does a data model transformation), check if there is anything to extend before doing
// the extend. We might be able to optimize extendComplexSelector, but this approach keeps us closer to ruby sass (which helps
// when debugging).
if (!complexSelectorHasExtension(pSelector, ctx, subset_map)) {
if (!complexSelectorHasExtension(pSelector, ctx, subset_map, seen)) {
*pNewSelectors << pSelector;
continue;
}

extendedSomething = true;

std::set<Compound_Selector> seen;

Node extendedSelectors = extendComplexSelector(pSelector, ctx, subset_map, seen, isReplace, true);
if (!pSelector->has_placeholder()) {
if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) {
*pNewSelectors << pSelector;
continue;
}
}

Expand All @@ -1955,7 +1965,9 @@ namespace Sass {
// process tails
while (cur) {
// process header
if (cur->head()) {
if (cur->head() && seen.find(*cur->head()) == seen.end()) {
std::set<Compound_Selector> recseen(seen);
recseen.insert(*cur->head());
// create a copy since we add multiple items if stuff get unwrapped
Compound_Selector* cpy_head = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, cur->pstate());
for (Simple_Selector* hs : *cur->head()) {
Expand All @@ -1969,14 +1981,19 @@ namespace Sass {
// has wrapped selectors
else {
// extend the inner list of wrapped selector
Selector_List* ext_sl = extendSelectorList(sl, ctx, subset_map);
Selector_List* ext_sl = extendSelectorList(sl, ctx, subset_map, recseen);
for (size_t i = 0; i < ext_sl->length(); i += 1) {
if (Complex_Selector* ext_cs = ext_sl->at(i)) {
// create clones for wrapped selector and the inner list
Wrapped_Selector* cpy_ws = SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, *ws);
Selector_List* cpy_ws_sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, sl->pstate());
// remove parent selectors from inner selector
if (ext_cs->first()) *cpy_ws_sl << ext_cs->first();
if (ext_cs->first()) {
if (ext_cs->first()->has_wrapped_selector()) {
continue; // ignore this case for now
}
*cpy_ws_sl << ext_cs->first();
}
// assign list to clone
cpy_ws->selector(cpy_ws_sl);
// append the clone
Expand Down
7 changes: 7 additions & 0 deletions src/extend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define SASS_EXTEND_H

#include <string>
#include <set>

#include "ast.hpp"
#include "operation.hpp"
Expand All @@ -23,11 +24,17 @@ namespace Sass {

public:
static Node subweave(Node& one, Node& two, Context& ctx);
static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething, std::set<Compound_Selector>& seen);
static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething);
static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace = false) {
bool extendedSomething = false;
return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething);
}
static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, std::set<Compound_Selector>& seen) {
bool isReplace = false;
bool extendedSomething = false;
return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething, seen);
}
Extend(Context&, ExtensionSubsetMap&);
~Extend() { }

Expand Down

0 comments on commit 2f6e81a

Please sign in to comment.