4 #include "check_nesting.hpp"
8 CheckNesting::CheckNesting()
9 : parents(std::vector<Statement_Ptr>()),
11 current_mixin_definition(0)
14 Statement_Ptr CheckNesting::visit_children(Statement_Ptr parent)
16 Statement_Ptr old_parent = this->parent;
18 if (At_Root_Block_Ptr root = Cast<At_Root_Block>(parent)) {
19 std::vector<Statement_Ptr> old_parents = this->parents;
20 std::vector<Statement_Ptr> new_parents;
22 for (size_t i = 0, L = this->parents.size(); i < L; i++) {
23 Statement_Ptr p = this->parents.at(i);
24 if (!root->exclude_node(p)) {
25 new_parents.push_back(p);
28 this->parents = new_parents;
30 for (size_t i = this->parents.size(); i > 0; i--) {
33 if (i > 0) p = this->parents.at(i - 1);
34 if (i > 1) gp = this->parents.at(i - 2);
36 if (!this->is_transparent_parent(p, gp)) {
42 At_Root_Block_Ptr ar = Cast<At_Root_Block>(parent);
43 Statement_Ptr ret = this->visit_children(ar->block());
45 this->parent = old_parent;
46 this->parents = old_parents;
51 if (!this->is_transparent_parent(parent, old_parent)) {
52 this->parent = parent;
55 this->parents.push_back(parent);
57 Block_Ptr b = Cast<Block>(parent);
60 if (Has_Block_Ptr bb = Cast<Has_Block>(parent)) {
66 for (auto n : b->elements()) {
70 this->parent = old_parent;
71 this->parents.pop_back();
77 Statement_Ptr CheckNesting::operator()(Block_Ptr b)
79 return this->visit_children(b);
82 Statement_Ptr CheckNesting::operator()(Definition_Ptr n)
84 if (!this->should_visit(n)) return NULL;
90 Definition_Ptr old_mixin_definition = this->current_mixin_definition;
91 this->current_mixin_definition = n;
95 this->current_mixin_definition = old_mixin_definition;
100 Statement_Ptr CheckNesting::fallback_impl(Statement_Ptr s)
102 Block_Ptr b1 = Cast<Block>(s);
103 Has_Block_Ptr b2 = Cast<Has_Block>(s);
104 return b1 || b2 ? visit_children(s) : s;
107 bool CheckNesting::should_visit(Statement_Ptr node)
109 if (!this->parent) return true;
111 if (Cast<Content>(node))
112 { this->invalid_content_parent(this->parent); }
114 if (is_charset(node))
115 { this->invalid_charset_parent(this->parent); }
117 if (Cast<Extension>(node))
118 { this->invalid_extend_parent(this->parent); }
120 // if (Cast<Import>(node))
121 // { this->invalid_import_parent(this->parent); }
123 if (this->is_mixin(node))
124 { this->invalid_mixin_definition_parent(this->parent); }
126 if (this->is_function(node))
127 { this->invalid_function_parent(this->parent); }
129 if (this->is_function(this->parent))
130 { this->invalid_function_child(node); }
132 if (Cast<Declaration>(node))
133 { this->invalid_prop_parent(this->parent); }
135 if (Cast<Declaration>(this->parent))
136 { this->invalid_prop_child(node); }
138 if (Cast<Return>(node))
139 { this->invalid_return_parent(this->parent); }
144 void CheckNesting::invalid_content_parent(Statement_Ptr parent)
146 if (!this->current_mixin_definition) {
147 throw Exception::InvalidSass(
149 "@content may only be used within a mixin."
154 void CheckNesting::invalid_charset_parent(Statement_Ptr parent)
159 throw Exception::InvalidSass(
161 "@charset may only be used at the root of a document."
166 void CheckNesting::invalid_extend_parent(Statement_Ptr parent)
169 Cast<Ruleset>(parent) ||
170 Cast<Mixin_Call>(parent) ||
173 throw Exception::InvalidSass(
175 "Extend directives may only be used within rules."
180 // void CheckNesting::invalid_import_parent(Statement_Ptr parent)
182 // for (auto pp : this->parents) {
187 // Cast<While>(pp) ||
188 // Cast<Trace>(pp) ||
189 // Cast<Mixin_Call>(pp) ||
192 // throw Exception::InvalidSass(
194 // "Import directives may not be defined within control directives or other mixins."
199 // if (this->is_root_node(parent)) {
203 // if (false/*n.css_import?*/) {
204 // throw Exception::InvalidSass(
206 // "CSS import directives may only be used at the root of a document."
211 void CheckNesting::invalid_mixin_definition_parent(Statement_Ptr parent)
213 for (Statement_Ptr pp : this->parents) {
220 Cast<Mixin_Call>(pp) ||
223 throw Exception::InvalidSass(
225 "Mixins may not be defined within control directives or other mixins."
231 void CheckNesting::invalid_function_parent(Statement_Ptr parent)
233 for (Statement_Ptr pp : this->parents) {
240 Cast<Mixin_Call>(pp) ||
243 throw Exception::InvalidSass(
245 "Functions may not be defined within control directives or other mixins."
251 void CheckNesting::invalid_function_child(Statement_Ptr child)
257 Cast<While>(child) ||
258 Cast<Trace>(child) ||
259 Cast<Comment>(child) ||
260 Cast<Debug>(child) ||
261 Cast<Return>(child) ||
262 Cast<Variable>(child) ||
263 // Ruby Sass doesn't distinguish variables and assignments
264 Cast<Assignment>(child) ||
265 Cast<Warning>(child) ||
268 throw Exception::InvalidSass(
270 "Functions can only contain variable declarations and control directives."
275 void CheckNesting::invalid_prop_child(Statement_Ptr child)
281 Cast<While>(child) ||
282 Cast<Trace>(child) ||
283 Cast<Comment>(child) ||
284 Cast<Declaration>(child) ||
285 Cast<Mixin_Call>(child)
287 throw Exception::InvalidSass(
289 "Illegal nesting: Only properties may be nested beneath properties."
294 void CheckNesting::invalid_prop_parent(Statement_Ptr parent)
298 is_directive_node(parent) ||
299 Cast<Ruleset>(parent) ||
300 Cast<Keyframe_Rule>(parent) ||
301 Cast<Declaration>(parent) ||
302 Cast<Mixin_Call>(parent)
304 throw Exception::InvalidSass(
306 "Properties are only allowed within rules, directives, mixin includes, or other properties."
311 void CheckNesting::invalid_return_parent(Statement_Ptr parent)
313 if (!this->is_function(parent)) {
314 throw Exception::InvalidSass(
316 "@return may only be used within a function."
321 bool CheckNesting::is_transparent_parent(Statement_Ptr parent, Statement_Ptr grandparent)
323 bool parent_bubbles = parent && parent->bubbles();
325 bool valid_bubble_node = parent_bubbles &&
326 !is_root_node(grandparent) &&
327 !is_at_root_node(grandparent);
329 return Cast<Import>(parent) ||
330 Cast<Each>(parent) ||
333 Cast<While>(parent) ||
334 Cast<Trace>(parent) ||
338 bool CheckNesting::is_charset(Statement_Ptr n)
340 Directive_Ptr d = Cast<Directive>(n);
341 return d && d->keyword() == "charset";
344 bool CheckNesting::is_mixin(Statement_Ptr n)
346 Definition_Ptr def = Cast<Definition>(n);
347 return def && def->type() == Definition::MIXIN;
350 bool CheckNesting::is_function(Statement_Ptr n)
352 Definition_Ptr def = Cast<Definition>(n);
353 return def && def->type() == Definition::FUNCTION;
356 bool CheckNesting::is_root_node(Statement_Ptr n)
358 if (Cast<Ruleset>(n)) return false;
360 Block_Ptr b = Cast<Block>(n);
361 return b && b->is_root();
364 bool CheckNesting::is_at_root_node(Statement_Ptr n)
366 return Cast<At_Root_Block>(n) != NULL;
369 bool CheckNesting::is_directive_node(Statement_Ptr n)
371 return Cast<Directive>(n) ||
373 Cast<Media_Block>(n) ||
374 Cast<Supports_Block>(n);