diff --git a/src/cc65/error.c b/src/cc65/error.c
index 2ad7133ed..5cd29b388 100644
--- a/src/cc65/error.c
+++ b/src/cc65/error.c
@@ -59,8 +59,10 @@
 
 
 /* Count of errors/warnings */
-unsigned ErrorCount     = 0;
-unsigned WarningCount   = 0;
+unsigned PPErrorCount     = 0;  /* Pre-parser errors */
+unsigned PPWarningCount   = 0;  /* Pre-parser warnings */
+unsigned ErrorCount       = 0;  /* Errors occurred in parser and later translation phases */
+unsigned WarningCount     = 0;  /* Warnings occurred in parser and later translation phases */
 unsigned RecentLineNo     = 0;
 unsigned RecentErrorCount = 0;
 
@@ -197,7 +199,7 @@ void Internal (const char* Format, ...)
 
 
 
-static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va_list ap)
+static void IntError (errcat_t EC, const char* Filename, unsigned LineNo, const char* Msg, va_list ap)
 /* Print an error message - internal function */
 {
     fprintf (stderr, "%s:%u: Error: ", Filename, LineNo);
@@ -208,7 +210,11 @@ static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va
         Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line));
     }
 
-    ++ErrorCount;
+    if (EC != EC_PP) {
+        ++ErrorCount;
+    } else {
+        ++PPErrorCount;
+    }
     if (RecentLineNo != LineNo) {
         RecentLineNo = LineNo;
         RecentErrorCount = 0;
@@ -216,7 +222,7 @@ static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va
         ++RecentErrorCount;
     }
 
-    if (RecentErrorCount > 20 || ErrorCount > 200) {
+    if (RecentErrorCount > 20 || GetTotalErrors () > 200) {
         Fatal ("Too many errors");
     }
 }
@@ -228,18 +234,18 @@ void Error (const char* Format, ...)
 {
     va_list ap;
     va_start (ap, Format);
-    IntError (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap);
+    IntError (EC_PARSER, GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap);
     va_end (ap);
 }
 
 
 
-void LIError (const LineInfo* LI, const char* Format, ...)
+void LIError (errcat_t EC, const LineInfo* LI, const char* Format, ...)
 /* Print an error message with the line info given explicitly */
 {
     va_list ap;
     va_start (ap, Format);
-    IntError (GetInputName (LI), GetInputLine (LI), Format, ap);
+    IntError (EC, GetInputName (LI), GetInputLine (LI), Format, ap);
     va_end (ap);
 }
 
@@ -250,7 +256,7 @@ void PPError (const char* Format, ...)
 {
     va_list ap;
     va_start (ap, Format);
-    IntError (GetCurrentFilename(), GetCurrentLineNum(), Format, ap);
+    IntError (EC_PP, GetCurrentFilename(), GetCurrentLineNum(), Format, ap);
     va_end (ap);
 }
 
@@ -262,13 +268,13 @@ void PPError (const char* Format, ...)
 
 
 
-static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg, va_list ap)
+static void IntWarning (errcat_t EC, const char* Filename, unsigned LineNo, const char* Msg, va_list ap)
 /* Print a warning message - internal function */
 {
     if (IS_Get (&WarningsAreErrors)) {
 
         /* Treat the warning as an error */
-        IntError (Filename, LineNo, Msg, ap);
+        IntError (EC, Filename, LineNo, Msg, ap);
 
     } else if (IS_Get (&WarnEnable)) {
 
@@ -279,7 +285,12 @@ static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg,
         if (Line) {
             Print (stderr, 1, "Input: %.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line));
         }
-        ++WarningCount;
+
+        if (EC != EC_PP) {
+            ++WarningCount;
+        } else {
+            ++PPWarningCount;
+        }
 
     }
 }
@@ -291,18 +302,18 @@ void Warning (const char* Format, ...)
 {
     va_list ap;
     va_start (ap, Format);
-    IntWarning (GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap);
+    IntWarning (EC_PARSER, GetDiagnosticFileName (), GetDiagnosticLineNum (), Format, ap);
     va_end (ap);
 }
 
 
 
-void LIWarning (const LineInfo* LI, const char* Format, ...)
+void LIWarning (errcat_t EC, const LineInfo* LI, const char* Format, ...)
 /* Print a warning message with the line info given explicitly */
 {
     va_list ap;
     va_start (ap, Format);
-    IntWarning (GetInputName (LI), GetInputLine (LI), Format, ap);
+    IntWarning (EC, GetInputName (LI), GetInputLine (LI), Format, ap);
     va_end (ap);
 }
 
@@ -313,7 +324,7 @@ void PPWarning (const char* Format, ...)
 {
     va_list ap;
     va_start (ap, Format);
-    IntWarning (GetCurrentFilename(), GetCurrentLineNum(), Format, ap);
+    IntWarning (EC_PP, GetCurrentFilename(), GetCurrentLineNum(), Format, ap);
     va_end (ap);
 }
 
@@ -398,16 +409,34 @@ void PPNote (const char* Format, ...)
 
 
 /*****************************************************************************/
-/*                                   Code                                    */
+/*                               Error summary                               */
 /*****************************************************************************/
 
 
 
+unsigned GetTotalErrors (void)
+/* Get total count of errors of all categories */
+{
+    return PPErrorCount + ErrorCount;
+}
+
+
+
+unsigned GetTotalWarnings (void)
+/* Get total count of warnings of all categories */
+{
+    return PPWarningCount + WarningCount;
+}
+
+
+
 void ErrorReport (void)
 /* Report errors (called at end of compile) */
 {
-    unsigned int V = (ErrorCount != 0 ? 0 : 1);
-    Print (stdout, V, "%u errors and %u warnings generated.\n", ErrorCount, WarningCount);
+    unsigned TotalErrors = GetTotalErrors ();
+    unsigned TotalWarnings = GetTotalWarnings ();
+    unsigned int V = (TotalErrors != 0 ? 0 : 1);
+    Print (stdout, V, "%u errors and %u warnings generated.\n", TotalErrors, TotalWarnings);
 }
 
 
diff --git a/src/cc65/error.h b/src/cc65/error.h
index 83be8c782..4dce6cf91 100644
--- a/src/cc65/error.h
+++ b/src/cc65/error.h
@@ -55,9 +55,20 @@
 
 
 
+/* Error categories */
+typedef enum errcat_t errcat_t;
+enum errcat_t {
+    EC_PP,      /* Pre-parser phases */
+    EC_PARSER,  /* Parser and later phases */
+};
+
+
+
 /* Count of errors/warnings */
-extern unsigned ErrorCount;
-extern unsigned WarningCount;
+extern unsigned PPErrorCount;           /* Pre-parser errors */
+extern unsigned PPWarningCount;         /* Pre-parser warnings */
+extern unsigned ErrorCount;             /* Errors occurred in parser and later translation phases */
+extern unsigned WarningCount;           /* Warnings occurred in parser and later translation phases */
 
 /* Warning and error options */
 extern IntStack WarnEnable;             /* Enable warnings */
@@ -98,7 +109,7 @@ void Internal (const char* Format, ...) attribute ((noreturn, format (printf, 1,
 void Error (const char* Format, ...) attribute ((format (printf, 1, 2)));
 /* Print an error message */
 
-void LIError (const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 2, 3)));
+void LIError (errcat_t EC, const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4)));
 /* Print an error message with the line info given explicitly */
 
 void PPError (const char* Format, ...) attribute ((format (printf, 1, 2)));
@@ -107,7 +118,7 @@ void PPError (const char* Format, ...) attribute ((format (printf, 1, 2)));
 void Warning (const char* Format, ...) attribute ((format (printf, 1, 2)));
 /* Print a warning message */
 
-void LIWarning (const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 2, 3)));
+void LIWarning (errcat_t EC, const LineInfo* LI, const char* Format, ...) attribute ((format (printf, 3, 4)));
 /* Print a warning message with the line info given explicitly */
 
 void PPWarning (const char* Format, ...) attribute ((format (printf, 1, 2)));
@@ -130,6 +141,12 @@ void LINote (const LineInfo* LI, const char* Format, ...) attribute ((format (pr
 void PPNote (const char* Format, ...) attribute ((format (printf, 1, 2)));
 /* Print a note message. For use within the preprocessor */
 
+unsigned GetTotalErrors (void);
+/* Get total count of errors of all categories */
+
+unsigned GetTotalWarnings (void);
+/* Get total count of warnings of all categories */
+
 void ErrorReport (void);
 /* Report errors (called at end of compile) */
 
diff --git a/src/cc65/main.c b/src/cc65/main.c
index bef646cdd..7dc5417f6 100644
--- a/src/cc65/main.c
+++ b/src/cc65/main.c
@@ -1089,7 +1089,7 @@ int main (int argc, char* argv[])
     Compile (InputFile);
 
     /* Create the output file if we didn't had any errors */
-    if (PreprocessOnly == 0 && (ErrorCount == 0 || Debug)) {
+    if (PreprocessOnly == 0 && (GetTotalErrors () == 0 || Debug)) {
 
         /* Emit literals, do cleanup and optimizations */
         FinishCompile ();
@@ -1115,5 +1115,5 @@ int main (int argc, char* argv[])
     DoneSegAddrSizes ();
 
     /* Return an apropriate exit code */
-    return (ErrorCount > 0)? EXIT_FAILURE : EXIT_SUCCESS;
+    return (GetTotalErrors () > 0)? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/test/err/bug2312-pperror-only.c b/test/err/bug2312-pperror-only.c
new file mode 100644
index 000000000..bdec33956
--- /dev/null
+++ b/test/err/bug2312-pperror-only.c
@@ -0,0 +1,8 @@
+/* Bug #2312 */
+
+#error "Compiler should exit with failure"
+
+int main(void)
+{
+    return 0;
+}
diff --git a/test/ref/Makefile b/test/ref/Makefile
index 9538fdee7..9ecb33c00 100644
--- a/test/ref/Makefile
+++ b/test/ref/Makefile
@@ -63,7 +63,8 @@ CUSTOMSOURCES = \
 # exact error output is required
 ERRORSOURCES = \
 	custom-reference-error.c \
-	bug1889-missing-identifier.c
+	bug1889-missing-identifier.c \
+	bug2312-preprocessor-error.c
 
 SOURCES := $(filter-out $(CUSTOMSOURCES) $(ERRORSOURCES),$(wildcard *.c))
 
diff --git a/test/ref/bug2312-preprocessor-error.c b/test/ref/bug2312-preprocessor-error.c
new file mode 100644
index 000000000..b7872f514
--- /dev/null
+++ b/test/ref/bug2312-preprocessor-error.c
@@ -0,0 +1,8 @@
+/* Bug #2312 - Error recovery from preprocessor errors at the end of a declaration */
+
+typedef int A;  /* ';' consumption triggers PP below */
+
+#define         /* PP error during ';' consumption */
+
+A   f(void);    /* Should be OK */
+int A(void);    /* Should be an error */
diff --git a/test/ref/bug2312-preprocessor-error.cref b/test/ref/bug2312-preprocessor-error.cref
new file mode 100644
index 000000000..680950fd6
--- /dev/null
+++ b/test/ref/bug2312-preprocessor-error.cref
@@ -0,0 +1,2 @@
+bug2312-preprocessor-error.c:5: Error: Missing macro name
+bug2312-preprocessor-error.c:8: Error: Redefinition of typedef 'A' as different kind of symbol
diff --git a/test/ref/custom-reference-error.c b/test/ref/custom-reference-error.c
index 857145fc0..a7c1b6c56 100644
--- a/test/ref/custom-reference-error.c
+++ b/test/ref/custom-reference-error.c
@@ -13,7 +13,10 @@
     and then "make" again to confirm
 */
 
-short main(int argc, char* argv[])
+typedef short return_t;
+#error /* produce an error */
+
+return_t main(int argc, char* argv[])
 {
     printf("%02x", 0x42); /* produce an error */
     n = 0;                /* produce an error */
diff --git a/test/ref/custom-reference-error.cref b/test/ref/custom-reference-error.cref
index 728cc0e15..b21c72dce 100644
--- a/test/ref/custom-reference-error.cref
+++ b/test/ref/custom-reference-error.cref
@@ -1,5 +1,6 @@
-custom-reference-error.c:18: Error: Call to undeclared function 'printf'
-custom-reference-error.c:19: Error: Undeclared identifier 'n'
-custom-reference-error.c:21: Warning: Control reaches end of non-void function [-Wreturn-type]
-custom-reference-error.c:21: Warning: Parameter 'argc' is never used
-custom-reference-error.c:21: Warning: Parameter 'argv' is never used
+custom-reference-error.c:17: Error: #error
+custom-reference-error.c:21: Error: Call to undeclared function 'printf'
+custom-reference-error.c:22: Error: Undeclared identifier 'n'
+custom-reference-error.c:24: Warning: Control reaches end of non-void function [-Wreturn-type]
+custom-reference-error.c:24: Warning: Parameter 'argc' is never used
+custom-reference-error.c:24: Warning: Parameter 'argv' is never used