Skip to content

Commit 2f6e81a

Browse files
committed
Merge pull request #2052 from mgreter/bugfix/extend-recursion
Fix issue with extend and recursions
2 parents 4d0bc51 + d83f879 commit 2f6e81a

File tree

3 files changed

+67
-16
lines changed

3 files changed

+67
-16
lines changed

src/ast.hpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,6 +1889,9 @@ namespace Sass {
18891889
virtual void set_media_block(Media_Block* mb) {
18901890
media_block(mb);
18911891
}
1892+
virtual bool has_wrapped_selector() {
1893+
return false;
1894+
}
18921895
};
18931896
inline Selector::~Selector() { }
18941897

@@ -2185,6 +2188,10 @@ namespace Sass {
21852188
}
21862189
return hash_;
21872190
}
2191+
virtual bool has_wrapped_selector()
2192+
{
2193+
return true;
2194+
}
21882195
virtual unsigned long specificity()
21892196
{
21902197
return selector_ ? selector_->specificity() : 0;
@@ -2270,6 +2277,15 @@ namespace Sass {
22702277
return sum;
22712278
}
22722279

2280+
virtual bool has_wrapped_selector()
2281+
{
2282+
if (length() == 0) return false;
2283+
if (Simple_Selector* ss = elements().front()) {
2284+
if (ss->has_wrapped_selector()) return true;
2285+
}
2286+
return false;
2287+
}
2288+
22732289
bool is_empty_reference()
22742290
{
22752291
return length() == 1 &&
@@ -2398,6 +2414,11 @@ namespace Sass {
23982414
if (tail_) tail_->set_media_block(mb);
23992415
if (head_) head_->set_media_block(mb);
24002416
}
2417+
virtual bool has_wrapped_selector() {
2418+
if (head_ && head_->has_wrapped_selector()) return true;
2419+
if (tail_ && tail_->has_wrapped_selector()) return true;
2420+
return false;
2421+
}
24012422
bool operator<(const Complex_Selector& rhs) const;
24022423
bool operator==(const Complex_Selector& rhs) const;
24032424
inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); }
@@ -2506,6 +2527,12 @@ namespace Sass {
25062527
cs->set_media_block(mb);
25072528
}
25082529
}
2530+
virtual bool has_wrapped_selector() {
2531+
for (Complex_Selector* cs : elements()) {
2532+
if (cs->has_wrapped_selector()) return true;
2533+
}
2534+
return false;
2535+
}
25092536
Selector_List* clone(Context&) const; // does not clone Compound_Selector*s
25102537
Selector_List* cloneFully(Context&) const; // clones Compound_Selector*s
25112538
virtual bool operator==(const Selector& rhs) const;

src/extend.cpp

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,7 +1711,8 @@ namespace Sass {
17111711
static bool complexSelectorHasExtension(
17121712
Complex_Selector* pComplexSelector,
17131713
Context& ctx,
1714-
ExtensionSubsetMap& subset_map) {
1714+
ExtensionSubsetMap& subset_map,
1715+
std::set<Compound_Selector>& seen) {
17151716

17161717
bool hasExtension = false;
17171718

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

17231724
if (pHead) {
1724-
for (Simple_Selector* pSimple : *pHead) {
1725-
if (Wrapped_Selector* ws = dynamic_cast<Wrapped_Selector*>(pSimple)) {
1726-
if (Selector_List* sl = dynamic_cast<Selector_List*>(ws->selector())) {
1727-
for (Complex_Selector* cs : sl->elements()) {
1728-
while (cs) {
1729-
if (complexSelectorHasExtension(cs, ctx, subset_map)) {
1730-
hasExtension = true;
1731-
break;
1725+
if (seen.find(*pHead) == seen.end()) {
1726+
for (Simple_Selector* pSimple : *pHead) {
1727+
if (Wrapped_Selector* ws = dynamic_cast<Wrapped_Selector*>(pSimple)) {
1728+
if (Selector_List* sl = dynamic_cast<Selector_List*>(ws->selector())) {
1729+
for (Complex_Selector* cs : sl->elements()) {
1730+
while (cs) {
1731+
if (complexSelectorHasExtension(cs, ctx, subset_map, seen)) {
1732+
hasExtension = true;
1733+
break;
1734+
}
1735+
cs = cs->tail();
17321736
}
1733-
cs = cs->tail();
17341737
}
17351738
}
17361739
}
@@ -1907,6 +1910,14 @@ namespace Sass {
19071910
This is the equivalent of ruby's CommaSequence.do_extend.
19081911
*/
19091912
Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething) {
1913+
std::set<Compound_Selector> seen;
1914+
return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething, seen);
1915+
}
1916+
1917+
/*
1918+
This is the equivalent of ruby's CommaSequence.do_extend.
1919+
*/
1920+
Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething, std::set<Compound_Selector>& seen) {
19101921

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

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

19281939
extendedSomething = true;
19291940

1930-
std::set<Compound_Selector> seen;
1931-
19321941
Node extendedSelectors = extendComplexSelector(pSelector, ctx, subset_map, seen, isReplace, true);
19331942
if (!pSelector->has_placeholder()) {
19341943
if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) {
19351944
*pNewSelectors << pSelector;
1945+
continue;
19361946
}
19371947
}
19381948

@@ -1955,7 +1965,9 @@ namespace Sass {
19551965
// process tails
19561966
while (cur) {
19571967
// process header
1958-
if (cur->head()) {
1968+
if (cur->head() && seen.find(*cur->head()) == seen.end()) {
1969+
std::set<Compound_Selector> recseen(seen);
1970+
recseen.insert(*cur->head());
19591971
// create a copy since we add multiple items if stuff get unwrapped
19601972
Compound_Selector* cpy_head = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, cur->pstate());
19611973
for (Simple_Selector* hs : *cur->head()) {
@@ -1969,14 +1981,19 @@ namespace Sass {
19691981
// has wrapped selectors
19701982
else {
19711983
// extend the inner list of wrapped selector
1972-
Selector_List* ext_sl = extendSelectorList(sl, ctx, subset_map);
1984+
Selector_List* ext_sl = extendSelectorList(sl, ctx, subset_map, recseen);
19731985
for (size_t i = 0; i < ext_sl->length(); i += 1) {
19741986
if (Complex_Selector* ext_cs = ext_sl->at(i)) {
19751987
// create clones for wrapped selector and the inner list
19761988
Wrapped_Selector* cpy_ws = SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, *ws);
19771989
Selector_List* cpy_ws_sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, sl->pstate());
19781990
// remove parent selectors from inner selector
1979-
if (ext_cs->first()) *cpy_ws_sl << ext_cs->first();
1991+
if (ext_cs->first()) {
1992+
if (ext_cs->first()->has_wrapped_selector()) {
1993+
continue; // ignore this case for now
1994+
}
1995+
*cpy_ws_sl << ext_cs->first();
1996+
}
19801997
// assign list to clone
19811998
cpy_ws->selector(cpy_ws_sl);
19821999
// append the clone

src/extend.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define SASS_EXTEND_H
33

44
#include <string>
5+
#include <set>
56

67
#include "ast.hpp"
78
#include "operation.hpp"
@@ -23,11 +24,17 @@ namespace Sass {
2324

2425
public:
2526
static Node subweave(Node& one, Node& two, Context& ctx);
27+
static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething, std::set<Compound_Selector>& seen);
2628
static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething);
2729
static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace = false) {
2830
bool extendedSomething = false;
2931
return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething);
3032
}
33+
static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, std::set<Compound_Selector>& seen) {
34+
bool isReplace = false;
35+
bool extendedSomething = false;
36+
return extendSelectorList(pSelectorList, ctx, subset_map, isReplace, extendedSomething, seen);
37+
}
3138
Extend(Context&, ExtensionSubsetMap&);
3239
~Extend() { }
3340

0 commit comments

Comments
 (0)