1 (* M2Error.mod error reporting interface.
3 Copyright (C) 2001-2023 Free Software Foundation, Inc.
4 Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 This file is part of GNU Modula-2.
8 GNU Modula-2 is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
13 GNU Modula-2 is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GNU Modula-2; see the file COPYING3. If not see
20 <http://www.gnu.org/licenses/>. *)
22 IMPLEMENTATION MODULE M2Error ;
24 FROM NameKey IMPORT NulName, Name, KeyToCharStar ;
25 FROM DynamicStrings IMPORT String, InitString, InitStringCharStar, ConCat, ConCatChar, Mark, string, KillString, Dup ;
26 FROM FIO IMPORT StdOut, WriteNBytes, Close, FlushBuffer ;
27 FROM StrLib IMPORT StrLen, StrEqual ;
28 FROM FormatStrings IMPORT Sprintf0, Sprintf1, Sprintf2, Sprintf3 ;
29 FROM M2LexBuf IMPORT FindFileNameFromToken, TokenToLineNo, TokenToColumnNo, GetTokenNo ;
30 FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
31 FROM M2Printf IMPORT printf0, printf1, printf2, printf3 ;
32 FROM M2Options IMPORT Xcode ;
33 FROM M2RTS IMPORT ExitOnHalt ;
34 FROM SYSTEM IMPORT ADDRESS ;
35 FROM M2Emit IMPORT EmitError ;
36 FROM M2LexBuf IMPORT UnknownTokenNo ;
37 FROM M2StackAddress IMPORT StackOfAddress, InitStackAddress, PushAddress, PopAddress, NoOfItemsInStackAddress ;
38 FROM Indexing IMPORT Index, HighIndice, InitIndex, GetIndice, PutIndice ;
39 FROM M2Debug IMPORT Assert ;
40 FROM M2Pass IMPORT IsPass0, IsPass1 ;
41 FROM SymbolTable IMPORT NulSym ;
43 FROM M2ColorString IMPORT filenameColor, endColor, errorColor, warningColor, noteColor,
44 range1Color, range2Color, quoteOpen, quoteClose ;
55 Error = POINTER TO RECORD
62 (* index of token causing the error *)
68 KindScope = (noscope, definition, implementation, program, module, procedure) ;
70 ErrorScope = POINTER TO RECORD
71 scopeKind: KindScope ;
73 symbol : CARDINAL ; (* symbol table entry. *)
79 InInternal : BOOLEAN ;
80 lastScope : ErrorScope ;
81 scopeIndex : CARDINAL ;
83 currentScope: ErrorScope ;
84 scopeStack : StackOfAddress ;
88 SetColor - informs the error module that this error will have had colors
89 assigned to it. If an error is issued without colors assigned
90 then the default colors will be assigned to the legacy error
94 PROCEDURE SetColor (e: Error) : Error ;
105 PROCEDURE Cast (VAR a: ARRAY OF BYTE; b: ARRAY OF BYTE) ;
111 FOR i := 0 TO HIGH(a) DO
119 TranslateNameToString - takes a format specification string, a, and
120 if they consist of of %a then this is translated
121 into a String and %a is replaced by %s.
124 PROCEDURE TranslateNameToCharStar (VAR a: ARRAY OF CHAR;
125 n: CARDINAL) : BOOLEAN ;
134 IF (a[i]='%') AND (i+1<h)
136 IF (a[i+1]='a') AND (argno=n)
151 END TranslateNameToCharStar ;
155 InternalError - displays an internal error message together with the compiler source
156 file and line number.
157 This function is not buffered and is used when the compiler is about
161 PROCEDURE InternalError (message: ARRAY OF CHAR) <* noreturn *> ;
168 M2Emit.InternalError (message) ;
173 (* ***************************************************************************
174 The following routines are used for normal syntax and semantic error reporting
175 *************************************************************************** *)
179 WriteFormat0 - displays the source module and line together
180 with the encapsulated format string.
181 Used for simple error messages tied to the current token.
184 PROCEDURE WriteFormat0 (a: ARRAY OF CHAR) ;
188 e := NewError(GetTokenNo()) ;
190 s := Sprintf0(Mark(InitString(a)))
196 WarnFormat0 - displays the source module and line together
197 with the encapsulated format string.
198 Used for simple warning messages tied to the current token.
201 PROCEDURE WarnFormat0 (a: ARRAY OF CHAR) ;
205 e := NewWarning(GetTokenNo()) ;
207 s := Sprintf0(Mark(InitString(a)))
216 PROCEDURE DoFormat1 (a: ARRAY OF CHAR; w: ARRAY OF BYTE) : String ;
222 IF TranslateNameToCharStar(a, 1)
225 s := Mark(InitStringCharStar(KeyToCharStar(n))) ;
226 s := Sprintf1(Mark(InitString(a)), s)
228 s := Sprintf1(Mark(InitString(a)), w)
235 WriteFormat1 - displays the source module and line together
236 with the encapsulated format string.
237 Used for simple error messages tied to the current token.
240 PROCEDURE WriteFormat1 (a: ARRAY OF CHAR; w: ARRAY OF BYTE) ;
244 e := NewError(GetTokenNo()) ;
245 e^.s := DoFormat1(a, w)
250 WarnFormat1 - displays the source module and line together
251 with the encapsulated format string.
252 Used for simple warning messages tied to the current token.
255 PROCEDURE WarnFormat1 (a: ARRAY OF CHAR; w: ARRAY OF BYTE) ;
259 e := NewWarning(GetTokenNo()) ;
260 e^.s := DoFormat1(a, w)
268 PROCEDURE DoFormat2 (a: ARRAY OF CHAR; w1, w2: ARRAY OF BYTE) : String ;
277 IF TranslateNameToCharStar(a, 1)
280 s1 := Mark(InitStringCharStar(KeyToCharStar(n))) ;
283 IF TranslateNameToCharStar(a, 2)
286 s2 := Mark(InitStringCharStar(KeyToCharStar(n))) ;
291 {} : s := Sprintf2(Mark(InitString(a)), w1, w2) |
292 {1} : s := Sprintf2(Mark(InitString(a)), s1, w2) |
293 {2} : s := Sprintf2(Mark(InitString(a)), w1, s2) |
294 {1,2}: s := Sprintf2(Mark(InitString(a)), s1, s2)
304 WriteFormat2 - displays the module and line together with the encapsulated
306 Used for simple error messages tied to the current token.
309 PROCEDURE WriteFormat2 (a: ARRAY OF CHAR; w1, w2: ARRAY OF BYTE) ;
313 e := NewError(GetTokenNo()) ;
314 e^.s := DoFormat2(a, w1, w2)
318 PROCEDURE DoFormat3 (a: ARRAY OF CHAR; w1, w2, w3: ARRAY OF BYTE) : String ;
321 s, s1, s2, s3: String ;
326 IF TranslateNameToCharStar(a, 1)
329 s1 := Mark(InitStringCharStar(KeyToCharStar(n))) ;
332 IF TranslateNameToCharStar(a, 2)
335 s2 := Mark(InitStringCharStar(KeyToCharStar(n))) ;
338 IF TranslateNameToCharStar(a, 3)
341 s3 := Mark(InitStringCharStar(KeyToCharStar(n))) ;
346 {} : s := Sprintf3(Mark(InitString(a)), w1, w2, w3) |
347 {1} : s := Sprintf3(Mark(InitString(a)), s1, w2, w3) |
348 {2} : s := Sprintf3(Mark(InitString(a)), w1, s2, w3) |
349 {1,2} : s := Sprintf3(Mark(InitString(a)), s1, s2, w3) |
350 {3} : s := Sprintf3(Mark(InitString(a)), w1, w2, s3) |
351 {1,3} : s := Sprintf3(Mark(InitString(a)), s1, w2, s3) |
352 {2,3} : s := Sprintf3(Mark(InitString(a)), w1, s2, s3) |
353 {1,2,3}: s := Sprintf3(Mark(InitString(a)), s1, s2, s3)
363 WriteFormat3 - displays the module and line together with the encapsulated
365 Used for simple error messages tied to the current token.
368 PROCEDURE WriteFormat3 (a: ARRAY OF CHAR; w1, w2, w3: ARRAY OF BYTE) ;
372 e := NewError(GetTokenNo()) ;
373 e^.s := DoFormat3(a, w1, w2, w3)
378 MoveError - repositions an error, e, to token, AtTokenNo, and returns, e.
381 PROCEDURE MoveError (e: Error; AtTokenNo: CARDINAL) : Error ;
385 e^.token := AtTokenNo
392 NewError - creates and returns a new error handle.
395 PROCEDURE NewError (AtTokenNo: CARDINAL) : Error ;
399 IF AtTokenNo = UnknownTokenNo
401 (* this could be used as a useful debugging hook as the front end
402 has forgotten the token no. This can occur if a complex record
403 structure or array is used for example. *)
404 AtTokenNo := GetTokenNo ()
417 (* Assert (scopeKind # noscope) ; *)
418 e^.scope := currentScope ;
419 IF (head=NIL) OR (head^.token>AtTokenNo)
425 WHILE (f^.next#NIL) AND (f^.next^.token<AtTokenNo) DO
436 NewWarning - creates and returns a new error handle suitable for a warning.
437 A warning will not stop compilation.
440 PROCEDURE NewWarning (AtTokenNo: CARDINAL) : Error ;
444 e := NewError(AtTokenNo) ;
452 NewNote - creates and returns a new error handle suitable for a note.
453 A note will not stop compilation.
456 PROCEDURE NewNote (AtTokenNo: CARDINAL) : Error ;
460 e := NewError(AtTokenNo) ;
468 ChainError - creates and returns a new error handle, this new error
469 is associated with, e, and is chained onto the end of, e.
470 If, e, is NIL then the result to NewError is returned.
473 PROCEDURE ChainError (AtTokenNo: CARDINAL; e: Error) : Error ;
479 RETURN NewError (AtTokenNo)
498 ErrorFormat routines provide a printf capability for the error handle.
501 PROCEDURE ErrorFormat0 (e: Error; a: ARRAY OF CHAR) ;
506 s := Sprintf0(Mark(InitString(a)))
508 s := ConCat(s, Mark(Sprintf0(Mark(InitString(a)))))
514 PROCEDURE ErrorFormat1 (e: Error; a: ARRAY OF CHAR; w: ARRAY OF BYTE) ;
518 s1 := DoFormat1(a, w) ;
524 s := ConCat(s, Mark(s1))
530 PROCEDURE ErrorFormat2 (e: Error; a: ARRAY OF CHAR; w1, w2: ARRAY OF BYTE) ;
534 s1 := DoFormat2(a, w1, w2) ;
540 s := ConCat(s, Mark(s1))
546 PROCEDURE ErrorFormat3 (e: Error; a: ARRAY OF CHAR;
547 w1, w2, w3: ARRAY OF BYTE) ;
551 s1 := DoFormat3(a, w1, w2, w3) ;
557 s := ConCat(s, Mark(s1))
563 PROCEDURE ErrorString (e: Error; str: String) ;
572 Init - initializes the error list.
578 InInternal := FALSE ;
579 scopeStack := InitStackAddress () ;
580 scopeArray := InitIndex (1) ;
581 currentScope := NIL ;
587 CheckIncludes - generates a sequence of error messages which determine the relevant
588 included file and line number.
592 In file included from b.h:1,
594 c.h:1: parse error before `and'
596 where a.c is: #include "b.h"
597 b.h is: #include "c.h"
598 c.h is: and this and that
600 we attempt to follow the error messages that gcc issues.
603 PROCEDURE CheckIncludes (token: CARDINAL; depth: CARDINAL) ;
608 included := FindFileNameFromToken(token, depth+1) ;
611 lineno := TokenToLineNo(token, depth+1) ;
614 printf2('In file included from %s:%d', included, lineno)
616 printf2(' from %s:%d', included, lineno)
618 IF FindFileNameFromToken(token, depth+2)=NIL
624 CheckIncludes(token, depth+1)
630 FlushAll - flushes all errors in list, e.
633 PROCEDURE FlushAll (e: Error; FatalStatus: BOOLEAN) : BOOLEAN ;
643 IF (FatalStatus=fatal) AND (s#NIL)
645 currentScope := scope ;
646 CheckIncludes (token, 0) ;
647 EmitError (fatal, note, token, AnnounceScope (e, s)) ;
648 IF (child#NIL) AND FlushAll (child, FatalStatus)
671 FlushErrors - switches the output channel to the error channel
672 and then writes out all errors.
675 PROCEDURE FlushErrors ;
679 printf0('\nFlushing all errors\n') ;
680 printf0('===================\n')
682 IF FlushAll (head, TRUE)
691 FlushWarnings - switches the output channel to the error channel
692 and then writes out all warnings.
693 If an error is present the compilation is terminated,
694 if warnings only were emitted then compilation will
698 PROCEDURE FlushWarnings ;
700 IF FlushAll (head, FALSE)
707 ErrorStringsAt2 - given error strings, s1, and, s2, it places these
708 strings at token positions, tok1 and tok2, respectively.
709 Both strings are consumed.
712 PROCEDURE ErrorStringsAt2 (s1, s2: String; tok1, tok2: CARDINAL) ;
720 e := NewError(tok1) ;
722 ErrorString(ChainError(tok2, e), s2)
723 END ErrorStringsAt2 ;
727 ErrorStringAt2 - given an error string, s, it places this
728 string at token positions, tok1 and tok2, respectively.
729 The string is consumed.
732 PROCEDURE ErrorStringAt2 (s: String; tok1, tok2: CARDINAL) ;
734 ErrorStringsAt2(s, s, tok1, tok2)
739 ErrorStringAt - given an error string, s, it places this
740 string at token position, tok.
741 The string is consumed.
744 PROCEDURE ErrorStringAt (s: String; tok: CARDINAL) ;
754 WarnStringsAt2 - given warning strings, s1, and, s2, it places these
755 strings at token positions, tok1 and tok2, respectively.
756 Both strings are consumed.
759 PROCEDURE WarnStringsAt2 (s1, s2: String; tok1, tok2: CARDINAL) ;
767 e := NewWarning(tok1) ;
769 ErrorString(ChainError(tok2, e), s2)
774 WarnStringAt2 - given an warning string, s, it places this
775 string at token positions, tok1 and tok2, respectively.
776 The string is consumed.
779 PROCEDURE WarnStringAt2 (s: String; tok1, tok2: CARDINAL) ;
781 WarnStringsAt2(s, s, tok1, tok2)
786 WarnStringAt - given an error string, s, it places this
787 string at token position, tok.
788 The string is consumed.
791 PROCEDURE WarnStringAt (s: String; tok: CARDINAL) ;
795 e := NewWarning(tok) ;
801 ErrorAbort0 - aborts compiling, it flushes all warnings and errors before aborting.
804 PROCEDURE ErrorAbort0 (a: ARRAY OF CHAR) ;
807 IF NOT StrEqual(a, '')
811 IF NOT FlushAll (head, TRUE)
813 WriteFormat0('unidentified error') ;
814 IF FlushAll (head, TRUE)
824 IsErrorScopeNul - returns TRUE if es is NIL or it has a NulName.
827 PROCEDURE IsErrorScopeNul (es: ErrorScope) : BOOLEAN ;
829 RETURN (es = NIL) OR (es^.scopeName = NulName)
830 END IsErrorScopeNul ;
834 GetAnnounceScope - return message with the error scope attached to message.
835 filename and message are treated as read only by this
839 PROCEDURE GetAnnounceScope (filename, message: String) : String ;
848 pre := InitString ('')
850 pre := Sprintf1 (Mark (InitString ("%s: ")), filename)
853 IF NOT IsErrorScopeNul (currentScope)
855 quoted := InitString ('') ;
856 quoted := quoteOpen (quoted) ;
857 quoted := ConCat (quoted, Mark (InitStringCharStar (KeyToCharStar (currentScope^.scopeName)))) ;
858 quoted := quoteClose (quoted)
861 IF currentScope = NIL
863 desc := InitString ("no scope active")
865 CASE currentScope^.scopeKind OF
867 definition : desc := InitString ("In definition module") |
868 implementation: desc := InitString ("In implementation module") |
869 program : desc := InitString ("In program module") |
870 module : desc := InitString ("In inner module") |
871 procedure : desc := InitString ("In procedure")
875 fmt := ConCat (pre, Mark (desc)) ;
876 IF IsErrorScopeNul (currentScope)
878 fmt := ConCat (fmt, Sprintf0 (Mark (InitString (": "))))
880 fmt := ConCat (fmt, Sprintf1 (Mark (InitString (" %s: ")), quoted))
882 RETURN ConCat (fmt, message)
883 END GetAnnounceScope ;
887 IsSameScope - return TRUE if a and b refer to the same scope.
890 PROCEDURE IsSameScope (a, b: ErrorScope) : BOOLEAN ;
895 ELSIF (a = NIL) OR (b = NIL)
899 (* this does not compare the symbol field. *)
900 RETURN (a^.scopeKind = b^.scopeKind) AND (a^.scopeName = b^.scopeName)
906 AnnounceScope - return the error string s with a scope description prepended
907 assuming that scope has changed.
910 PROCEDURE AnnounceScope (e: Error; message: String) : String ;
912 IF NOT IsSameScope (lastScope, e^.scope)
914 lastScope := e^.scope ;
915 IF IsErrorScopeNul (lastScope)
917 RETURN ConCat (InitString ("no scope active: "), message)
919 Assert ((e^.scope # NIL) AND (e^.scope^.scopeKind # noscope)) ;
920 (* filename := FindFileNameFromToken (e^.token, 0) ; *)
921 message := GetAnnounceScope (NIL, message)
929 newErrorScope - create an ErrorScope of kindScope and return the object.
930 It is also added the a dynamic array.
933 PROCEDURE newErrorScope (kind: KindScope) : ErrorScope ;
941 es^.scopeKind := kind ;
942 es^.scopeName := NulName ;
943 es^.symbol := NulSym ;
944 PutIndice (scopeArray, HighIndice (scopeArray) + 1, es) ;
947 c := HighIndice (scopeArray) ;
948 printf2 ("pass 0: %d %d\n", c, kind)
952 es := GetIndice (scopeArray, scopeIndex) ;
957 printf3 ("pass 1: %d %d %d\n", scopeIndex, es^.scopeKind, kind)
959 printf3 ("pass 2: %d %d %d\n", scopeIndex, es^.scopeKind, kind)
962 Assert (es^.scopeKind = kind)
969 DefaultProgramModule - sets up an unnamed program scope before the Ident is seen.
972 PROCEDURE DefaultProgramModule ;
974 PushAddress (scopeStack, currentScope) ;
975 currentScope := newErrorScope (program)
976 END DefaultProgramModule ;
980 DefaultImplementationModule - sets up an unnamed implementation
981 scope before the Ident is seen.
984 PROCEDURE DefaultImplementationModule ;
986 PushAddress (scopeStack, currentScope) ;
987 currentScope := newErrorScope (implementation)
988 END DefaultImplementationModule ;
992 DefaultDefinitionModule - sets up an unnamed definition
993 scope before the Ident is seen.
996 PROCEDURE DefaultDefinitionModule ;
998 PushAddress (scopeStack, currentScope) ;
999 currentScope := newErrorScope (definition)
1000 END DefaultDefinitionModule ;
1004 DefaultInnerModule - sets up an unnamed inner
1005 scope before the Ident is seen.
1008 PROCEDURE DefaultInnerModule ;
1010 PushAddress (scopeStack, currentScope) ;
1011 currentScope := newErrorScope (module)
1012 END DefaultInnerModule ;
1016 DefaultProcedure - sets up an unnamed procedure
1017 scope before the Ident is seen.
1020 PROCEDURE DefaultProcedure ;
1022 PushAddress (scopeStack, currentScope) ;
1023 currentScope := newErrorScope (procedure)
1024 END DefaultProcedure ;
1028 EnterImplementationScope - signifies to the error routines that the front end
1029 has started to compile implementation module scopeName.
1032 PROCEDURE EnterImplementationScope (scopename: Name) ;
1034 Assert (currentScope # NIL) ;
1035 Assert (currentScope^.scopeKind = implementation) ;
1036 IF currentScope^.scopeName = NulName
1040 printf1 ("seen implementation: %a\n", scopename)
1042 currentScope^.scopeName := scopename
1044 END EnterImplementationScope ;
1048 EnterProgramScope - signifies to the error routines that the front end
1049 has started to compile program module scopeName.
1052 PROCEDURE EnterProgramScope (scopename: Name) ;
1054 Assert (currentScope # NIL) ;
1055 Assert (currentScope^.scopeKind = program) ;
1056 IF currentScope^.scopeName = NulName
1060 printf1 ("seen program: %a\n", scopename)
1062 currentScope^.scopeName := scopename
1064 END EnterProgramScope ;
1068 EnterModuleScope - signifies to the error routines that the front end
1069 has started to compile an inner module scopeName.
1072 PROCEDURE EnterModuleScope (scopename: Name) ;
1074 Assert (currentScope # NIL) ;
1075 Assert (currentScope^.scopeKind = module) ;
1076 IF currentScope^.scopeName = NulName
1080 printf1 ("seen module: %a\n", scopename)
1082 currentScope^.scopeName := scopename
1084 END EnterModuleScope ;
1088 EnterDefinitionScope - signifies to the error routines that the front end
1089 has started to compile definition module scopeName.
1092 PROCEDURE EnterDefinitionScope (scopename: Name) ;
1094 Assert (currentScope # NIL) ;
1095 Assert (currentScope^.scopeKind = definition) ;
1096 IF currentScope^.scopeName = NulName
1100 printf1 ("seen definition: %a\n", scopename)
1102 currentScope^.scopeName := scopename
1104 END EnterDefinitionScope ;
1108 EnterProcedureScope - signifies to the error routines that the front end
1109 has started to compile definition module scopeName.
1112 PROCEDURE EnterProcedureScope (scopename: Name) ;
1114 Assert (currentScope # NIL) ;
1115 Assert (currentScope^.scopeKind = procedure) ;
1116 IF currentScope^.scopeName = NulName
1120 printf1 ("seen procedure: %a\n", scopename)
1122 currentScope^.scopeName := scopename
1124 END EnterProcedureScope ;
1128 LeaveErrorScope - leave the current scope and pop into the previous one.
1131 PROCEDURE LeaveErrorScope ;
1133 currentScope := PopAddress (scopeStack)
1134 END LeaveErrorScope ;
1138 EnterErrorScope - pushes the currentScope and sets currentScope to scope.
1141 PROCEDURE EnterErrorScope (scope: ErrorScope) ;
1143 PushAddress (scopeStack, currentScope) ;
1144 currentScope := scope
1145 END EnterErrorScope ;
1149 GetCurrentErrorScope - returns currentScope.
1152 PROCEDURE GetCurrentErrorScope () : ErrorScope ;
1155 END GetCurrentErrorScope ;
1159 DepthScope - returns the depth of the scope stack.
1162 PROCEDURE DepthScope () : CARDINAL ;
1164 RETURN NoOfItemsInStackAddress (scopeStack)
1169 ResetErrorScope - should be called at the start of each pass to
1170 reset the error scope index.
1173 PROCEDURE ResetErrorScope ;
1176 END ResetErrorScope ;