|
Packit Service |
7770af |
#include "sass.hpp"
|
|
Packit Service |
7770af |
#include <vector>
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
#include "check_nesting.hpp"
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
namespace Sass {
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
CheckNesting::CheckNesting()
|
|
Packit Service |
7770af |
: parents(std::vector<Statement_Ptr>()),
|
|
Packit Service |
7770af |
parent(0),
|
|
Packit Service |
7770af |
current_mixin_definition(0)
|
|
Packit Service |
7770af |
{ }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
Statement_Ptr CheckNesting::visit_children(Statement_Ptr parent)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
Statement_Ptr old_parent = this->parent;
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (At_Root_Block_Ptr root = Cast<At_Root_Block>(parent)) {
|
|
Packit Service |
7770af |
std::vector<Statement_Ptr> old_parents = this->parents;
|
|
Packit Service |
7770af |
std::vector<Statement_Ptr> new_parents;
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
for (size_t i = 0, L = this->parents.size(); i < L; i++) {
|
|
Packit Service |
7770af |
Statement_Ptr p = this->parents.at(i);
|
|
Packit Service |
7770af |
if (!root->exclude_node(p)) {
|
|
Packit Service |
7770af |
new_parents.push_back(p);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
this->parents = new_parents;
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
for (size_t i = this->parents.size(); i > 0; i--) {
|
|
Packit Service |
7770af |
Statement_Ptr p = 0;
|
|
Packit Service |
7770af |
Statement_Ptr gp = 0;
|
|
Packit Service |
7770af |
if (i > 0) p = this->parents.at(i - 1);
|
|
Packit Service |
7770af |
if (i > 1) gp = this->parents.at(i - 2);
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (!this->is_transparent_parent(p, gp)) {
|
|
Packit Service |
7770af |
this->parent = p;
|
|
Packit Service |
7770af |
break;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
At_Root_Block_Ptr ar = Cast<At_Root_Block>(parent);
|
|
Packit Service |
7770af |
Statement_Ptr ret = this->visit_children(ar->block());
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
this->parent = old_parent;
|
|
Packit Service |
7770af |
this->parents = old_parents;
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
return ret;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (!this->is_transparent_parent(parent, old_parent)) {
|
|
Packit Service |
7770af |
this->parent = parent;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
this->parents.push_back(parent);
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
Block_Ptr b = Cast<Block>(parent);
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (!b) {
|
|
Packit Service |
7770af |
if (Has_Block_Ptr bb = Cast<Has_Block>(parent)) {
|
|
Packit Service |
7770af |
b = bb->block();
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (b) {
|
|
Packit Service |
7770af |
for (auto n : b->elements()) {
|
|
Packit Service |
7770af |
n->perform(this);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
this->parent = old_parent;
|
|
Packit Service |
7770af |
this->parents.pop_back();
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
return b;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
Statement_Ptr CheckNesting::operator()(Block_Ptr b)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
return this->visit_children(b);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
Statement_Ptr CheckNesting::operator()(Definition_Ptr n)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
if (!this->should_visit(n)) return NULL;
|
|
Packit Service |
7770af |
if (!is_mixin(n)) {
|
|
Packit Service |
7770af |
visit_children(n);
|
|
Packit Service |
7770af |
return n;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
Definition_Ptr old_mixin_definition = this->current_mixin_definition;
|
|
Packit Service |
7770af |
this->current_mixin_definition = n;
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
visit_children(n);
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
this->current_mixin_definition = old_mixin_definition;
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
return n;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
Statement_Ptr CheckNesting::fallback_impl(Statement_Ptr s)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
Block_Ptr b1 = Cast<Block>(s);
|
|
Packit Service |
7770af |
Has_Block_Ptr b2 = Cast<Has_Block>(s);
|
|
Packit Service |
7770af |
return b1 || b2 ? visit_children(s) : s;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
bool CheckNesting::should_visit(Statement_Ptr node)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
if (!this->parent) return true;
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (Cast<Content>(node))
|
|
Packit Service |
7770af |
{ this->invalid_content_parent(this->parent); }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (is_charset(node))
|
|
Packit Service |
7770af |
{ this->invalid_charset_parent(this->parent); }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (Cast<Extension>(node))
|
|
Packit Service |
7770af |
{ this->invalid_extend_parent(this->parent); }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
// if (Cast<Import>(node))
|
|
Packit Service |
7770af |
// { this->invalid_import_parent(this->parent); }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (this->is_mixin(node))
|
|
Packit Service |
7770af |
{ this->invalid_mixin_definition_parent(this->parent); }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (this->is_function(node))
|
|
Packit Service |
7770af |
{ this->invalid_function_parent(this->parent); }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (this->is_function(this->parent))
|
|
Packit Service |
7770af |
{ this->invalid_function_child(node); }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (Cast<Declaration>(node))
|
|
Packit Service |
7770af |
{ this->invalid_prop_parent(this->parent); }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (Cast<Declaration>(this->parent))
|
|
Packit Service |
7770af |
{ this->invalid_prop_child(node); }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
if (Cast<Return>(node))
|
|
Packit Service |
7770af |
{ this->invalid_return_parent(this->parent); }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
return true;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
void CheckNesting::invalid_content_parent(Statement_Ptr parent)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
if (!this->current_mixin_definition) {
|
|
Packit Service |
7770af |
throw Exception::InvalidSass(
|
|
Packit Service |
7770af |
parent->pstate(),
|
|
Packit Service |
7770af |
"@content may only be used within a mixin."
|
|
Packit Service |
7770af |
);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
void CheckNesting::invalid_charset_parent(Statement_Ptr parent)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
if (!(
|
|
Packit Service |
7770af |
is_root_node(parent)
|
|
Packit Service |
7770af |
)) {
|
|
Packit Service |
7770af |
throw Exception::InvalidSass(
|
|
Packit Service |
7770af |
parent->pstate(),
|
|
Packit Service |
7770af |
"@charset may only be used at the root of a document."
|
|
Packit Service |
7770af |
);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
void CheckNesting::invalid_extend_parent(Statement_Ptr parent)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
if (!(
|
|
Packit Service |
7770af |
Cast<Ruleset>(parent) ||
|
|
Packit Service |
7770af |
Cast<Mixin_Call>(parent) ||
|
|
Packit Service |
7770af |
is_mixin(parent)
|
|
Packit Service |
7770af |
)) {
|
|
Packit Service |
7770af |
throw Exception::InvalidSass(
|
|
Packit Service |
7770af |
parent->pstate(),
|
|
Packit Service |
7770af |
"Extend directives may only be used within rules."
|
|
Packit Service |
7770af |
);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
// void CheckNesting::invalid_import_parent(Statement_Ptr parent)
|
|
Packit Service |
7770af |
// {
|
|
Packit Service |
7770af |
// for (auto pp : this->parents) {
|
|
Packit Service |
7770af |
// if (
|
|
Packit Service |
7770af |
// Cast<Each>(pp) ||
|
|
Packit Service |
7770af |
// Cast<For>(pp) ||
|
|
Packit Service |
7770af |
// Cast<If>(pp) ||
|
|
Packit Service |
7770af |
// Cast<While>(pp) ||
|
|
Packit Service |
7770af |
// Cast<Trace>(pp) ||
|
|
Packit Service |
7770af |
// Cast<Mixin_Call>(pp) ||
|
|
Packit Service |
7770af |
// is_mixin(pp)
|
|
Packit Service |
7770af |
// ) {
|
|
Packit Service |
7770af |
// throw Exception::InvalidSass(
|
|
Packit Service |
7770af |
// parent->pstate(),
|
|
Packit Service |
7770af |
// "Import directives may not be defined within control directives or other mixins."
|
|
Packit Service |
7770af |
// );
|
|
Packit Service |
7770af |
// }
|
|
Packit Service |
7770af |
// }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
// if (this->is_root_node(parent)) {
|
|
Packit Service |
7770af |
// return;
|
|
Packit Service |
7770af |
// }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
// if (false/*n.css_import?*/) {
|
|
Packit Service |
7770af |
// throw Exception::InvalidSass(
|
|
Packit Service |
7770af |
// parent->pstate(),
|
|
Packit Service |
7770af |
// "CSS import directives may only be used at the root of a document."
|
|
Packit Service |
7770af |
// );
|
|
Packit Service |
7770af |
// }
|
|
Packit Service |
7770af |
// }
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
void CheckNesting::invalid_mixin_definition_parent(Statement_Ptr parent)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
for (Statement_Ptr pp : this->parents) {
|
|
Packit Service |
7770af |
if (
|
|
Packit Service |
7770af |
Cast<Each>(pp) ||
|
|
Packit Service |
7770af |
Cast<For>(pp) ||
|
|
Packit Service |
7770af |
Cast<If>(pp) ||
|
|
Packit Service |
7770af |
Cast<While>(pp) ||
|
|
Packit Service |
7770af |
Cast<Trace>(pp) ||
|
|
Packit Service |
7770af |
Cast<Mixin_Call>(pp) ||
|
|
Packit Service |
7770af |
is_mixin(pp)
|
|
Packit Service |
7770af |
) {
|
|
Packit Service |
7770af |
throw Exception::InvalidSass(
|
|
Packit Service |
7770af |
parent->pstate(),
|
|
Packit Service |
7770af |
"Mixins may not be defined within control directives or other mixins."
|
|
Packit Service |
7770af |
);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
void CheckNesting::invalid_function_parent(Statement_Ptr parent)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
for (Statement_Ptr pp : this->parents) {
|
|
Packit Service |
7770af |
if (
|
|
Packit Service |
7770af |
Cast<Each>(pp) ||
|
|
Packit Service |
7770af |
Cast<For>(pp) ||
|
|
Packit Service |
7770af |
Cast<If>(pp) ||
|
|
Packit Service |
7770af |
Cast<While>(pp) ||
|
|
Packit Service |
7770af |
Cast<Trace>(pp) ||
|
|
Packit Service |
7770af |
Cast<Mixin_Call>(pp) ||
|
|
Packit Service |
7770af |
is_mixin(pp)
|
|
Packit Service |
7770af |
) {
|
|
Packit Service |
7770af |
throw Exception::InvalidSass(
|
|
Packit Service |
7770af |
parent->pstate(),
|
|
Packit Service |
7770af |
"Functions may not be defined within control directives or other mixins."
|
|
Packit Service |
7770af |
);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
void CheckNesting::invalid_function_child(Statement_Ptr child)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
if (!(
|
|
Packit Service |
7770af |
Cast<Each>(child) ||
|
|
Packit Service |
7770af |
Cast<For>(child) ||
|
|
Packit Service |
7770af |
Cast<If>(child) ||
|
|
Packit Service |
7770af |
Cast<While>(child) ||
|
|
Packit Service |
7770af |
Cast<Trace>(child) ||
|
|
Packit Service |
7770af |
Cast<Comment>(child) ||
|
|
Packit Service |
7770af |
Cast<Debug>(child) ||
|
|
Packit Service |
7770af |
Cast<Return>(child) ||
|
|
Packit Service |
7770af |
Cast<Variable>(child) ||
|
|
Packit Service |
7770af |
// Ruby Sass doesn't distinguish variables and assignments
|
|
Packit Service |
7770af |
Cast<Assignment>(child) ||
|
|
Packit Service |
7770af |
Cast<Warning>(child) ||
|
|
Packit Service |
7770af |
Cast<Error>(child)
|
|
Packit Service |
7770af |
)) {
|
|
Packit Service |
7770af |
throw Exception::InvalidSass(
|
|
Packit Service |
7770af |
child->pstate(),
|
|
Packit Service |
7770af |
"Functions can only contain variable declarations and control directives."
|
|
Packit Service |
7770af |
);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
void CheckNesting::invalid_prop_child(Statement_Ptr child)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
if (!(
|
|
Packit Service |
7770af |
Cast<Each>(child) ||
|
|
Packit Service |
7770af |
Cast<For>(child) ||
|
|
Packit Service |
7770af |
Cast<If>(child) ||
|
|
Packit Service |
7770af |
Cast<While>(child) ||
|
|
Packit Service |
7770af |
Cast<Trace>(child) ||
|
|
Packit Service |
7770af |
Cast<Comment>(child) ||
|
|
Packit Service |
7770af |
Cast<Declaration>(child) ||
|
|
Packit Service |
7770af |
Cast<Mixin_Call>(child)
|
|
Packit Service |
7770af |
)) {
|
|
Packit Service |
7770af |
throw Exception::InvalidSass(
|
|
Packit Service |
7770af |
child->pstate(),
|
|
Packit Service |
7770af |
"Illegal nesting: Only properties may be nested beneath properties."
|
|
Packit Service |
7770af |
);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
void CheckNesting::invalid_prop_parent(Statement_Ptr parent)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
if (!(
|
|
Packit Service |
7770af |
is_mixin(parent) ||
|
|
Packit Service |
7770af |
is_directive_node(parent) ||
|
|
Packit Service |
7770af |
Cast<Ruleset>(parent) ||
|
|
Packit Service |
7770af |
Cast<Keyframe_Rule>(parent) ||
|
|
Packit Service |
7770af |
Cast<Declaration>(parent) ||
|
|
Packit Service |
7770af |
Cast<Mixin_Call>(parent)
|
|
Packit Service |
7770af |
)) {
|
|
Packit Service |
7770af |
throw Exception::InvalidSass(
|
|
Packit Service |
7770af |
parent->pstate(),
|
|
Packit Service |
7770af |
"Properties are only allowed within rules, directives, mixin includes, or other properties."
|
|
Packit Service |
7770af |
);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
void CheckNesting::invalid_return_parent(Statement_Ptr parent)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
if (!this->is_function(parent)) {
|
|
Packit Service |
7770af |
throw Exception::InvalidSass(
|
|
Packit Service |
7770af |
parent->pstate(),
|
|
Packit Service |
7770af |
"@return may only be used within a function."
|
|
Packit Service |
7770af |
);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
bool CheckNesting::is_transparent_parent(Statement_Ptr parent, Statement_Ptr grandparent)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
bool parent_bubbles = parent && parent->bubbles();
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
bool valid_bubble_node = parent_bubbles &&
|
|
Packit Service |
7770af |
!is_root_node(grandparent) &&
|
|
Packit Service |
7770af |
!is_at_root_node(grandparent);
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
return Cast<Import>(parent) ||
|
|
Packit Service |
7770af |
Cast<Each>(parent) ||
|
|
Packit Service |
7770af |
Cast<For>(parent) ||
|
|
Packit Service |
7770af |
Cast<If>(parent) ||
|
|
Packit Service |
7770af |
Cast<While>(parent) ||
|
|
Packit Service |
7770af |
Cast<Trace>(parent) ||
|
|
Packit Service |
7770af |
valid_bubble_node;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
bool CheckNesting::is_charset(Statement_Ptr n)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
Directive_Ptr d = Cast<Directive>(n);
|
|
Packit Service |
7770af |
return d && d->keyword() == "charset";
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
bool CheckNesting::is_mixin(Statement_Ptr n)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
Definition_Ptr def = Cast<Definition>(n);
|
|
Packit Service |
7770af |
return def && def->type() == Definition::MIXIN;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
bool CheckNesting::is_function(Statement_Ptr n)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
Definition_Ptr def = Cast<Definition>(n);
|
|
Packit Service |
7770af |
return def && def->type() == Definition::FUNCTION;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
bool CheckNesting::is_root_node(Statement_Ptr n)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
if (Cast<Ruleset>(n)) return false;
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
Block_Ptr b = Cast<Block>(n);
|
|
Packit Service |
7770af |
return b && b->is_root();
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
bool CheckNesting::is_at_root_node(Statement_Ptr n)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
return Cast<At_Root_Block>(n) != NULL;
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
|
|
Packit Service |
7770af |
bool CheckNesting::is_directive_node(Statement_Ptr n)
|
|
Packit Service |
7770af |
{
|
|
Packit Service |
7770af |
return Cast<Directive>(n) ||
|
|
Packit Service |
7770af |
Cast<Import>(n) ||
|
|
Packit Service |
7770af |
Cast<Media_Block>(n) ||
|
|
Packit Service |
7770af |
Cast<Supports_Block>(n);
|
|
Packit Service |
7770af |
}
|
|
Packit Service |
7770af |
}
|