diff --git a/src/ModFile.cc b/src/ModFile.cc
index 6a378ecd62186acc169140f5555f15c33c458c21..5a0f38c064303bd443954d5588db13bb595eb68a 100644
--- a/src/ModFile.cc
+++ b/src/ModFile.cc
@@ -1311,8 +1311,8 @@ ModFile::writeMOutput(const string& basename, bool clear_all, bool clear_global,
 
       if (!no_warn)
         {
-          if (warnings.countWarnings() > 0)
-            mOutputFile << "disp('Note: " << warnings.countWarnings()
+          if (int num_warnings {warnings.numWarnings()}; num_warnings > 0)
+            mOutputFile << "disp('Note: " << num_warnings
                         << " warning(s) encountered in the preprocessor')" << endl;
 
           mOutputFile << "if ~isempty(lastwarn)" << endl
diff --git a/src/ParsingDriver.cc b/src/ParsingDriver.cc
index 7933280afbedab14e0847c67e69afbb2bf368e4a..d8191d747312b767824290128c10a840420b1856 100644
--- a/src/ParsingDriver.cc
+++ b/src/ParsingDriver.cc
@@ -28,6 +28,8 @@
 #include "ExprNode.hh"
 #include "ParsingDriver.hh"
 #include "Statement.hh"
+/* NB: the following also imports our specialization of operator<< for location class,
+   used below in error() and undeclared_model_variable_error() */
 #include "WarningConsolidation.hh"
 
 bool
@@ -113,7 +115,7 @@ ParsingDriver::parse(istream& in, bool debug)
 void
 ParsingDriver::error(const Dynare::parser::location_type& l, const string& m)
 {
-  create_error_string(l, m, cerr);
+  cerr << "ERROR: " << l << ": " << m << endl;
   exit(EXIT_FAILURE);
 }
 
@@ -123,54 +125,12 @@ 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)
-      stream << ", col " << l.begin.column;
-    else
-      stream << ", cols " << l.begin.column << "-" << l.end.column - 1;
-  else
-    stream << ", col " << l.begin.column << " -"
-           << " line " << l.end.line << ", col " << l.end.column - 1;
-  stream << ": " << m << endl;
-}
-
-void
-ParsingDriver::create_error_string(const Dynare::parser::location_type& l, const string& m,
-                                   const string& var)
-{
-  ostringstream stream;
-  create_error_string(l, m, stream);
-  model_errors.emplace_back(var, stream.str());
-}
-
-void
-ParsingDriver::model_error(const string& m, const string& var)
-{
-  create_error_string(location, m, var);
-}
-
 void
 ParsingDriver::undeclared_model_variable_error(const string& m, const string& var)
 {
   ostringstream stream;
   if (!nostrict)
-    {
-      stream << "ERROR: " << *location.begin.filename << ": line " << location.begin.line;
-      if (location.begin.line == location.end.line)
-        if (location.begin.column == location.end.column - 1)
-          stream << ", col " << location.begin.column;
-        else
-          stream << ", cols " << location.begin.column << "-" << location.end.column - 1;
-      else
-        stream << ", col " << location.begin.column << " -"
-               << " line " << location.end.line << ", col " << location.end.column - 1;
-      stream << ": ";
-    }
+    stream << "ERROR: " << location << ": ";
   stream << m;
   if (nostrict)
     stream << " automatically declared exogenous.";
@@ -913,13 +873,6 @@ void
 ParsingDriver::end_model()
 {
   bool exit_after_write = false;
-  if (model_errors.size() > 0)
-    for (auto& it : model_errors)
-      {
-        if (it.first.empty())
-          exit_after_write = true;
-        cerr << it.second;
-      }
 
   if (undeclared_model_variable_errors.size() > 0)
     for (auto& it : undeclared_model_variable_errors)
diff --git a/src/ParsingDriver.hh b/src/ParsingDriver.hh
index a33642677f693b07c8945edb9caba3866f480c19..d1b7a1b191c1fb588670d77d299b0c5a944aba6e 100644
--- a/src/ParsingDriver.hh
+++ b/src/ParsingDriver.hh
@@ -272,7 +272,6 @@ private:
 
   bool nostrict;
 
-  vector<pair<string, string>> model_errors;
   vector<pair<string, string>> undeclared_model_variable_errors;
 
   //! True when parsing the epilogue block
@@ -327,15 +326,8 @@ public:
 
   //! Error handler with explicit location (used in model block, accumulating error messages to be
   //! printed later)
-  void model_error(const string& m, const string& var);
   void undeclared_model_variable_error(const string& m, const string& var);
 
-  //! Code shared between model_error() and error()
-  void create_error_string(const Dynare::parser::location_type& l, const string& m,
-                           const string& var);
-  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 string& s);
   //! Sets mode of ModelTree class to use C output
diff --git a/src/WarningConsolidation.cc b/src/WarningConsolidation.cc
index 4f584df700dd24e239f4599975e0603ed46e132c..0f51ee5cac7f7a8c220dc8fff1bfbae1617a6d7c 100644
--- a/src/WarningConsolidation.cc
+++ b/src/WarningConsolidation.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2012-2022 Dynare Team
+ * Copyright © 2012-2024 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -18,39 +18,33 @@
  */
 
 #include "WarningConsolidation.hh"
-#include <ostream>
 
-WarningConsolidation&
-operator<<(WarningConsolidation& wcc, const string& warning)
+ostream&
+operator<<(ostream& stream, const Dynare::location& l)
 {
-  if (wcc.no_warn)
-    return wcc;
-
-  cerr << warning;
-  wcc.addWarning(warning);
-  return wcc;
-};
+  stream << *l.begin.filename << ": line " << l.begin.line;
+  if (l.begin.line == l.end.line)
+    if (l.begin.column == l.end.column - 1)
+      stream << ", col " << l.begin.column;
+    else
+      stream << ", cols " << l.begin.column << "-" << l.end.column - 1;
+  else
+    stream << ", col " << l.begin.column << " -"
+           << " line " << l.end.line << ", col " << l.end.column - 1;
+
+  return stream;
+}
 
-WarningConsolidation&
-operator<<(WarningConsolidation& wcc, const Dynare::location& loc)
+void
+WarningConsolidation::incrementWarnings(const string& msg)
 {
-  if (wcc.no_warn)
-    return wcc;
-
-  stringstream ostr;
-  Dynare::position last = loc.end - 1;
-  ostr << loc.begin;
-  if (last.filename && (!loc.begin.filename || *loc.begin.filename != *last.filename))
-    ostr << '-' << last;
-  else if (loc.begin.line != last.line)
-    ostr << '-' << last.line << '.' << last.column;
-  else if (loc.begin.column != last.column)
-    ostr << '-' << last.column;
-
-  cerr << ostr.str();
-  wcc.addWarning(ostr.str());
-  return wcc;
-};
+  size_t p {0};
+  while ((p = msg.find('\n', p)) != string::npos)
+    {
+      p++;
+      num_warnings++;
+    }
+}
 
 WarningConsolidation&
 operator<<(WarningConsolidation& wcc, ostream& (*pf)(ostream&))
@@ -58,49 +52,12 @@ operator<<(WarningConsolidation& wcc, ostream& (*pf)(ostream&))
   if (wcc.no_warn)
     return wcc;
 
-  cerr << pf;
-  wcc.addWarning(pf);
-  return wcc;
-}
-
-void
-WarningConsolidation::writeOutput(ostream& output) const
-{
-  if (warnings.str().empty())
-    return;
+  ostringstream ostr;
+  ostr << pf;
 
-  output << "disp([char(10) 'Dynare Preprocessor Warning(s) Encountered:']);" << endl;
-
-  bool writedisp = true;
-  string warningsstr = warnings.str();
-  for (size_t i = 0; i < warningsstr.length(); i++)
-    {
-      if (writedisp)
-        {
-          output << "disp('     ";
-          writedisp = false;
-        }
+  cerr << ostr.str();
 
-      if (warningsstr[i] != '\n')
-        output << warningsstr[i];
-      else
-        {
-          output << "');" << endl;
-          if (i + 1 < warningsstr.length())
-            writedisp = true;
-        }
-    }
-}
+  wcc.incrementWarnings(ostr.str());
 
-int
-WarningConsolidation::countWarnings() const
-{
-  size_t p = 0;
-  int n = 0;
-  while ((p = warnings.str().find('\n', p)) != string::npos)
-    {
-      p++;
-      n++;
-    }
-  return n;
+  return wcc;
 }
diff --git a/src/WarningConsolidation.hh b/src/WarningConsolidation.hh
index ac01f82526c8c098b99765344cc12527816c2f53..1285ffdb69fdcd52b611187e18682b1123580550 100644
--- a/src/WarningConsolidation.hh
+++ b/src/WarningConsolidation.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2012-2023 Dynare Team
+ * Copyright © 2012-2024 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -21,45 +21,64 @@
 #define WARNING_CONSOLIDATION_HH
 
 #include "DynareBisonLocation.hh"
+
+#include <iostream>
+#include <ostream>
 #include <sstream>
 #include <string>
 
 using namespace std;
 
-//! Stores Warnings issued by the Preprocessor
+/* Provide our implementation of operator<< with locations in DynareBison.hh. Note that the
+   following is a template specialization of the version provided in DynareBisonLocation.hh.
+
+   Ideally it should go into DynareBisonLocation.hh, but there does not seem to be a way to achieve
+   that. */
+ostream& operator<<(ostream& stream, const Dynare::location& l);
+
 class WarningConsolidation
 {
 private:
-  stringstream warnings;
-  bool no_warn;
+  const bool no_warn;
+  int num_warnings {0};
+
+  // Increases the warning counter by as many newlines as there are in the message
+  void incrementWarnings(const string& msg);
 
 public:
   explicit WarningConsolidation(bool no_warn_arg) : no_warn {no_warn_arg}
   {
   }
 
-  //! Add A Warning to the StringStream
-  friend WarningConsolidation& operator<<(WarningConsolidation& wcc, const string& warning);
-  friend WarningConsolidation& operator<<(WarningConsolidation& wcc, const Dynare::location& loc);
+  // Generic function to print something to the warning stream
+  friend WarningConsolidation& operator<<(WarningConsolidation& wcc, auto&& warning);
+
+  /* Print std::endl to the warning stream. Unfortunately, since std::endl is a template of
+     functions, it cannot be bound to the universal reference of the generic function, hence the
+     need for this specialization. */
   friend WarningConsolidation& operator<<(WarningConsolidation& wcc, ostream& (*pf)(ostream&));
 
-  void
-  addWarning(const string& w)
-  {
-    warnings << w;
-  };
-  void
-  addWarning(ostream& (*pf)(ostream&))
+  int
+  numWarnings() const
   {
-    warnings << pf;
+    return num_warnings;
   };
-
-  //! Write Warnings to m file
-  void writeOutput(ostream& output) const;
-  //! Count warnings
-  /*! This is done in a very lousy way, by counting newlines in the
-    stringstream... */
-  int countWarnings() const;
 };
 
+WarningConsolidation&
+operator<<(WarningConsolidation& wcc, auto&& warning)
+{
+  if (wcc.no_warn)
+    return wcc;
+
+  ostringstream ostr;
+  ostr << warning;
+
+  cerr << ostr.str();
+
+  wcc.incrementWarnings(ostr.str());
+
+  return wcc;
+}
+
 #endif