]> git.sur5r.net Git - cc65/blobdiff - src/ca65/error.c
Merge remote-tracking branch 'upstream/master' into a5200
[cc65] / src / ca65 / error.c
index f480fe961760c2b856247ec4abda5a7f079a5953..6731aa0e574e4abc01277c5ec65146d7ff5a3a3e 100644 (file)
@@ -1,15 +1,15 @@
 /*****************************************************************************/
 /*                                                                           */
-/*                                 error.c                                  */
+/*                                  error.c                                  */
 /*                                                                           */
-/*               Error handling for the ca65 macroassembler                 */
+/*                Error handling for the ca65 macroassembler                 */
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 1998-2003 Ullrich von Bassewitz                                       */
-/*               Römerstrasse 52                                             */
-/*               D-70794 Filderstadt                                         */
-/* EMail:        uz@cc65.org                                                 */
+/* (C) 1998-2012, Ullrich von Bassewitz                                      */
+/*                Roemerstrasse 52                                           */
+/*                D-70794 Filderstadt                                        */
+/* EMail:         uz@cc65.org                                                */
 /*                                                                           */
 /*                                                                           */
 /* This software is provided 'as-is', without any expressed or implied       */
 #include <stdlib.h>
 #include <stdarg.h>
 
+/* common */
+#include "strbuf.h"
+
 /* ca65 */
+#include "error.h"
 #include "filetab.h"
+#include "lineinfo.h"
 #include "nexttok.h"
-#include "error.h"
 
 
 
 /*****************************************************************************/
-/*                                  Data                                    */
+/*                                   Data                                    */
 /*****************************************************************************/
 
 
 
 /* Warning level */
-unsigned WarnLevel         = 1;
+unsigned WarnLevel      = 1;
 
 /* Statistics */
-unsigned ErrorCount    = 0;
-unsigned WarningCount  = 0;
+unsigned ErrorCount     = 0;
+unsigned WarningCount   = 0;
+
+/* Maximum number of additional notifications */
+#define MAX_NOTES       8
 
 
 
 /*****************************************************************************/
-/*                                Warnings                                  */
+/*                             Helper functions                              */
 /*****************************************************************************/
 
 
 
-void WarningMsg (const FilePos* Pos, unsigned WarnNum, va_list ap)
-/* Print warning message. */
+static void VPrintMsg (const FilePos* Pos, const char* Desc,
+                       const char* Format, va_list ap)
+/* Format and output an error/warning message. */
 {
-    static const struct {
-       unsigned char   Level;
-       const char*     Msg;
-    } Warnings [WARN_COUNT-1] = {
-       {   1,  "Mask error"                                    },
-               {   2,  "Symbol `%s' is defined but never used"         },
-        {   2,  "Symbol `%s' is imported but never used"       },
-       {   1,  "Cannot track processor status byte"            },
-               {   0,  "User warning: %s"                              },
-    };
-
-    if (Warnings [WarnNum-1].Level <= WarnLevel) {
-       fprintf (stderr, "%s(%lu): Warning #%u: ",
-                GetFileName (Pos->Name), Pos->Line, WarnNum);
-       vfprintf (stderr, Warnings [WarnNum-1].Msg, ap);
-       fprintf (stderr, "\n");
-       ++WarningCount;
-    }
+    StrBuf S = STATIC_STRBUF_INITIALIZER;
+
+    /* Format the actual message */
+    StrBuf Msg = STATIC_STRBUF_INITIALIZER;
+    SB_VPrintf (&Msg, Format, ap);
+    SB_Terminate (&Msg);
+
+    /* Format the message header */
+    SB_Printf (&S, "%s(%u): %s: ",
+               SB_GetConstBuf (GetFileName (Pos->Name)),
+               Pos->Line,
+               Desc);
+
+    /* Append the message to the message header */
+    SB_Append (&S, &Msg);
+
+    /* Delete the formatted message */
+    SB_Done (&Msg);
+
+    /* Add a new line and terminate the generated full message */
+    SB_AppendChar (&S, '\n');
+    SB_Terminate (&S);
+
+    /* Output the full message */
+    fputs (SB_GetConstBuf (&S), stderr);
+
+    /* Delete the buffer for the full message */
+    SB_Done (&S);
 }
 
 
 
-void Warning (unsigned WarnNum, ...)
-/* Print warning message. */
+static void PrintMsg (const FilePos* Pos, const char* Desc,
+                      const char* Format, ...)
+/* Format and output an error/warning message. */
 {
     va_list ap;
-    va_start (ap, WarnNum);
-    WarningMsg (&CurPos, WarnNum, ap);
+    va_start (ap, Format);
+    VPrintMsg (Pos, Desc, Format, ap);
     va_end (ap);
 }
 
 
 
-void PWarning (const FilePos* Pos, unsigned WarnNum, ...)
+static void AddNotifications (const Collection* LineInfos)
+/* Output additional notifications for an error or warning */
+{
+    unsigned I;
+    unsigned Output;
+    unsigned Skipped;
+
+    /* The basic line info is always in slot zero. It has been used to
+     * output the actual error or warning. The following slots may contain
+     * more information. Check them and print additional notifications if
+     * they're present, but limit the number to a reasonable value.
+     */
+    for (I = 1, Output = 0, Skipped = 0; I < CollCount (LineInfos); ++I) {
+        /* Get next line info */
+        const LineInfo* LI = CollConstAt (LineInfos, I);
+        /* Check the type and output an appropriate note */
+        const char* Msg;
+        switch (GetLineInfoType (LI)) {
+
+            case LI_TYPE_ASM:
+                Msg = "Expanded from here";
+                break;
+
+            case LI_TYPE_EXT:
+                Msg = "Assembler code generated from this line";
+                break;
+
+            case LI_TYPE_MACRO:
+                Msg = "Macro was defined here";
+                break;
+
+            case LI_TYPE_MACPARAM:
+                Msg = "Macro parameter came from here";
+                break;
+
+            default:
+                /* No output */
+                Msg = 0;
+                break;
+
+        }
+
+        /* Output until an upper limit of messages is reached */
+        if (Msg) {
+            if (Output < MAX_NOTES) {
+                PrintMsg (GetSourcePos (LI), "Note", "%s", Msg);
+                ++Output;
+            } else {
+                ++Skipped;
+            }
+        }
+    }
+
+    /* Add a note if we have more stuff that we won't output */
+    if (Skipped > 0) {
+        const LineInfo* LI = CollConstAt (LineInfos, 0);
+        PrintMsg (GetSourcePos (LI), "Note",
+                  "Dropping %u additional line infos", Skipped);
+    }
+}
+
+
+
+/*****************************************************************************/
+/*                                 Warnings                                  */
+/*****************************************************************************/
+
+
+
+static void WarningMsg (const Collection* LineInfos, const char* Format, va_list ap)
+/* Print warning message. */
+{
+    /* The first entry in the collection is that of the actual source pos */
+    const LineInfo* LI = CollConstAt (LineInfos, 0);
+
+    /* Output a warning for this position */
+    VPrintMsg (GetSourcePos (LI), "Warning", Format, ap);
+
+    /* Add additional notifications if necessary */
+    AddNotifications (LineInfos);
+
+    /* Count warnings */
+    ++WarningCount;
+}
+
+
+
+void Warning (unsigned Level, const char* Format, ...)
+/* Print warning message. */
+{
+    if (Level <= WarnLevel) {
+
+        va_list ap;
+        Collection LineInfos = STATIC_COLLECTION_INITIALIZER;
+
+        /* Get line infos for the current position */
+        GetFullLineInfo (&LineInfos);
+
+        /* Output the message */
+        va_start (ap, Format);
+        WarningMsg (&LineInfos, Format, ap);
+        va_end (ap);
+
+        /* Free the line info list */
+        ReleaseFullLineInfo (&LineInfos);
+        DoneCollection (&LineInfos);
+    }
+}
+
+
+
+void PWarning (const FilePos* Pos, unsigned Level, const char* Format, ...)
 /* Print warning message giving an explicit file and position. */
 {
-    va_list ap;
-    va_start (ap, WarnNum);
-    WarningMsg (Pos, WarnNum, ap);
-    va_end (ap);
+    if (Level <= WarnLevel) {
+        va_list ap;
+        va_start (ap, Format);
+        VPrintMsg (Pos, "Warning", Format, ap);
+        va_end (ap);
+
+        /* Count warnings */
+        ++WarningCount;
+    }
+}
+
+
+
+void LIWarning (const Collection* LineInfos, unsigned Level, const char* Format, ...)
+/* Print warning message using the given line infos */
+{
+    if (Level <= WarnLevel) {
+        /* Output the message */
+        va_list ap;
+        va_start (ap, Format);
+        WarningMsg (LineInfos, Format, ap);
+        va_end (ap);
+    }
 }
 
 
 
 /*****************************************************************************/
-/*                                 Errors                                   */
+/*                                  Errors                                   */
 /*****************************************************************************/
 
 
 
-void ErrorMsg (const FilePos* Pos, unsigned ErrNum, va_list ap)
+void ErrorMsg (const Collection* LineInfos, const char* Format, va_list ap)
 /* Print an error message */
 {
-    static const char* Msgs [ERR_COUNT-1] = {
-       "Command/operation not implemented",
-       "Cannot open include file `%s': %s",
-       "Cannot read from include file `%s': %s",
-       "Include nesting too deep",
-        "Invalid input character: %02X",
-       "Hex digit expected",
-       "Digit expected",
-       "`0' or `1' expected",
-       "Numerical overflow",
-        "Control statement expected",
-       "Too many characters",
-       "`:' expected",
-               "`(' expected",
-       "`)' expected",
-       "`]' expected",
-       "`,' expected",
-        "Boolean switch value expected (on/off/+/-)",
-       "`Y' expected",
-       "`X' expected",
-       "Integer constant expected",
-       "String constant expected",
-       "Character constant expected",
-       "Constant expression expected",
-       "Identifier expected",
-       "`.ENDMACRO' expected",
-       "Option key expected",
-       "`=' expected",
-       "Command is only valid in 65816 mode",
-       "User error: %s",
-       "String constant too long",
-       "Newline in string constant",
-       "Illegal character constant",
-       "Illegal addressing mode",
-       "Illegal character to start local symbols",
-       "Illegal use of local symbol",
-       "Illegal segment name: `%s'",
-       "Illegal segment attribute",
-       "Illegal macro package name",
-       "Illegal emulation feature",
-        "Illegal scope specifier",
-       "Syntax error",
-       "Symbol `%s' is already defined",
-       "Undefined symbol `%s'",
-       "Symbol `%s' is already marked as import",
-        "Symbol `%s' is already marked as export",
-       "Exported symbol `%s' is undefined",
-       "Exported values must be constant",
-       "Unexpected end of file",
-       "Unexpected end of line",
-       "Unexpected `%s'",
-       "Division by zero",
-       "Modulo operation with zero",
-        "Range error",
-       "Too many macro parameters",
-       "Macro parameter expected",
-       "Circular reference in symbol definition",
-               "Symbol `%s' redeclaration mismatch",
-        "Alignment value must be a power of 2",
-       "Duplicate `.ELSE'",
-               "Conditional assembly branch was never closed",
-       "Lexical level was not terminated correctly",
-        "No open lexical level",
-       "Segment attribute mismatch",
-        "Segment stack overflow",
-        "Segment stack is empty",
-        "Segment stack is not empty at end of assembly",
-       "CPU not supported",
-       "Counter underflow",
-       "Undefined label",
-               "Open `%s'",
-       "File name `%s' not found in file table",
-    };
-
-    fprintf (stderr, "%s(%lu): Error #%u: ",
-            GetFileName (Pos->Name), Pos->Line, ErrNum);
-    vfprintf (stderr, Msgs [ErrNum-1], ap);
-    fprintf (stderr, "\n");
+    /* The first entry in the collection is that of the actual source pos */
+    const LineInfo* LI = CollConstAt (LineInfos, 0);
+
+    /* Output an error for this position */
+    VPrintMsg (GetSourcePos (LI), "Error", Format, ap);
+
+    /* Add additional notifications if necessary */
+    AddNotifications (LineInfos);
+
+    /* Count errors */
     ++ErrorCount;
 }
 
 
 
-void Error (unsigned ErrNum, ...)
+void Error (const char* Format, ...)
 /* Print an error message */
 {
     va_list ap;
-    va_start (ap, ErrNum);
-    ErrorMsg (&CurPos, ErrNum, ap);
+    Collection LineInfos = STATIC_COLLECTION_INITIALIZER;
+
+    /* Get line infos for the current position */
+    GetFullLineInfo (&LineInfos);
+
+    /* Output the message */
+    va_start (ap, Format);
+    ErrorMsg (&LineInfos, Format, ap);
     va_end (ap);
+
+    /* Free the line info list */
+    ReleaseFullLineInfo (&LineInfos);
+    DoneCollection (&LineInfos);
 }
 
 
 
-void PError (const FilePos* Pos, unsigned ErrNum, ...)
+void PError (const FilePos* Pos, const char* Format, ...)
 /* Print an error message giving an explicit file and position. */
 {
     va_list ap;
-    va_start (ap, ErrNum);
-    ErrorMsg (Pos, ErrNum, ap);
+    va_start (ap, Format);
+    VPrintMsg (Pos, "Error", Format, ap);
+    va_end (ap);
+
+    /* Count errors */
+    ++ErrorCount;
+}
+
+
+
+void LIError (const Collection* LineInfos, const char* Format, ...)
+/* Print an error message using the given line infos. */
+{
+    /* Output an error for this position */
+    va_list ap;
+    va_start (ap, Format);
+    ErrorMsg (LineInfos, Format, ap);
     va_end (ap);
 }
 
 
 
-void ErrorSkip (unsigned ErrNum, ...)
+void ErrorSkip (const char* Format, ...)
 /* Print an error message and skip the rest of the line */
 {
     va_list ap;
-    va_start (ap, ErrNum);
-    ErrorMsg (&CurPos, ErrNum, ap);
+    Collection LineInfos = STATIC_COLLECTION_INITIALIZER;
+
+    /* Get line infos for the current position */
+    GetFullLineInfo (&LineInfos);
+
+    /* Output the message */
+    va_start (ap, Format);
+    ErrorMsg (&LineInfos, Format, ap);
     va_end (ap);
 
+    /* Free the line info list */
+    ReleaseFullLineInfo (&LineInfos);
+    DoneCollection (&LineInfos);
+
+    /* Skip tokens until we reach the end of the line */
     SkipUntilSep ();
 }
 
 
 
 /*****************************************************************************/
-/*                                  Code                                    */
+/*                                   Code                                    */
 /*****************************************************************************/
 
 
 
-void Fatal (unsigned FatNum, ...)
+void Fatal (const char* Format, ...)
 /* Print a message about a fatal error and die */
 {
-    static const char* Msgs [FAT_COUNT-1] = {
-       "Maximum number of input files reached",
-       "Out of memory",
-       "Too many segments",
-       "String too long",
-       "Cannot open input file `%s': %s",
-       "Cannot stat input file `%s': %s",
-       "Cannot open output file `%s': %s",
-       "Cannot write to output file `%s': %s",
-       "Cannot open listing file: %s",
-       "Cannot write to listing file: %s",
-       "Cannot read from listing file: %s",
-       "Too many nested constructs",
-       ".IF nesting too deep",
-       "Too many symbols",
-    };
     va_list ap;
+    StrBuf S = STATIC_STRBUF_INITIALIZER;
 
-    va_start (ap, FatNum);
-    fprintf (stderr, "Fatal #%u: ", FatNum);
-    vfprintf (stderr, Msgs [FatNum-1], ap);
-    fprintf (stderr, "\n");
+    va_start (ap, Format);
+    SB_VPrintf (&S, Format, ap);
+    SB_Terminate (&S);
     va_end (ap);
 
+    fprintf (stderr, "Fatal error: %s\n", SB_GetConstBuf (&S));
+
+    SB_Done (&S);
+
     /* And die... */
     exit (EXIT_FAILURE);
 }
@@ -279,17 +384,19 @@ void Fatal (unsigned FatNum, ...)
 
 
 void Internal (const char* Format, ...)
-/* Print a message about an internal compiler error and die. */
+/* Print a message about an internal assembler error and die. */
 {
     va_list ap;
+    StrBuf S = STATIC_STRBUF_INITIALIZER;
+
     va_start (ap, Format);
-    fprintf (stderr, "Internal assembler error\n");
-    vfprintf (stderr, Format, ap);
+    SB_VPrintf (&S, Format, ap);
+    SB_Terminate (&S);
     va_end (ap);
-    fprintf (stderr, "\n");
-
-    exit (EXIT_FAILURE);
-}
 
+    fprintf (stderr, "Internal assembler error: %s\n", SB_GetConstBuf (&S));
 
+    SB_Done (&S);
 
+    exit (EXIT_FAILURE);
+}