Coverage Report

Created: 2025-08-29 06:11

/src/libsass/src/ast_sel_unify.cpp
Line
Count
Source (jump to first uncovered line)
1
// sass.hpp must go before all system headers to get the
2
// __EXTENSIONS__ fix on Solaris.
3
#include "sass.hpp"
4
5
#include "ast.hpp"
6
7
namespace Sass {
8
9
  // ##########################################################################
10
  // Returns the contents of a [SelectorList] that matches only 
11
  // elements that are matched by both [complex1] and [complex2].
12
  // If no such list can be produced, returns `null`.
13
  // ##########################################################################
14
  // ToDo: fine-tune API to avoid unnecessary wrapper allocations
15
  // ##########################################################################
16
  sass::vector<sass::vector<SelectorComponentObj>> unifyComplex(
17
    const sass::vector<sass::vector<SelectorComponentObj>>& complexes)
18
0
  {
19
20
0
    SASS_ASSERT(!complexes.empty(), "Can't unify empty list");
21
0
    if (complexes.size() == 1) return complexes;
22
23
0
    CompoundSelectorObj unifiedBase = SASS_MEMORY_NEW(CompoundSelector, SourceSpan("[unify]"));
24
0
    for (auto complex : complexes) {
25
0
      SelectorComponentObj base = complex.back();
26
0
      if (CompoundSelector * comp = base->getCompound()) {
27
0
        if (unifiedBase->empty()) {
28
0
          unifiedBase->concat(comp);
29
0
        }
30
0
        else {
31
0
          for (SimpleSelectorObj simple : comp->elements()) {
32
0
            unifiedBase = simple->unifyWith(unifiedBase);
33
0
            if (unifiedBase.isNull()) return {};
34
0
          }
35
0
        }
36
0
      }
37
0
      else {
38
0
        return {};
39
0
      }
40
0
    }
41
42
0
    sass::vector<sass::vector<SelectorComponentObj>> complexesWithoutBases;
43
0
    for (size_t i = 0; i < complexes.size(); i += 1) {
44
0
      sass::vector<SelectorComponentObj> sel = complexes[i];
45
0
      sel.pop_back(); // remove last item (base) from the list
46
0
      complexesWithoutBases.push_back(std::move(sel));
47
0
    }
48
49
0
    complexesWithoutBases.back().push_back(unifiedBase);
50
51
0
    return weave(complexesWithoutBases);
52
53
0
  }
54
  // EO unifyComplex
55
56
  // ##########################################################################
57
  // Returns a [CompoundSelector] that matches only elements
58
  // that are matched by both [compound1] and [compound2].
59
  // If no such selector can be produced, returns `null`.
60
  // ##########################################################################
61
  CompoundSelector* CompoundSelector::unifyWith(CompoundSelector* rhs)
62
0
  {
63
0
    if (empty()) return rhs;
64
0
    CompoundSelectorObj unified = SASS_MEMORY_COPY(rhs);
65
0
    for (const SimpleSelectorObj& sel : elements()) {
66
0
      unified = sel->unifyWith(unified);
67
0
      if (unified.isNull()) break;
68
0
    }
69
0
    return unified.detach();
70
0
  }
71
  // EO CompoundSelector::unifyWith(CompoundSelector*)
72
73
  // ##########################################################################
74
  // Returns the compoments of a [CompoundSelector] that matches only elements
75
  // matched by both this and [compound]. By default, this just returns a copy
76
  // of [compound] with this selector added to the end, or returns the original
77
  // array if this selector already exists in it. Returns `null` if unification
78
  // is impossible—for example, if there are multiple ID selectors.
79
  // ##########################################################################
80
  // This is implemented in `selector/simple.dart` as `SimpleSelector::unify`
81
  // ##########################################################################
82
  CompoundSelector* SimpleSelector::unifyWith(CompoundSelector* rhs)
83
0
  {
84
85
0
    if (rhs->length() == 1) {
86
0
      if (rhs->get(0)->is_universal()) {
87
0
        CompoundSelector* this_compound = SASS_MEMORY_NEW(CompoundSelector, pstate());
88
0
        this_compound->append(SASS_MEMORY_COPY(this));
89
0
        CompoundSelector* unified = rhs->get(0)->unifyWith(this_compound);
90
0
        if (unified == nullptr || unified != this_compound) delete this_compound;
91
0
        return unified;
92
0
      }
93
0
    }
94
0
    for (const SimpleSelectorObj& sel : rhs->elements()) {
95
0
      if (*this == *sel) {
96
0
        return rhs;
97
0
      }
98
0
    }
99
100
0
    CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, rhs->pstate());
101
102
0
    bool addedThis = false;
103
0
    for (auto simple : rhs->elements()) {
104
      // Make sure pseudo selectors always come last.
105
0
      if (!addedThis && simple->getPseudoSelector()) {
106
0
        result->append(this);
107
0
        addedThis = true;
108
0
      }
109
0
      result->append(simple);
110
0
    }
111
112
0
    if (!addedThis) {
113
0
      result->append(this);
114
0
    }
115
0
    return result.detach();
116
117
0
  }
118
  // EO SimpleSelector::unifyWith(CompoundSelector*)
119
120
  // ##########################################################################
121
  // This is implemented in `selector/type.dart` as `PseudoSelector::unify`
122
  // ##########################################################################
123
  CompoundSelector* TypeSelector::unifyWith(CompoundSelector* rhs)
124
0
  {
125
0
    if (rhs->empty()) {
126
0
      rhs->append(this);
127
0
      return rhs;
128
0
    }
129
0
    TypeSelector* type = Cast<TypeSelector>(rhs->at(0));
130
0
    if (type != nullptr) {
131
0
      SimpleSelector* unified = unifyWith(type);
132
0
      if (unified == nullptr) {
133
0
        return nullptr;
134
0
      }
135
0
      rhs->elements()[0] = unified;
136
0
    }
137
0
    else if (!is_universal() || (has_ns_ && ns_ != "*")) {
138
0
      rhs->insert(rhs->begin(), this);
139
0
    }
140
0
    return rhs;
141
0
  }
142
143
  // ##########################################################################
144
  // This is implemented in `selector/id.dart` as `PseudoSelector::unify`
145
  // ##########################################################################
146
  CompoundSelector* IDSelector::unifyWith(CompoundSelector* rhs)
147
0
  {
148
0
    for (const SimpleSelector* sel : rhs->elements()) {
149
0
      if (const IDSelector* id_sel = Cast<IDSelector>(sel)) {
150
0
        if (id_sel->name() != name()) return nullptr;
151
0
      }
152
0
    }
153
0
    return SimpleSelector::unifyWith(rhs);
154
0
  }
155
156
  // ##########################################################################
157
  // This is implemented in `selector/pseudo.dart` as `PseudoSelector::unify`
158
  // ##########################################################################
159
  CompoundSelector* PseudoSelector::unifyWith(CompoundSelector* compound)
160
0
  {
161
162
0
    if (compound->length() == 1 && compound->first()->is_universal()) {
163
      // std::cerr << "implement universal pseudo\n";
164
0
    }
165
166
0
    for (const SimpleSelectorObj& sel : compound->elements()) {
167
0
      if (*this == *sel) {
168
0
        return compound;
169
0
      }
170
0
    }
171
172
0
    CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, compound->pstate());
173
174
0
    bool addedThis = false;
175
0
    for (auto simple : compound->elements()) {
176
      // Make sure pseudo selectors always come last.
177
0
      if (PseudoSelectorObj pseudo = simple->getPseudoSelector()) {
178
0
        if (pseudo->isElement()) {
179
          // A given compound selector may only contain one pseudo element. If
180
          // [compound] has a different one than [this], unification fails.
181
0
          if (isElement()) {
182
0
            return {};
183
0
          }
184
          // Otherwise, this is a pseudo selector and
185
          // should come before pseduo elements.
186
0
          result->append(this);
187
0
          addedThis = true;
188
0
        }
189
0
      }
190
0
      result->append(simple);
191
0
    }
192
193
0
    if (!addedThis) {
194
0
      result->append(this);
195
0
    }
196
197
0
    return result.detach();
198
199
0
  }
200
  // EO PseudoSelector::unifyWith(CompoundSelector*
201
202
  // ##########################################################################
203
  // This is implemented in `extend/functions.dart` as `unifyUniversalAndElement`
204
  // Returns a [SimpleSelector] that matches only elements that are matched by
205
  // both [selector1] and [selector2], which must both be either [UniversalSelector]s
206
  // or [TypeSelector]s. If no such selector can be produced, returns `null`.
207
  // Note: libsass handles universal selector directly within the type selector
208
  // ##########################################################################
209
  SimpleSelector* TypeSelector::unifyWith(const SimpleSelector* rhs)
210
0
  {
211
0
    bool rhs_ns = false;
212
0
    if (!(is_ns_eq(*rhs) || rhs->is_universal_ns())) {
213
0
      if (!is_universal_ns()) {
214
0
        return nullptr;
215
0
      }
216
0
      rhs_ns = true;
217
0
    }
218
0
    bool rhs_name = false;
219
0
    if (!(name_ == rhs->name() || rhs->is_universal())) {
220
0
      if (!(is_universal())) {
221
0
        return nullptr;
222
0
      }
223
0
      rhs_name = true;
224
0
    }
225
0
    if (rhs_ns) {
226
0
      ns(rhs->ns());
227
0
      has_ns(rhs->has_ns());
228
0
    }
229
0
    if (rhs_name) name(rhs->name());
230
0
    return this;
231
0
  }
232
  // EO TypeSelector::unifyWith(const SimpleSelector*)
233
234
  // ##########################################################################
235
  // Unify two complex selectors. Internally calls `unifyComplex`
236
  // and then wraps the result in newly create ComplexSelectors.
237
  // ##########################################################################
238
  SelectorList* ComplexSelector::unifyWith(ComplexSelector* rhs)
239
0
  {
240
0
    SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate());
241
0
    sass::vector<sass::vector<SelectorComponentObj>> rv =
242
0
       unifyComplex({ elements(), rhs->elements() });
243
0
    for (sass::vector<SelectorComponentObj> items : rv) {
244
0
      ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate());
245
0
      sel->elements() = std::move(items);
246
0
      list->append(sel);
247
0
    }
248
0
    return list.detach();
249
0
  }
250
  // EO ComplexSelector::unifyWith(ComplexSelector*)
251
252
  // ##########################################################################
253
  // only called from the sass function `selector-unify`
254
  // ##########################################################################
255
  SelectorList* SelectorList::unifyWith(SelectorList* rhs)
256
0
  {
257
0
    SelectorList* slist = SASS_MEMORY_NEW(SelectorList, pstate());
258
    // Unify all of children with RHS's children,
259
    // storing the results in `unified_complex_selectors`
260
0
    for (ComplexSelectorObj& seq1 : elements()) {
261
0
      for (ComplexSelectorObj& seq2 : rhs->elements()) {
262
0
        if (SelectorListObj unified = seq1->unifyWith(seq2)) {
263
0
          std::move(unified->begin(), unified->end(),
264
0
            std::inserter(slist->elements(), slist->end()));
265
0
        }
266
0
      }
267
0
    }
268
0
    return slist;
269
0
  }
270
  // EO SelectorList::unifyWith(SelectorList*)
271
272
  // ##########################################################################
273
  // ##########################################################################
274
275
}