File cf36780b-Implement-import-recursion-check.patch of Package libsass.7438

From cf36780bce7f0258c39181b14e0e749dfd669ce3 Mon Sep 17 00:00:00 2001
From: Marcel Greter <marcel.greter@ocbnet.ch>
Date: Wed, 6 Jan 2016 20:41:36 +0100
Subject: [PATCH] Implement import recursion check

Also adds import stack reports on errors.
---
 src/context.cpp        | 28 ++++++++++++++++++++++++----
 src/context.hpp        |  2 +-
 src/error_handling.cpp |  9 +++++----
 src/error_handling.hpp |  5 +++--
 src/sass_context.cpp   | 17 ++++++++++++++---
 5 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/src/context.cpp b/src/context.cpp
index 7c74a6d9..eb5e0d63 100644
--- a/src/context.cpp
+++ b/src/context.cpp
@@ -28,6 +28,7 @@
 #include "extend.hpp"
 #include "remove_placeholders.hpp"
 #include "functions.hpp"
+#include "sass_functions.hpp"
 #include "backtrace.hpp"
 #include "sass2scss.h"
 #include "prelexer.hpp"
@@ -254,7 +255,7 @@ namespace Sass {
 
   // register include with resolved path and its content
   // memory of the resources will be freed by us on exit
-  void Context::register_resource(const Include& inc, const Resource& res)
+  void Context::register_resource(const Include& inc, const Resource& res, ParserState* prstate)
   {
 
     // do not parse same resource twice
@@ -297,6 +298,24 @@ namespace Sass {
     strings.push_back(sass_strdup(inc.abs_path.c_str()));
     // create the initial parser state from resource
     ParserState pstate(strings.back(), contents, idx);
+
+    // check existing import stack for possible recursion
+    for (size_t i = 0; i < import_stack.size() - 2; ++i) {
+      auto parent = import_stack[i];
+      if (std::strcmp(parent->abs_path, import->abs_path) == 0) {
+        std::string stack("An @import loop has been found:");
+        for (size_t n = 1; n < i + 2; ++n) {
+          stack += "\n    " + std::string(import_stack[n]->imp_path) +
+            " imports " + std::string(import_stack[n+1]->imp_path);
+        }
+        // implement error throw directly until we
+        // decided how to handle full stack traces
+        ParserState state = prstate ? *prstate : pstate;
+        throw Exception::InvalidSyntax(state, stack, &import_stack);
+        // error(stack, prstate ? *prstate : pstate, import_stack);
+      }
+    }
+
     // create a parser instance from the given c_str buffer
     Parser p(Parser::from_c_str(contents, *this, pstate));
     // do not yet dispose these buffers
@@ -344,7 +363,7 @@ namespace Sass {
       // the memory buffer returned must be freed by us!
       if (char* contents = read_file(resolved[0].abs_path)) {
         // register the newly resolved file resource
-        register_resource(resolved[0], { contents, 0 });
+        register_resource(resolved[0], { contents, 0 }, &pstate);
         // return resolved entry
         return resolved[0];
       }
@@ -433,7 +452,7 @@ namespace Sass {
           // handle error message passed back from custom importer
           // it may (or may not) override the line and column info
           if (const char* err_message = sass_import_get_error_message(include)) {
-            if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap });
+            if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, &pstate);
             if (line == std::string::npos && column == std::string::npos) error(err_message, pstate);
             else error(err_message, ParserState(ctx_path, source, Position(line, column)));
           }
@@ -447,7 +466,7 @@ namespace Sass {
             // attach information to AST node
             imp->incs().push_back(include);
             // register the resource buffers
-            register_resource(include, { source, srcmap });
+            register_resource(include, { source, srcmap }, &pstate);
           }
           // only a path was retuned
           // try to load it like normal
@@ -639,6 +658,7 @@ namespace Sass {
     Expand expand(*this, &global, &backtrace);
     Cssize cssize(*this, &backtrace);
     // expand and eval the tree
+    // debug_ast(root);
     root = root->perform(&expand)->block();
     // merge and bubble certain rules
     root = root->perform(&cssize)->block();
diff --git a/src/context.hpp b/src/context.hpp
index d81d5435..1628d5c6 100644
--- a/src/context.hpp
+++ b/src/context.hpp
@@ -92,7 +92,7 @@ namespace Sass {
     virtual char* render(Block* root);
     virtual char* render_srcmap();
 
-    void register_resource(const Include&, const Resource&);
+    void register_resource(const Include&, const Resource&, ParserState* = 0);
     std::vector<Include> find_includes(const Importer& import);
     Include load_import(const Importer&, ParserState pstate);
 
diff --git a/src/error_handling.cpp b/src/error_handling.cpp
index 6e49f4be..389939a8 100644
--- a/src/error_handling.cpp
+++ b/src/error_handling.cpp
@@ -10,9 +10,10 @@ namespace Sass {
 
   namespace Exception {
 
-    Base::Base(ParserState pstate, std::string msg)
+    Base::Base(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)
     : std::runtime_error(msg),
-      msg(msg), pstate(pstate)
+      msg(msg), pstate(pstate),
+      import_stack(import_stack)
     { }
 
     const char* Base::what() const throw()
@@ -44,8 +45,8 @@ namespace Sass {
       msg += " for `" + fn + "'";
     }
 
-    InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg)
-    : Base(pstate, msg)
+    InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)
+    : Base(pstate, msg, import_stack)
     { }
 
   }
diff --git a/src/error_handling.hpp b/src/error_handling.hpp
index aa08c7c3..0c2b1182 100644
--- a/src/error_handling.hpp
+++ b/src/error_handling.hpp
@@ -19,8 +19,9 @@ namespace Sass {
         std::string msg;
       public:
         ParserState pstate;
+        std::vector<Sass_Import_Entry>* import_stack;
       public:
-        Base(ParserState pstate, std::string msg = def_msg);
+        Base(ParserState pstate, std::string msg = def_msg, std::vector<Sass_Import_Entry>* import_stack = 0);
         virtual const char* what() const throw();
         virtual ~Base() throw() {};
     };
@@ -53,7 +54,7 @@ namespace Sass {
 
     class InvalidSyntax : public Base {
       public:
-        InvalidSyntax(ParserState pstate, std::string msg);
+        InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack = 0);
         virtual ~InvalidSyntax() throw() {};
     };
 
diff --git a/src/sass_context.cpp b/src/sass_context.cpp
index 32e6b83e..3c644409 100644
--- a/src/sass_context.cpp
+++ b/src/sass_context.cpp
@@ -10,6 +10,7 @@
 #include "util.hpp"
 #include "context.hpp"
 #include "sass_context.hpp"
+#include "sass_functions.hpp"
 #include "ast_fwd_decl.hpp"
 #include "error_handling.hpp"
 
@@ -41,7 +42,6 @@ extern "C" {
     catch (Exception::Base& e) {
       std::stringstream msg_stream;
       std::string cwd(Sass::File::get_cwd());
-      std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));
 
       std::string msg_prefix("Error: ");
       bool got_newline = false;
@@ -60,8 +60,19 @@ extern "C" {
         ++ msg;
       }
       if (!got_newline) msg_stream << "\n";
-      msg_stream << std::string(msg_prefix.size(), ' ');
-      msg_stream << " on line " << e.pstate.line+1 << " of " << rel_path << "\n";
+      if (e.import_stack) {
+        for (size_t i = 1; i < e.import_stack->size() - 1; ++i) {
+          std::string path((*e.import_stack)[i]->imp_path);
+          std::string rel_path(Sass::File::abs2rel(path, cwd, cwd));
+          msg_stream << std::string(msg_prefix.size(), ' ');
+          msg_stream << (i == 1 ? " on line " : " from line ");
+          msg_stream << e.pstate.line+1 << " of " << rel_path << "\n";
+        }
+      } else {
+        std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));
+        msg_stream << std::string(msg_prefix.size(), ' ');
+        msg_stream << " on line " << e.pstate.line+1 << " of " << rel_path << "\n";
+      }
 
       // now create the code trace (ToDo: maybe have util functions?)
       if (e.pstate.line != std::string::npos && e.pstate.column != std::string::npos) {
-- 
2.13.2

openSUSE Build Service is sponsored by