Skip to content

Commit

Permalink
Merge pull request sass#2586 from mgreter/feature/backtraces
Browse files Browse the repository at this point in the history
Implement more detailed backtraces
  • Loading branch information
mgreter authored Mar 15, 2018
2 parents be64aef + e2b6f9a commit 9cfe0df
Show file tree
Hide file tree
Showing 33 changed files with 946 additions and 860 deletions.
2 changes: 2 additions & 0 deletions Makefile.conf
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ SOURCES = \
sass_context.cpp \
sass_functions.cpp \
sass2scss.cpp \
backtrace.cpp \
operators.cpp \
to_c.cpp \
to_value.cpp \
source_map.cpp \
Expand Down
39 changes: 21 additions & 18 deletions src/ast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ namespace Sass {
return lhs_list->is_superselector_of(rhs_list);
}
}
error("is_superselector expected a Selector_List", sub->pstate());
coreError("is_superselector expected a Selector_List", sub->pstate());
return false;
}

Expand Down Expand Up @@ -1171,7 +1171,7 @@ namespace Sass {
// check if we need to append some headers
// then we need to check for the combinator
// only then we can safely set the new tail
void Complex_Selector::append(Complex_Selector_Obj ss)
void Complex_Selector::append(Complex_Selector_Obj ss, Backtraces& traces)
{

Complex_Selector_Obj t = ss->tail();
Expand All @@ -1185,7 +1185,8 @@ namespace Sass {
// append old headers
if (h && h->length()) {
if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {
error("Invalid parent selector", pstate_);
traces.push_back(Backtrace(pstate()));
throw Exception::InvalidParent(this, traces, ss);
} else if (last()->head_ && last()->head_->length()) {
Compound_Selector_Obj rh = last()->head();
size_t i;
Expand Down Expand Up @@ -1258,21 +1259,21 @@ namespace Sass {
return list;
}

Selector_List_Ptr Selector_List::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, bool implicit_parent)
Selector_List_Ptr Selector_List::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent)
{
if (!this->has_parent_ref()) return this;
Selector_List_Ptr ss = SASS_MEMORY_NEW(Selector_List, pstate());
Selector_List_Ptr ps = pstack.back();
for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) {
for (size_t si = 0, sL = this->length(); si < sL; ++si) {
Selector_List_Obj rv = at(si)->resolve_parent_refs(pstack, implicit_parent);
Selector_List_Obj rv = at(si)->resolve_parent_refs(pstack, traces, implicit_parent);
ss->concat(rv);
}
}
return ss;
}

Selector_List_Ptr Complex_Selector::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, bool implicit_parent)
Selector_List_Ptr Complex_Selector::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent)
{
Complex_Selector_Obj tail = this->tail();
Compound_Selector_Obj head = this->head();
Expand All @@ -1285,7 +1286,7 @@ namespace Sass {
}

// first resolve_parent_refs the tail (which may return an expanded list)
Selector_List_Obj tails = tail ? tail->resolve_parent_refs(pstack, implicit_parent) : 0;
Selector_List_Obj tails = tail ? tail->resolve_parent_refs(pstack, traces, implicit_parent) : 0;

if (head && head->length() > 0) {

Expand Down Expand Up @@ -1331,7 +1332,7 @@ namespace Sass {
// keep old parser state
s->pstate(pstate());
// append new tail
s->append(ss);
s->append(ss, traces);
retval->append(s);
}
}
Expand All @@ -1346,7 +1347,8 @@ namespace Sass {
// this is only if valid if the parent has no trailing op
// otherwise we cannot append more simple selectors to head
if (parent->last()->combinator() != ANCESTOR_OF) {
throw Exception::InvalidParent(parent, ss);
traces.push_back(Backtrace(pstate()));
throw Exception::InvalidParent(parent, traces, ss);
}
ss->tail(tail ? SASS_MEMORY_CLONE(tail) : NULL);
Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);
Expand All @@ -1369,7 +1371,7 @@ namespace Sass {
// keep old parser state
s->pstate(pstate());
// append new tail
s->append(ss);
s->append(ss, traces);
retval->append(s);
}
}
Expand Down Expand Up @@ -1406,7 +1408,7 @@ namespace Sass {
for (Simple_Selector_Obj ss : head->elements()) {
if (Wrapped_Selector_Ptr ws = Cast<Wrapped_Selector>(ss)) {
if (Selector_List_Ptr sl = Cast<Selector_List>(ws->selector())) {
if (parents) ws->selector(sl->resolve_parent_refs(pstack, implicit_parent));
if (parents) ws->selector(sl->resolve_parent_refs(pstack, traces, implicit_parent));
}
}
}
Expand Down Expand Up @@ -1690,7 +1692,7 @@ namespace Sass {
}

if (!pIter->head() || pIter->tail()) {
error("nested selectors may not be extended", c->pstate());
coreError("nested selectors may not be extended", c->pstate());
}

compound_sel->is_optional(extendee->is_optional());
Expand Down Expand Up @@ -1766,31 +1768,31 @@ namespace Sass {
{
if (!a->name().empty()) {
if (has_keyword_argument()) {
error("named arguments must precede variable-length argument", a->pstate());
coreError("named arguments must precede variable-length argument", a->pstate());
}
has_named_arguments(true);
}
else if (a->is_rest_argument()) {
if (has_rest_argument()) {
error("functions and mixins may only be called with one variable-length argument", a->pstate());
coreError("functions and mixins may only be called with one variable-length argument", a->pstate());
}
if (has_keyword_argument_) {
error("only keyword arguments may follow variable arguments", a->pstate());
coreError("only keyword arguments may follow variable arguments", a->pstate());
}
has_rest_argument(true);
}
else if (a->is_keyword_argument()) {
if (has_keyword_argument()) {
error("functions and mixins may only be called with one keyword argument", a->pstate());
coreError("functions and mixins may only be called with one keyword argument", a->pstate());
}
has_keyword_argument(true);
}
else {
if (has_rest_argument()) {
error("ordinal arguments must precede variable-length arguments", a->pstate());
coreError("ordinal arguments must precede variable-length arguments", a->pstate());
}
if (has_named_arguments()) {
error("ordinal arguments must precede named arguments", a->pstate());
coreError("ordinal arguments must precede named arguments", a->pstate());
}
}
}
Expand Down Expand Up @@ -1907,6 +1909,7 @@ namespace Sass {
l.normalize(); r.normalize();
Units &lhs_unit = l, &rhs_unit = r;
if (!(lhs_unit == rhs_unit)) {
/* ToDo: do we always get usefull backtraces? */
throw Exception::IncompatibleUnits(rhs, *this);
}
return lhs_unit < rhs_unit ||
Expand Down
85 changes: 37 additions & 48 deletions src/ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,13 +572,15 @@ namespace Sass {
// Trace.
/////////////////
class Trace : public Has_Block {
ADD_CONSTREF(char, type)
ADD_CONSTREF(std::string, name)
public:
Trace(ParserState pstate, std::string n, Block_Obj b = 0)
: Has_Block(pstate, b), name_(n)
Trace(ParserState pstate, std::string n, Block_Obj b = 0, char type = 'm')
: Has_Block(pstate, b), type_(type), name_(n)
{ }
Trace(const Trace* ptr)
: Has_Block(ptr),
type_(ptr->type_),
name_(ptr->name_)
{ }
ATTACH_AST_OPERATIONS(Trace)
Expand Down Expand Up @@ -943,7 +945,7 @@ namespace Sass {
/////////////////////////////////////////////////////////////////////////////
struct Backtrace;
typedef const char* Signature;
typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*, std::vector<Selector_List_Obj>);
typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtraces, std::vector<Selector_List_Obj>);
class Definition : public Has_Block {
public:
enum Type { MIXIN, FUNCTION };
Expand Down Expand Up @@ -1184,6 +1186,27 @@ namespace Sass {
}
}

inline static const std::string sass_op_separator(enum Sass_OP op) {
switch (op) {
case AND: return "&&";
case OR: return "||";
case EQ: return "==";
case NEQ: return "!=";
case GT: return ">";
case GTE: return ">=";
case LT: return "<";
case LTE: return "<=";
case ADD: return "+";
case SUB: return "-";
case MUL: return "*";
case DIV: return "/";
case MOD: return "%";
// this is only used internally!
case NUM_OPS: return "[OPS]";
default: return "invalid";
}
}

//////////////////////////////////////////////////////////////////////////
// Binary expressions. Represents logical, relational, and arithmetic
// operations. Templatized to avoid large switch statements and repetitive
Expand All @@ -1208,44 +1231,10 @@ namespace Sass {
hash_(ptr->hash_)
{ }
const std::string type_name() {
switch (optype()) {
case AND: return "and";
case OR: return "or";
case EQ: return "eq";
case NEQ: return "neq";
case GT: return "gt";
case GTE: return "gte";
case LT: return "lt";
case LTE: return "lte";
case ADD: return "add";
case SUB: return "sub";
case MUL: return "mul";
case DIV: return "div";
case MOD: return "mod";
// this is only used internally!
case NUM_OPS: return "[OPS]";
default: return "invalid";
}
return sass_op_to_name(optype());
}
const std::string separator() {
switch (optype()) {
case AND: return "&&";
case OR: return "||";
case EQ: return "==";
case NEQ: return "!=";
case GT: return ">";
case GTE: return ">=";
case LT: return "<";
case LTE: return "<=";
case ADD: return "+";
case SUB: return "-";
case MUL: return "*";
case DIV: return "/";
case MOD: return "%";
// this is only used internally!
case NUM_OPS: return "[OPS]";
default: return "invalid";
}
return sass_op_separator(optype());
}
bool is_left_interpolant(void) const;
bool is_right_interpolant(void) const;
Expand Down Expand Up @@ -1360,7 +1349,7 @@ namespace Sass {
: Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0)
{
if (!name_.empty() && is_rest_argument_) {
error("variable-length argument may not be passed by name", pstate_);
coreError("variable-length argument may not be passed by name", pstate_);
}
}
Argument(const Argument* ptr)
Expand All @@ -1372,7 +1361,7 @@ namespace Sass {
hash_(ptr->hash_)
{
if (!name_.empty() && is_rest_argument_) {
error("variable-length argument may not be passed by name", pstate_);
coreError("variable-length argument may not be passed by name", pstate_);
}
}

Expand Down Expand Up @@ -2218,22 +2207,22 @@ namespace Sass {
{
if (p->default_value()) {
if (has_rest_parameter()) {
error("optional parameters may not be combined with variable-length parameters", p->pstate());
coreError("optional parameters may not be combined with variable-length parameters", p->pstate());
}
has_optional_parameters(true);
}
else if (p->is_rest_parameter()) {
if (has_rest_parameter()) {
error("functions and mixins cannot have more than one variable-length parameter", p->pstate());
coreError("functions and mixins cannot have more than one variable-length parameter", p->pstate());
}
has_rest_parameter(true);
}
else {
if (has_rest_parameter()) {
error("required parameters must precede variable-length parameters", p->pstate());
coreError("required parameters must precede variable-length parameters", p->pstate());
}
if (has_optional_parameters()) {
error("required parameters must precede optional parameters", p->pstate());
coreError("required parameters must precede optional parameters", p->pstate());
}
}
}
Expand Down Expand Up @@ -2872,13 +2861,13 @@ namespace Sass {
Complex_Selector_Obj innermost() { return last(); };

size_t length() const;
Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, bool implicit_parent = true);
Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent = true);
virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = "");
virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = "");
virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");
Selector_List_Ptr unify_with(Complex_Selector_Ptr rhs);
Combinator clear_innermost();
void append(Complex_Selector_Obj);
void append(Complex_Selector_Obj, Backtraces& traces);
void set_innermost(Complex_Selector_Obj, Combinator);
virtual size_t hash()
{
Expand Down Expand Up @@ -2994,7 +2983,7 @@ namespace Sass {
virtual bool has_parent_ref() const;
virtual bool has_real_parent_ref() const;
void remove_parent_selectors();
Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, bool implicit_parent = true);
Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent = true);
virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = "");
virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = "");
virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");
Expand Down
2 changes: 1 addition & 1 deletion src/ast_def_macros.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class LocalOption {

#define NESTING_GUARD(name) \
LocalOption<size_t> cnt_##name(name, name + 1); \
if (name > MAX_NESTING) throw Exception::NestingLimitError(pstate); \
if (name > MAX_NESTING) throw Exception::NestingLimitError(pstate, traces); \

#define ATTACH_OPERATIONS()\
virtual void perform(Operation<void>* op) { (*op)(this); }\
Expand Down
3 changes: 3 additions & 0 deletions src/ast_fwd_decl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <unordered_map>
#include <unordered_set>
#include "memory/SharedPtr.hpp"
#include "sass/functions.h"

/////////////////////////////////////////////
// Forward declarations for the AST visitors.
Expand Down Expand Up @@ -418,6 +419,8 @@ namespace Sass {
typedef std::set<Compound_Selector_Obj, OrderNodes> CompoundSelectorSet;
typedef std::unordered_set<Simple_Selector_Obj, HashNodes, CompareNodes> SimpleSelectorDict;

typedef std::vector<Sass_Import_Entry>* ImporterStack;

// only to switch implementations for testing
#define environment_map std::map

Expand Down
Loading

0 comments on commit 9cfe0df

Please sign in to comment.