1// sass.hpp must go before all system headers to get the
2// __EXTENSIONS__ fix on Solaris.
3#include "sass.hpp"
4#include "ast.hpp"
5#include "check_nesting.hpp"
6
7namespace Sass {
8
9 CheckNesting::CheckNesting()
10 : parents(sass::vector<Statement*>()),
11 traces(sass::vector<Backtrace>()),
12 parent(0), current_mixin_definition(0)
13 { }
14
15 void error(AST_Node* node, Backtraces traces, sass::string msg) {
16 traces.push_back(x: Backtrace(node->pstate()));
17 throw Exception::InvalidSass(node->pstate(), traces, msg);
18 }
19
20 Statement* CheckNesting::visit_children(Statement* parent)
21 {
22 Statement* old_parent = this->parent;
23
24 if (AtRootRule* root = Cast<AtRootRule>(ptr: parent)) {
25 sass::vector<Statement*> old_parents = this->parents;
26 sass::vector<Statement*> new_parents;
27
28 for (size_t i = 0, L = this->parents.size(); i < L; i++) {
29 Statement* p = this->parents.at(n: i);
30 if (!root->exclude_node(s: p)) {
31 new_parents.push_back(x: p);
32 }
33 }
34 this->parents = new_parents;
35
36 for (size_t i = this->parents.size(); i > 0; i--) {
37 Statement* p = 0;
38 Statement* gp = 0;
39 if (i > 0) p = this->parents.at(n: i - 1);
40 if (i > 1) gp = this->parents.at(n: i - 2);
41
42 if (!this->is_transparent_parent(p, gp)) {
43 this->parent = p;
44 break;
45 }
46 }
47
48 AtRootRule* ar = Cast<AtRootRule>(ptr: parent);
49 Block* ret = ar->block();
50
51 if (ret != NULL) {
52 for (auto n : ret->elements()) {
53 n->perform(op: this);
54 }
55 }
56
57 this->parent = old_parent;
58 this->parents = old_parents;
59
60 return ret;
61 }
62
63 if (!this->is_transparent_parent(parent, old_parent)) {
64 this->parent = parent;
65 }
66
67 this->parents.push_back(x: parent);
68
69 Block* b = Cast<Block>(ptr: parent);
70
71 if (Trace* trace = Cast<Trace>(ptr: parent)) {
72 if (trace->type() == 'i') {
73 this->traces.push_back(x: Backtrace(trace->pstate()));
74 }
75 }
76
77 if (!b) {
78 if (ParentStatement* bb = Cast<ParentStatement>(ptr: parent)) {
79 b = bb->block();
80 }
81 }
82
83 if (b) {
84 for (auto n : b->elements()) {
85 n->perform(op: this);
86 }
87 }
88
89 this->parent = old_parent;
90 this->parents.pop_back();
91
92 if (Trace* trace = Cast<Trace>(ptr: parent)) {
93 if (trace->type() == 'i') {
94 this->traces.pop_back();
95 }
96 }
97
98 return b;
99 }
100
101
102 Statement* CheckNesting::operator()(Block* b)
103 {
104 return this->visit_children(parent: b);
105 }
106
107 Statement* CheckNesting::operator()(Definition* n)
108 {
109 if (!this->should_visit(n)) return NULL;
110 if (!is_mixin(n)) {
111 visit_children(parent: n);
112 return n;
113 }
114
115 Definition* old_mixin_definition = this->current_mixin_definition;
116 this->current_mixin_definition = n;
117
118 visit_children(parent: n);
119
120 this->current_mixin_definition = old_mixin_definition;
121
122 return n;
123 }
124
125 Statement* CheckNesting::operator()(If* i)
126 {
127 this->visit_children(parent: i);
128
129 if (Block* b = Cast<Block>(ptr: i->alternative())) {
130 for (auto n : b->elements()) n->perform(op: this);
131 }
132
133 return i;
134 }
135
136 bool CheckNesting::should_visit(Statement* node)
137 {
138 if (!this->parent) return true;
139
140 if (Cast<Content>(ptr: node))
141 { this->invalid_content_parent(this->parent, node); }
142
143 if (is_charset(node))
144 { this->invalid_charset_parent(this->parent, node); }
145
146 if (Cast<ExtendRule>(ptr: node))
147 { this->invalid_extend_parent(this->parent, node); }
148
149 // if (Cast<Import>(node))
150 // { this->invalid_import_parent(this->parent); }
151
152 if (this->is_mixin(node))
153 { this->invalid_mixin_definition_parent(this->parent, node); }
154
155 if (this->is_function(node))
156 { this->invalid_function_parent(this->parent, node); }
157
158 if (this->is_function(this->parent))
159 { this->invalid_function_child(node); }
160
161 if (Declaration* d = Cast<Declaration>(ptr: node))
162 {
163 this->invalid_prop_parent(this->parent, node);
164 this->invalid_value_child(d->value());
165 }
166
167 if (Cast<Declaration>(ptr: this->parent))
168 { this->invalid_prop_child(node); }
169
170 if (Cast<Return>(ptr: node))
171 { this->invalid_return_parent(this->parent, node); }
172
173 return true;
174 }
175
176 void CheckNesting::invalid_content_parent(Statement* parent, AST_Node* node)
177 {
178 if (!this->current_mixin_definition) {
179 error(node, traces, msg: "@content may only be used within a mixin.");
180 }
181 }
182
183 void CheckNesting::invalid_charset_parent(Statement* parent, AST_Node* node)
184 {
185 if (!(
186 is_root_node(parent)
187 )) {
188 error(node, traces, msg: "@charset may only be used at the root of a document.");
189 }
190 }
191
192 void CheckNesting::invalid_extend_parent(Statement* parent, AST_Node* node)
193 {
194 if (!(
195 Cast<StyleRule>(ptr: parent) ||
196 Cast<Mixin_Call>(ptr: parent) ||
197 is_mixin(parent)
198 )) {
199 error(node, traces, msg: "Extend directives may only be used within rules.");
200 }
201 }
202
203 // void CheckNesting::invalid_import_parent(Statement* parent, AST_Node* node)
204 // {
205 // for (auto pp : this->parents) {
206 // if (
207 // Cast<EachRule>(pp) ||
208 // Cast<ForRule>(pp) ||
209 // Cast<If>(pp) ||
210 // Cast<WhileRule>(pp) ||
211 // Cast<Trace>(pp) ||
212 // Cast<Mixin_Call>(pp) ||
213 // is_mixin(pp)
214 // ) {
215 // error(node, traces, "Import directives may not be defined within control directives or other mixins.");
216 // }
217 // }
218
219 // if (this->is_root_node(parent)) {
220 // return;
221 // }
222
223 // if (false/*n.css_import?*/) {
224 // error(node, traces, "CSS import directives may only be used at the root of a document.");
225 // }
226 // }
227
228 void CheckNesting::invalid_mixin_definition_parent(Statement* parent, AST_Node* node)
229 {
230 for (Statement* pp : this->parents) {
231 if (
232 Cast<EachRule>(ptr: pp) ||
233 Cast<ForRule>(ptr: pp) ||
234 Cast<If>(ptr: pp) ||
235 Cast<WhileRule>(ptr: pp) ||
236 Cast<Trace>(ptr: pp) ||
237 Cast<Mixin_Call>(ptr: pp) ||
238 is_mixin(pp)
239 ) {
240 error(node, traces, msg: "Mixins may not be defined within control directives or other mixins.");
241 }
242 }
243 }
244
245 void CheckNesting::invalid_function_parent(Statement* parent, AST_Node* node)
246 {
247 for (Statement* pp : this->parents) {
248 if (
249 Cast<EachRule>(ptr: pp) ||
250 Cast<ForRule>(ptr: pp) ||
251 Cast<If>(ptr: pp) ||
252 Cast<WhileRule>(ptr: pp) ||
253 Cast<Trace>(ptr: pp) ||
254 Cast<Mixin_Call>(ptr: pp) ||
255 is_mixin(pp)
256 ) {
257 error(node, traces, msg: "Functions may not be defined within control directives or other mixins.");
258 }
259 }
260 }
261
262 void CheckNesting::invalid_function_child(Statement* child)
263 {
264 if (!(
265 Cast<EachRule>(ptr: child) ||
266 Cast<ForRule>(ptr: child) ||
267 Cast<If>(ptr: child) ||
268 Cast<WhileRule>(ptr: child) ||
269 Cast<Trace>(ptr: child) ||
270 Cast<Comment>(ptr: child) ||
271 Cast<DebugRule>(ptr: child) ||
272 Cast<Return>(ptr: child) ||
273 Cast<Variable>(ptr: child) ||
274 // Ruby Sass doesn't distinguish variables and assignments
275 Cast<Assignment>(ptr: child) ||
276 Cast<WarningRule>(ptr: child) ||
277 Cast<ErrorRule>(ptr: child)
278 )) {
279 error(node: child, traces, msg: "Functions can only contain variable declarations and control directives.");
280 }
281 }
282
283 void CheckNesting::invalid_prop_child(Statement* child)
284 {
285 if (!(
286 Cast<EachRule>(ptr: child) ||
287 Cast<ForRule>(ptr: child) ||
288 Cast<If>(ptr: child) ||
289 Cast<WhileRule>(ptr: child) ||
290 Cast<Trace>(ptr: child) ||
291 Cast<Comment>(ptr: child) ||
292 Cast<Declaration>(ptr: child) ||
293 Cast<Mixin_Call>(ptr: child)
294 )) {
295 error(node: child, traces, msg: "Illegal nesting: Only properties may be nested beneath properties.");
296 }
297 }
298
299 void CheckNesting::invalid_prop_parent(Statement* parent, AST_Node* node)
300 {
301 if (!(
302 is_mixin(parent) ||
303 is_directive_node(parent) ||
304 Cast<StyleRule>(ptr: parent) ||
305 Cast<Keyframe_Rule>(ptr: parent) ||
306 Cast<Declaration>(ptr: parent) ||
307 Cast<Mixin_Call>(ptr: parent)
308 )) {
309 error(node, traces, msg: "Properties are only allowed within rules, directives, mixin includes, or other properties.");
310 }
311 }
312
313 void CheckNesting::invalid_value_child(AST_Node* d)
314 {
315 if (Map* m = Cast<Map>(ptr: d)) {
316 traces.push_back(x: Backtrace(m->pstate()));
317 throw Exception::InvalidValue(traces, *m);
318 }
319 if (Number* n = Cast<Number>(ptr: d)) {
320 if (!n->is_valid_css_unit()) {
321 traces.push_back(x: Backtrace(n->pstate()));
322 throw Exception::InvalidValue(traces, *n);
323 }
324 }
325
326 // error(dbg + " isn't a valid CSS value.", m->pstate(),);
327
328 }
329
330 void CheckNesting::invalid_return_parent(Statement* parent, AST_Node* node)
331 {
332 if (!this->is_function(parent)) {
333 error(node, traces, msg: "@return may only be used within a function.");
334 }
335 }
336
337 bool CheckNesting::is_transparent_parent(Statement* parent, Statement* grandparent)
338 {
339 bool parent_bubbles = parent && parent->bubbles();
340
341 bool valid_bubble_node = parent_bubbles &&
342 !is_root_node(grandparent) &&
343 !is_at_root_node(grandparent);
344
345 return Cast<Import>(ptr: parent) ||
346 Cast<EachRule>(ptr: parent) ||
347 Cast<ForRule>(ptr: parent) ||
348 Cast<If>(ptr: parent) ||
349 Cast<WhileRule>(ptr: parent) ||
350 Cast<Trace>(ptr: parent) ||
351 valid_bubble_node;
352 }
353
354 bool CheckNesting::is_charset(Statement* n)
355 {
356 AtRule* d = Cast<AtRule>(ptr: n);
357 return d && d->keyword() == "charset";
358 }
359
360 bool CheckNesting::is_mixin(Statement* n)
361 {
362 Definition* def = Cast<Definition>(ptr: n);
363 return def && def->type() == Definition::MIXIN;
364 }
365
366 bool CheckNesting::is_function(Statement* n)
367 {
368 Definition* def = Cast<Definition>(ptr: n);
369 return def && def->type() == Definition::FUNCTION;
370 }
371
372 bool CheckNesting::is_root_node(Statement* n)
373 {
374 if (Cast<StyleRule>(ptr: n)) return false;
375
376 Block* b = Cast<Block>(ptr: n);
377 return b && b->is_root();
378 }
379
380 bool CheckNesting::is_at_root_node(Statement* n)
381 {
382 return Cast<AtRootRule>(ptr: n) != NULL;
383 }
384
385 bool CheckNesting::is_directive_node(Statement* n)
386 {
387 return Cast<AtRule>(ptr: n) ||
388 Cast<Import>(ptr: n) ||
389 Cast<MediaRule>(ptr: n) ||
390 Cast<CssMediaRule>(ptr: n) ||
391 Cast<SupportsRule>(ptr: n);
392 }
393}
394

source code of gtk/subprojects/libsass/src/check_nesting.cpp