From 67bac0647c1330603833693fa07f8d92e94e24a8 Mon Sep 17 00:00:00 2001
From: Houtan Bastani <houtan@dynare.org>
Date: Fri, 23 Sep 2016 15:22:25 +0200
Subject: [PATCH] preprocessor: issue warnings for undeclared model variables
 when the end of the model block is encountered. #1286

---
 preprocessor/DynareBison.yy   |  4 +--
 preprocessor/ParsingDriver.cc | 60 +++++++++++++++++++++++++++++------
 preprocessor/ParsingDriver.hh | 18 ++++++++++-
 3 files changed, 69 insertions(+), 13 deletions(-)

diff --git a/preprocessor/DynareBison.yy b/preprocessor/DynareBison.yy
index 7f22d45d35..256cb228dd 100644
--- a/preprocessor/DynareBison.yy
+++ b/preprocessor/DynareBison.yy
@@ -696,9 +696,9 @@ model_options_list : model_options_list COMMA model_options
                    ;
 
 model : MODEL ';' { driver.begin_model(); }
-        equation_list END ';' { driver.reset_data_tree(); }
+        equation_list END ';' { driver.end_model(); }
       | MODEL '(' model_options_list ')' ';' { driver.begin_model(); }
-        equation_list END ';' { driver.reset_data_tree(); }
+        equation_list END ';' { driver.end_model(); }
       ;
 
 equation_list : equation_list equation
diff --git a/preprocessor/ParsingDriver.cc b/preprocessor/ParsingDriver.cc
index 42ac5cdf20..67e4bed07e 100644
--- a/preprocessor/ParsingDriver.cc
+++ b/preprocessor/ParsingDriver.cc
@@ -40,6 +40,13 @@ ParsingDriver::symbol_exists_and_is_not_modfile_local_or_external_function(const
   return (type != eModFileLocalVariable && type != eExternalFunction);
 }
 
+void
+ParsingDriver::check_symbol_existence_in_model_block(const string &name)
+{
+  if (!mod_file->symbol_table.exists(name))
+    model_error("Unknown symbol: " + name);
+}
+
 void
 ParsingDriver::check_symbol_existence(const string &name)
 {
@@ -106,23 +113,36 @@ ParsingDriver::parse(istream &in, bool debug)
 void
 ParsingDriver::error(const Dynare::parser::location_type &l, const string &m)
 {
-  cerr << "ERROR: " << *l.begin.filename << ": line " << l.begin.line;
+  create_error_string(l, m, cerr);
+  exit(EXIT_FAILURE);
+}
+
+void
+ParsingDriver::error(const string &m)
+{
+  error(location, m);
+}
+
+void
+ParsingDriver::create_error_string(const Dynare::parser::location_type &l, const string &m, ostream &stream)
+{
+  stream << "ERROR: " << *l.begin.filename << ": line " << l.begin.line;
   if (l.begin.line == l.end.line)
     if (l.begin.column == l.end.column - 1)
-      cerr << ", col " << l.begin.column;
+      stream << ", col " << l.begin.column;
     else
-      cerr << ", cols " << l.begin.column << "-" << l.end.column - 1;
+      stream << ", cols " << l.begin.column << "-" << l.end.column - 1;
   else
-    cerr << ", col " << l.begin.column << " -"
+    stream << ", col " << l.begin.column << " -"
          << " line " << l.end.line << ", col " << l.end.column - 1;
-  cerr << ": " << m << endl;
-  exit(EXIT_FAILURE);
+  stream << ": " << m << endl;
 }
 
 void
-ParsingDriver::error(const string &m)
+ParsingDriver::model_error(const string &m)
 {
-  error(location, m);
+  create_error_string(location, m, model_errors);
+  model_error_encountered = true;
 }
 
 void
@@ -334,8 +354,17 @@ ParsingDriver::add_inf_constant()
 expr_t
 ParsingDriver::add_model_variable(string *name)
 {
-  check_symbol_existence(*name);
-  int symb_id = mod_file->symbol_table.getID(*name);
+  check_symbol_existence_in_model_block(*name);
+  int symb_id;
+  try
+    {
+      symb_id = mod_file->symbol_table.getID(*name);
+    }
+  catch (SymbolTable::UnknownSymbolNameException &e)
+    {
+      declare_exogenous(new string (*name));
+      symb_id = mod_file->symbol_table.getID(*name);
+    }
   delete name;
   return add_model_variable(symb_id, 0);
 }
@@ -653,6 +682,17 @@ ParsingDriver::begin_model()
   set_current_data_tree(&mod_file->dynamic_model);
 }
 
+void
+ParsingDriver::end_model()
+{
+  if (model_error_encountered)
+    {
+      cerr << model_errors.str();
+      exit(EXIT_FAILURE);
+    }
+  reset_data_tree();
+}
+
 void
 ParsingDriver::end_shocks(bool overwrite)
 {
diff --git a/preprocessor/ParsingDriver.hh b/preprocessor/ParsingDriver.hh
index 34a312d44b..25e670c408 100644
--- a/preprocessor/ParsingDriver.hh
+++ b/preprocessor/ParsingDriver.hh
@@ -93,6 +93,10 @@ private:
   //! Checks that a given symbol exists and is a endogenous or exogenous, and stops with an error message if it isn't
   void check_symbol_is_endogenous_or_exogenous(string *name);
 
+  //! Checks for symbol existence in model block. If it doesn't exist, an error message is stored to be printed at
+  //! the end of the model block
+  void check_symbol_existence_in_model_block(const string &name);
+
   //! Helper to add a symbol declaration
   void declare_symbol(const string *name, SymbolType type, const string *tex_name, const vector<pair<string *, string *> *> *partition_value);
 
@@ -227,8 +231,12 @@ private:
 
   bool nostrict;
 
+  bool model_error_encountered;
+
+  ostringstream model_errors;
+
 public:
-  ParsingDriver(WarningConsolidation &warnings_arg, bool nostrict_arg) : warnings(warnings_arg), nostrict(nostrict_arg) { };
+  ParsingDriver(WarningConsolidation &warnings_arg, bool nostrict_arg) : warnings(warnings_arg), nostrict(nostrict_arg), model_error_encountered(false) { };
 
   //! Starts parsing, and constructs the MOD file representation
   /*! The returned pointer should be deleted after use */
@@ -256,6 +264,12 @@ public:
   //! Warning handler using saved location
   void warning(const string &m);
 
+  //! Error handler with explicit location (used in model block, accumulating error messages to be printed later)
+  void model_error(const string &m);
+
+  //! Code shared between model_error() and error()
+  void create_error_string(const Dynare::parser::location_type &l, const string &m, ostream &stream);
+
   //! Check if a given symbol exists in the parsing context, and is not a mod file local variable
   bool symbol_exists_and_is_not_modfile_local_or_external_function(const char *s);
   //! Sets mode of ModelTree class to use C output
@@ -340,6 +354,8 @@ public:
   void end_homotopy();
   //! Begin a model block
   void begin_model();
+  //! End a model block, printing errors that were encountered in parsing
+  void end_model();
   //! Writes a shocks statement
   void end_shocks(bool overwrite);
   //! Writes a mshocks statement
-- 
GitLab