home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
gnat-2.06-src.tgz
/
tar.out
/
fsf
/
gnat
/
ada
/
errout.adb
< prev
next >
Wrap
Text File
|
1996-09-28
|
64KB
|
2,030 lines
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- E R R O U T --
-- --
-- B o d y --
-- --
-- $Revision: 1.117 $ --
-- --
-- Copyright (c) 1992,1993,1994,1995 NYU, All Rights Reserved --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 2, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
-- for more details. You should have received a copy of the GNU General --
-- Public License distributed with GNAT; see file COPYING. If not, write --
-- to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. --
-- --
------------------------------------------------------------------------------
with Atree; use Atree;
with Casing; use Casing;
with Csets; use Csets;
with Debug; use Debug;
with Einfo; use Einfo;
with Expander; use Expander;
with Lib; use Lib;
with Namet; use Namet;
with Opt; use Opt;
with Output; use Output;
with Scans; use Scans;
with Sem_Util; use Sem_Util;
with Sinput; use Sinput;
with Sinfo; use Sinfo;
with Snames; use Snames;
with Stand; use Stand;
with Uintp; use Uintp;
with Uname; use Uname;
with System.Parameters;
package body Errout is
Max_Msg_Length : constant := 80 + 2 * System.Parameters.Max_Line_Length;
-- Maximum length of error message. The addition of Max_Line_Length
-- ensures that two insertion tokens of maximum length can be accomodated.
Msg_Buffer : String (1 .. Max_Msg_Length);
-- Buffer used to prepare error messages
Msg_Len : Integer;
-- Number of characters currently stored in the message buffer
Flag_Source : Source_File_Index;
-- Source file index for source file where error is being posted
Is_Warning_Msg : Boolean;
-- Set by Set_Msg_Text to indicate if current message is warning message
Is_Unconditional_Msg : Boolean;
-- Set by Set_Msg_Text to indicate if current message is unconditional
Cur_Msg : Error_Msg_Id;
-- Id of most recently posted error message
Current_Error_Source_File : Source_File_Index;
-- Id of current messages. Used to post file name when unit changes. This
-- is initialized to Main_Source at the start of a compilation, which means
-- that no file names will be output unless there are errors in units
-- other than the main unit.
Manual_Quote_Mode : Boolean;
-- Set True in manual quotation mode
List_Pragmas_Index : Int;
-- Index into List_Pragmas table
List_Pragmas_Mode : Boolean;
-- Starts True, gets set False by pragma List (Off), True by List (On)
Suppress_Message : Boolean;
-- A flag used to suppress certain obviously redundant messages (i.e.
-- those referring to a node whose type is Any_Type). This suppression
-- is effective only if All_Errors_Mode is off.
Kill_Message : Boolean;
-- A flag used to kill weird messages (e.g. those containing uninterpreted
-- implicit type references) if we have already seen at least one message
-- already. The idea is that we hope the weird message is a junk cascaded
-- message that should be suppressed.
Error_Msg_Loc_Output : Source_Ptr;
-- This is set by Error_Msg_Internal to indicate if a # insertion was
-- processed. If so, it is set to the output location, if not it is set
-- to No_Location. This is used in Error_Msg to handle the case where
-- the reference is to an instantiation.
-----------------------------------
-- Error Message Data Structures --
-----------------------------------
-- The error messages are stored as a linked list of error message objects
-- sorted into ascending order by the source location (Sloc). Each object
-- records the text of the message and its source location.
-- The following record type and table are used to represent error
-- messages, with one entry in the table being allocated for each message.
type Error_Msg_Object is record
Text : Name_Id; -- Text of error message
Next : Error_Msg_Id; -- Pointer to next message
Sfile : Source_File_Index; -- Source table index of source file
Sptr : Source_Ptr; -- Flag pointer
Line : Logical_Line_Number; -- Line number
Col : Column_Number; -- Column number
Warn : Boolean; -- True if warning message
Uncond : Boolean; -- True if unconditional message
end record;
package Errors is new Table (
Table_Component_Type => Error_Msg_Object,
Table_Index_Type => Error_Msg_Id,
Table_Low_Bound => 1,
Table_Initial => 200,
Table_Increment => 200,
Table_Name => "Error");
Error_Msgs : Error_Msg_Id;
-- The list of error messages
-----------------------
-- Local Subprograms --
-----------------------
procedure Debug_Output (N : Node_Id);
-- Called from Error_Msg_N and Error_Msg_NE to generate line of debug
-- output giving node number (of node N) if the debug X switch is set.
procedure Error_Msg_Internal (Msg : String; Flag_Location : Source_Ptr);
-- This is like Error_Msg, except that Flag_Location is known not to be
-- within a generic instance. The outer level routine, Error_Msg takes
-- care of dealing with the generic instance cases. Error_Msg_Internal
-- also leaves Error_Msg_Loc_Output set to indicate if a # insertion
-- was processed. If so, it is set to the location output, if not it
-- is set to No_Location.
function OK_Node (N : Node_Id) return Boolean;
-- Determines if a node is an OK node to place an error message on (return
-- True) or if the error message should be suppressed (return False). A
-- message is suppressed if the node already has an error posted on it,
-- or if it refers to an Etype that has an error posted on it, or if
-- it references an Entity that has an error posted on it.
procedure Output_Error_Msgs (E : in out Error_Msg_Id);
-- Output source line, error flag, and text of stored error message and
-- all subsequent messages for the same line and unit. On return E is
-- set to be one higher than the last message output.
procedure Output_Line_Number (L : Logical_Line_Number);
-- Output a line number as six digits (with leading zeroes suppressed),
-- followed by a period and a blank (note that this is 8 characters which
-- means that tabs in the source line will not get messed up).
procedure Output_Msg_Text (E : Error_Msg_Id);
-- Outputs characters of text in the text of the error message E, excluding
-- any final exclamation point. Note that no end of line is output, the
-- caller is responsible for adding the end of line.
procedure Output_Source_Line
(L : Logical_Line_Number;
Sfile : Source_File_Index;
Errs : Boolean);
-- Outputs text of source line L, in file S, together with preceding line
-- number, as described above for Output_Line_Number. The Errs parameter
-- indicates if there are errors attached to the line, which forces
-- listing on, even in the presence of pragma List (Off).
procedure Set_Msg_Blank;
-- Sets a single blank in the message if the preceding character is a
-- non-blank character other than a left parenthesis.
procedure Set_Msg_Blank_Conditional;
-- Sets a single blank in the message if the preceding character is a
-- non-blank character other than a left parenthesis or quote.
procedure Set_Msg_Char (C : Character);
-- Add a single character to the current message. This routine does not
-- check for special insertion characters (they are just treated as text
-- characters if they occur).
procedure Set_Msg_Insertion_Column;
-- Handle column number insertion (@ insertion character)
procedure Set_Msg_Insertion_Name;
-- Handle name insertion (% insertion character)
procedure Set_Msg_Insertion_Line_Number (Loc, Flag : Source_Ptr);
-- Handle line number insertion (# insertion character). Loc is the
-- location to be referenced, and Flag is the location at which the
-- flag is posted (used to determine whether to add "in file xxx")
procedure Set_Msg_Insertion_Node;
-- Handle node (name from node) insertion (& insertion character)
procedure Set_Msg_Insertion_Reserved_Name;
-- Handle insertion of reserved word name (* insertion character).
procedure Set_Msg_Insertion_Reserved_Word
(Text : String;
J : in out Integer);
-- Handle reserved word insertion (upper case letters). The Text argument
-- is the current error message input text, and J is an index which on
-- entry points to the first character of the reserved word, and on exit
-- points past the last character of the reserved word.
procedure Set_Msg_Insertion_Type_Reference (Flag : Source_Ptr);
-- Handle type reference (right brace insertion character). Flag is the
-- location of the flag, which is provided for the internal call to
-- Set_Msg_Insertion_Line_Number,
procedure Set_Msg_Insertion_Uint;
-- Handle Uint insertion (^ insertion character)
procedure Set_Msg_Insertion_Unit_Name;
-- Handle unit name insertion ($ insertion character)
procedure Set_Msg_Insertion_File_Name;
-- Handle file name insertion (left brace insertion character)
procedure Set_Msg_Int (Line : Int);
-- Set the decimal representation of the argument in the error message
-- buffer with no leading zeroes output.
procedure Set_Msg_Name_Buffer;
-- Output name from Name_Buffer, with surrounding quotes unless manual
-- quotation mode is in effect.
procedure Set_Msg_Node (Node : Node_Id);
-- Add the sequence of characters for the name associated with the
-- given node to the current message.
procedure Set_Msg_Quote;
-- Set quote if in normal quote mode, nothing if in manual quote mode
procedure Set_Msg_Str (Text : String);
-- Add a sequence of characters to the current message. This routine does
-- not check for special insertion characters (they are just treated as
-- text characters if they occur).
procedure Set_Msg_Text (Text : String; Flag : Source_Ptr);
-- Add a sequence of characters to the current message. The characters may
-- be one of the special insertion characters (see documentation in spec).
-- Flag is the location at which the error is to be posted, which is used
-- to determine whether or not the # insertion needs a file name. The
-- variables Msg_Buffer, Msg_Len, Is_Warning_Msg, and Is_Unconditional_Msg
-- are set on return.
-----------------------
-- Change_Error_Text --
-----------------------
procedure Change_Error_Text (Error_Id : Error_Msg_Id; New_Msg : String) is
Save_Next : Error_Msg_Id;
Err_Id : Error_Msg_Id := Error_Id;
begin
Set_Msg_Text (New_Msg, Errors.Table (Error_Id).Sptr);
Name_Len := Msg_Len;
Name_Buffer (1 .. Name_Len) := Msg_Buffer (1 .. Msg_Len);
Errors.Table (Error_Id).Text := Name_Find;
-- If in immediate error message mode, output modified error message now
-- This is just a bit tricky, because we want to output just a single
-- message, and the messages we modified is already linked in. We solve
-- this by temporarily resetting its forward pointer to empty.
if Immediate_Errors then
Save_Next := Errors.Table (Error_Id).Next;
Errors.Table (Error_Id).Next := No_Error_Msg;
Write_Eol;
Output_Source_Line
(Errors.Table (Error_Id).Line, Errors.Table (Error_Id).Sfile, True);
Output_Error_Msgs (Err_Id);
Errors.Table (Error_Id).Next := Save_Next;
end if;
end Change_Error_Text;
------------------
-- Debug_Output --
------------------
procedure Debug_Output (N : Node_Id) is
begin
if Debug_Flag_1 then
Write_Str ("*** following error message posted on node id = #");
Write_Int (Int (N));
Write_Str (" ***");
Write_Eol;
end if;
end Debug_Output;
---------------
-- Error_Msg --
---------------
-- Error_Msg is the same as Error_Msg_Internal for the cases where no
-- generic instantiations occur. There are two special cases in which
-- instantiations are relevant to error messages.
-- First, if an error is posted within an instance, then this is a
-- generic contract violation, and we want the error message to point
-- to the original instantiation.
-- Second, if the error message contains a reference to an instantiation,
-- then we want to point to the instantiation as well as the location in
-- the template being referenced.
procedure Error_Msg (Msg : String; Flag_Location : Source_Ptr) is
X : Source_File_Index;
Actual_Error_Loc : Source_Ptr;
-- Location of outer level instantiation in instantiation case, or
-- just a copy of Flag_Location in the normal case. This is the
-- location where all error messages will actually be posted.
Save_Error_Msg_Sloc : Source_Ptr;
-- Save Error_Msg_Sloc in instantiation case
begin
-- It is a fatal error to issue an error message when scanning from
-- the internal source buffer (see Sinput for further documentation)
pragma Assert (Source /= Internal_Source_Ptr);
-- Otherwise continue with error message processing
X := Get_Source_File_Index (Flag_Location);
-- If not an instance then just output the message at current location
if Instantiation (X) = No_Location then
Actual_Error_Loc := Flag_Location;
-- If we are trying to flag an error in an instantiation, we have
-- a generic contract violation. What we want to generate in this
-- case is:
-- instantiation requirements not met, detected at ...
-- original error message
-- All these messages are posted at the location of the top level
-- instantiation. If there are nested instantiations, then the
-- initial error message can be repeated. We first save the value
-- of Error_Msg_Sloc, in case it is used in the original error text.
else
Save_Error_Msg_Sloc := Error_Msg_Sloc;
-- Now use a recursive procedure to output the instantiation messages
-- The instantiation pointers point from the inside outwards, so the
-- first message to be output (for the outer instantiation) is for
-- the deepest one when we are following Instantiation links.
declare
procedure Output_Inst_Error_Msg (Loc : Source_Ptr);
-- Output instantiation message, where Loc is the location of
-- the instantiation.
procedure Output_Inst_Error_Msg (Loc : Source_Ptr) is
Xinst : Source_File_Index;
begin
Xinst := Get_Source_File_Index (Loc);
if Instantiation (Xinst) /= No_Location then
Output_Inst_Error_Msg (Instantiation (Xinst));
else
Actual_Error_Loc := Loc;
end if;
Error_Msg_Sloc := Loc;
Error_Msg_Internal
("instantiation requirements not met, detected at #",
Actual_Error_Loc);
end Output_Inst_Error_Msg;
begin
Output_Inst_Error_Msg (Instantiation (X));
end;
Error_Msg_Sloc := Save_Error_Msg_Sloc;
end if;
Error_Msg_Internal (Msg, Actual_Error_Loc);
-- Now we have output the message, together with instantiation
-- references if the message was placed within the instantiation.
-- Now we must deal with the case where the message contained a
-- reference to an instantiation. In this case we will append
-- messages of the form:
-- instantiated at #
-- showing the instantiation locations. In the case of nested
-- instantiations, more than one such message will be output.
if Error_Msg_Loc_Output /= No_Location then
declare
Xind : Source_File_Index;
begin
Error_Msg_Sloc := Error_Msg_Loc_Output;
loop
Xind := Get_Source_File_Index (Error_Msg_Sloc);
Error_Msg_Sloc := Instantiation (Xind);
exit when Error_Msg_Sloc = No_Location;
Error_Msg_Internal ("instantiated at #", Actual_Error_Loc);
end loop;
end;
end if;
end Error_Msg;
------------------------
-- Error_Msg_Internal --
------------------------
procedure Error_Msg_Internal (Msg : String; Flag_Location : Source_Ptr) is
Next_Msg : Error_Msg_Id;
-- Pointer to next message at insertion point
Prev_Msg : Error_Msg_Id;
-- Pointer to previous message at insertion point
Temp_Msg : Error_Msg_Id;
begin
Suppress_Message := False;
Kill_Message := False;
Error_Msg_Loc_Output := No_Location;
Set_Msg_Text (Msg, Flag_Location);
-- Return without doing anything if message is suppressed
if Suppress_Message
and not All_Errors_Mode
and not (Msg (Msg'Last) = '!')
then
return;
end if;
-- Return without doing anything if message is killed and this
-- is not the first error message. The philosophy is that if we
-- get a weird error message and we already have had a message,
-- then we hope the weird message is a junk cascaded message
if Kill_Message
and then not All_Errors_Mode
and then Errors_Detected /= 0
then
return;
end if;
-- Immediate return if warning message and warnings are suppressed
if Is_Warning_Msg and then Warning_Mode = Suppress then
Cur_Msg := No_Error_Msg;
return;
end if;
-- Otherwise build error message object for new message
Name_Buffer (1 .. Msg_Len) := Msg_Buffer (1 .. Msg_Len);
Name_Len := Msg_Len;
Errors.Increment_Last;
Cur_Msg := Errors.Last;
Errors.Table (Cur_Msg).Text := Name_Find;
Errors.Table (Cur_Msg).Next := No_Error_Msg;
Errors.Table (Cur_Msg).Sptr := Flag_Location;
Errors.Table (Cur_Msg).Sfile := Get_Source_File_Index (Flag_Location);
Errors.Table (Cur_Msg).Line := Get_Line_Number (Flag_Location);
Errors.Table (Cur_Msg).Col := Get_Column_Number (Flag_Location);
Errors.Table (Cur_Msg).Warn := Is_Warning_Msg;
Errors.Table (Cur_Msg).Uncond := Is_Unconditional_Msg;
-- If immediate errors mode set, output error message now. Also output
-- now if the -d1 debug flag is set (so node number message comes out
-- just before actual error message)
if Immediate_Errors or else Debug_Flag_1 then
Write_Eol;
Output_Source_Line (Errors.Table (Cur_Msg).Line,
Errors.Table (Cur_Msg).Sfile, True);
Temp_Msg := Cur_Msg;
Output_Error_Msgs (Temp_Msg);
-- If not in immediate errors mode, then we insert the message in the
-- error chain for later output by Finalize_Error_Output. The messages
-- are sorted first by unit (main unit comes first), and within a unit
-- by source location (earlier flag location first in the chain).
else
Prev_Msg := No_Error_Msg;
Next_Msg := Error_Msgs;
while Next_Msg /= No_Error_Msg loop
exit when
Errors.Table (Cur_Msg).Sfile < Errors.Table (Next_Msg).Sfile;
if Errors.Table (Cur_Msg).Sfile =
Errors.Table (Next_Msg).Sfile
then
exit when Flag_Location < Errors.Table (Next_Msg).Sptr;
end if;
Prev_Msg := Next_Msg;
Next_Msg := Errors.Table (Next_Msg).Next;
end loop;
-- The possible insertion point for the new message is after Prev_Msg
-- and before Next_Msg. However, there are some cases in which we do
-- not insert the message on the grounds that it is redundant with
-- respect to the previous message. We only consider deleting the
-- message if it is for the same line and unit as the previous one.
if Prev_Msg /= No_Error_Msg
and then Errors.Table (Prev_Msg).Line =
Errors.Table (Cur_Msg).Line
and then Errors.Table (Prev_Msg).Sfile =
Errors.Table (Cur_Msg).Sfile
then
-- Delete a complete duplicate message (i.e. same error text
-- at same position). Such duplicate messages are typically
-- lexical messages from tokens that are rescanned. Note that
-- such complete duplicates are deleted even if All_Errors
-- mode is set on, since they can't possibly give any useful
-- information under any circumstances.
if Errors.Table (Prev_Msg).Col = Errors.Table (Cur_Msg).Col
and then
Errors.Table (Prev_Msg).Text = Errors.Table (Cur_Msg).Text
then
return;
end if;
-- Remaining case is where we are parsing and we are not in
-- all errors mode (in semantics, don't delete any messages)
if not All_Errors_Mode and then Compiler_State = Parsing then
-- Don't delete unconditional messages
if not Errors.Table (Cur_Msg).Uncond then
-- Don't delete if prev msg is warning and new msg is
-- an error. This is because we don't want a real error
-- masked by a warning. In all other cases (that is parse
-- errors for the same line that are not unconditional)
-- we do delete the message. This helps to avoid
-- junk extra messages from cascaded parsing errors
if not Errors.Table (Prev_Msg).Warn
or else Errors.Table (Cur_Msg).Warn
then
-- All tests passed, delete the message by simply
-- returning without any further processing.
return;
end if;
end if;
end if;
end if;
-- Come here if message is to be inserted in the error chain
if Prev_Msg = No_Error_Msg then
Error_Msgs := Cur_Msg;
else
Errors.Table (Prev_Msg).Next := Cur_Msg;
end if;
Errors.Table (Cur_Msg).Next := Next_Msg;
end if;
-- Bump appropriate statistics count
if Errors.Table (Cur_Msg).Warn
and then Warning_Mode /= Treat_As_Error
then
Warnings_Detected := Warnings_Detected + 1;
else
if Error_Monitoring_On then
Monitored_Errors := Monitored_Errors + 1;
Warnings_Detected := Warnings_Detected + 1;
Errors.Table (Cur_Msg).Warn := True;
if Monitored_Message /= No_Name then
if Monitored_Message = Errors.Table (Cur_Msg).Text then
Monitored_Message := No_Name;
else
Monitored_Message := Error_Name;
end if;
end if;
else
Errors_Detected := Errors_Detected + 1;
end if;
-- Turn off code generation if not done already
if Operating_Mode = Generate_Code then
Operating_Mode := Check_Semantics;
Expander_Active := False;
end if;
-- Set the fatal error flag in the unit table unless we are
-- in Try_Semantics mode. This stops the semantics from
if not Try_Semantics then
Set_Fatal_Error (Get_Sloc_Unit_Number (Flag_Location));
end if;
end if;
-- Terminate if max errors reached
if Errors_Detected = Maximum_Errors then
raise Unrecoverable_Error;
end if;
end Error_Msg_Internal;
-----------------
-- Error_Msg_S --
-----------------
procedure Error_Msg_S (Msg : String) is
begin
Error_Msg (Msg, Scan_Ptr);
end Error_Msg_S;
------------------
-- Error_Msg_AP --
------------------
procedure Error_Msg_AP (Msg : String) is
S1 : Source_Ptr;
C : Character;
begin
-- If we had saved the Scan_Ptr value after scanning the previous
-- token, then we would have exactly the right place for putting
-- the flag immediately at hand. However, that would add at least
-- two instructions to a Scan call *just* to service the possibility
-- of an Error_Msg_AP call. So instead we reconstruct that value.
-- We have two possibilities, start with Prev_Token_Ptr and skip over
-- the current token, which is made harder by the possibility that this
-- token may be in error, or start with Token_Ptr and work backwards.
-- We used to take the second approach, but it's hard because of
-- comments, and harder still because things that look like comments
-- can appear inside strings. So now we take the first approach.
-- Note: in the case where there is no previous token, Prev_Token_Ptr
-- is set to Source_First, which is a reasonable position for the
-- error flag in this situation.
S1 := Prev_Token_Ptr;
C := Source (S1);
-- If the previous token is a string literal, we need a special approach
-- since there may be white space inside the literal and we don't want
-- to stop on that white space.
if Prev_Token = Tok_String_Literal then
loop
S1 := S1 + 1;
if Source (S1) = C then
S1 := S1 + 1;
exit when Source (S1) /= C;
elsif Source (S1) in Line_Terminator then
exit;
end if;
end loop;
-- Character literal also needs special handling
elsif Prev_Token = Tok_Char_Literal then
S1 := S1 + 3;
-- Otherwise we search forward for the end of the current token, marked
-- by a line terminator, white space, a comment symbol or if we bump
-- into the following token (i.e. the current token)
else
while Source (S1) not in Line_Terminator
and then Source (S1) /= ' '
and then Source (S1) /= Ascii.HT
and then (Source (S1) /= '-' or else Source (S1 + 1) /= '-')
and then S1 /= Token_Ptr
loop
S1 := S1 + 1;
end loop;
end if;
-- S1 is now set to the location for the flag
Error_Msg (Msg, S1);
end Error_Msg_AP;
------------------
-- Error_Msg_BC --
------------------
procedure Error_Msg_BC (Msg : String) is
begin
-- If we are at end of file, post the flag after the previous token
if Token = Tok_EOF then
Error_Msg_AP (Msg);
-- If we are at start of file, post the flag at the current token
elsif Token_Ptr = Source_First (Current_Source_File) then
Error_Msg_SC (Msg);
-- If the character before the current token is a space or a horizontal
-- tab, then we place the flag on this character (in the case of a tab
-- we would really like to place it in the "last" character of the tab
-- space, but that it too much trouble to worry about).
elsif Source (Token_Ptr - 1) = ' '
or else Source (Token_Ptr - 1) = Ascii.HT
then
Error_Msg (Msg, Token_Ptr - 1);
-- If there is no space or tab before the current token, then there is
-- no room to place the flag before the token, so we place it on the
-- token instead (this happens for example at the start of a line).
else
Error_Msg (Msg, Token_Ptr);
end if;
end Error_Msg_BC;
------------------
-- Error_Msg_SC --
------------------
procedure Error_Msg_SC (Msg : String) is
begin
-- If we are at end of file, post the flag after the previous token
if Token = Tok_EOF then
Error_Msg_AP (Msg);
-- For all other cases the message is posted at the current token
-- pointer position
else
Error_Msg (Msg, Token_Ptr);
end if;
end Error_Msg_SC;
------------------
-- Error_Msg_SP --
------------------
procedure Error_Msg_SP (Msg : String) is
begin
-- Note: in the case where there is no previous token, Prev_Token_Ptr
-- is set to Source_First, which is a reasonable position for the
-- error flag in this situation
Error_Msg (Msg, Prev_Token_Ptr);
end Error_Msg_SP;
-----------------
-- Error_Msg_N --
-----------------
procedure Error_Msg_N (Msg : String; N : Node_Id) is
begin
if All_Errors_Mode
or else Msg (Msg'Last) = '!'
or else OK_Node (N)
then
Debug_Output (N);
Error_Msg_Node_1 := N;
Error_Msg (Msg, Sloc (N));
end if;
if not Is_Warning_Msg then
Set_Error_Posted (N, True);
end if;
end Error_Msg_N;
------------------
-- Error_Msg_NE --
------------------
procedure Error_Msg_NE (Msg : String; N : Node_Id; E : Entity_Id) is
begin
if All_Errors_Mode
or else Msg (Msg'Last) = '!'
or else OK_Node (N)
then
Debug_Output (N);
Error_Msg_Node_1 := E;
Error_Msg (Msg, Sloc (N));
end if;
if not Is_Warning_Msg then
Set_Error_Posted (N, True);
end if;
end Error_Msg_NE;
--------------
-- Finalize --
--------------
procedure Finalize is
E : Error_Msg_Id;
Err_Flag : Boolean;
L : Logical_Line_Number;
begin
-- Brief Error mode
if Brief_Output or (not Full_List and not Verbose_Mode) then
E := Error_Msgs;
Set_Standard_Error;
while E /= No_Error_Msg loop
Write_Name (Reference_Name (Errors.Table (E).Sfile));
Write_Char (':');
Write_Int (Int (Errors.Table (E).Line));
Write_Char (':');
if Errors.Table (E).Col < 10 then
Write_Char ('0');
end if;
Write_Int (Int (Errors.Table (E).Col));
Write_Str (": ");
Output_Msg_Text (E);
Write_Eol;
E := Errors.Table (E).Next;
end loop;
Set_Standard_Output;
end if;
-- Full source listing case
if Full_List then
List_Pragmas_Index := 1;
List_Pragmas_Mode := True;
E := Error_Msgs;
Write_Eol;
-- First list initial main source file with its error messages
for N in 1 .. Num_Source_Lines (Main_Source) loop
L := Physical_To_Logical (N, Main_Source);
Err_Flag :=
E /= No_Error_Msg
and then Errors.Table (E).Line = L
and then Errors.Table (E).Sfile = Main_Source;
Output_Source_Line (L, Main_Source, Err_Flag);
if Err_Flag then
Output_Error_Msgs (E);
if not Debug_Flag_2 then
Write_Eol;
end if;
end if;
end loop;
-- Then output errors, if any, for subsidiary units
while E /= No_Error_Msg
and then Errors.Table (E).Sfile /= Main_Source
loop
Write_Eol;
Output_Source_Line
(Errors.Table (E).Line, Errors.Table (E).Sfile, True);
Output_Error_Msgs (E);
end loop;
end if;
-- Verbose mode (error lines only with error flags)
if Verbose_Mode and not Full_List then
E := Error_Msgs;
-- Loop through error lines
while E /= No_Error_Msg loop
Write_Eol;
Output_Source_Line
(Errors.Table (E).Line, Errors.Table (E).Sfile, True);
Output_Error_Msgs (E);
end loop;
end if;
-- Output error summary if verbose or full list mode
if Verbose_Mode or else Full_List then
-- Extra blank line if error messages or source listing were output
if Errors_Detected + Warnings_Detected > 0 or else Full_List then
Write_Eol;
end if;
-- Message giving total number of lines
Write_Str (" ");
Write_Int (Num_Source_Lines (Main_Source));
if Num_Source_Lines (Main_Source) = 1 then
Write_Str (" line: ");
else
Write_Str (" lines: ");
end if;
-- Message giving number of errors detected. This normally goes to
-- Standard_Output. The exception is when brief mode is not set,
-- verbose mode (or full list mode) is set, and there are errors.
-- In this case we send the message to standard error to make sure
-- that *something* appears on standard error in an error situation.
if Errors_Detected + Warnings_Detected /= 0
and then not Brief_Output
and then (Verbose_Mode or Full_List)
then
Set_Standard_Error;
end if;
if Errors_Detected = 0 then
Write_Str ("No errors");
elsif Errors_Detected = 1 then
Write_Str ("1 error");
else
Write_Int (Errors_Detected);
Write_Str (" errors");
end if;
if Warnings_Detected = 1 then
Write_Str (", 1 warning");
elsif Warnings_Detected > 1 then
Write_Str (", ");
Write_Int (Warnings_Detected);
Write_Str (" warnings");
end if;
Write_Eol;
Set_Standard_Output;
end if;
if Maximum_Errors /= 0
and then Errors_Detected = Maximum_Errors
then
Set_Standard_Error;
Write_Str ("fatal error: maximum errors reached");
Write_Eol;
Set_Standard_Output;
end if;
end Finalize;
----------------
-- Get_Msg_Id --
----------------
function Get_Msg_Id return Error_Msg_Id is
begin
return Cur_Msg;
end Get_Msg_Id;
----------------
-- Initialize --
----------------
procedure Initialize is
begin
Errors.Init;
Error_Msgs := No_Error_Msg;
Errors_Detected := 0;
Warnings_Detected := 0;
Error_Monitoring_On := False;
Cur_Msg := No_Error_Msg;
Current_Error_Source_File := Main_Source;
List_Pragmas.Init;
end Initialize;
-------------
-- OK_Node --
-------------
function OK_Node (N : Node_Id) return Boolean is
K : constant Node_Kind := Nkind (N);
begin
if Error_Posted (N) then
return False;
elsif K in N_Has_Etype
and then Present (Etype (N))
and then Error_Posted (Etype (N))
then
return False;
elsif (K in N_Op
or else K = N_Attribute_Reference
or else K = N_Character_Literal
or else K = N_Expanded_Name
or else K = N_Identifier
or else K = N_Operator_Symbol)
and then Present (Entity (N))
and then Error_Posted (Entity (N))
then
return False;
else
return True;
end if;
end OK_Node;
-----------------------
-- Output_Error_Msgs --
-----------------------
procedure Output_Error_Msgs (E : in out Error_Msg_Id) is
P : Source_Ptr;
T : Error_Msg_Id;
Flag_Num : Pos;
Mult_Flags : Boolean := False;
begin
-- Figure out if we will place more than one error flag on this line
T := E;
while T /= No_Error_Msg
and then Errors.Table (T).Line = Errors.Table (E).Line
and then Errors.Table (T).Sfile = Errors.Table (E).Sfile
loop
if Errors.Table (T).Sptr > Errors.Table (E).Sptr then
Mult_Flags := True;
end if;
T := Errors.Table (T).Next;
end loop;
-- Output the error flags. The circuit here makes sure that the tab
-- characters in the original line are properly accounted for. The
-- eight blanks at the start are to match the line number.
if not Debug_Flag_2 then
Write_Str (" ");
P := Line_Start (Errors.Table (E).Sptr);
Flag_Num := 1;
-- Loop through error messages for this line to place flags
T := E;
while T /= No_Error_Msg
and then Errors.Table (T).Line = Errors.Table (E).Line
and then Errors.Table (T).Sfile = Errors.Table (E).Sfile
loop
-- Loop to output blanks till current flag position
while P < Errors.Table (T).Sptr loop
if Source_Text (Errors.Table (T).Sfile) (P) = Ascii.HT then
Write_Char (Ascii.HT);
else
Write_Char (' ');
end if;
P := P + 1;
end loop;
-- Output flag (unless already output, this happens if more
-- than one error message occurs at the same flag position).
if P = Errors.Table (T).Sptr then
if (Flag_Num = 1 and then not Mult_Flags)
or else Flag_Num > 9
then
Write_Char ('|');
else
Write_Char (Character'Val (Character'Pos ('0') + Flag_Num));
end if;
P := P + 1;
end if;
T := Errors.Table (T).Next;
Flag_Num := Flag_Num + 1;
end loop;
Write_Eol;
end if;
-- Now output the error messages
T := E;
while T /= No_Error_Msg
and then Errors.Table (T).Line = Errors.Table (E).Line
and then Errors.Table (T).Sfile = Errors.Table (E).Sfile
loop
Write_Str (" >>> ");
Output_Msg_Text (T);
if Debug_Flag_2 then
while Column < 74 loop
Write_Char (' ');
end loop;
Write_Str (" <<<");
end if;
Write_Eol;
T := Errors.Table (T).Next;
end loop;
E := T;
end Output_Error_Msgs;
------------------------
-- Output_Line_Number --
------------------------
procedure Output_Line_Number (L : Logical_Line_Number) is
D : Int; -- next digit
C : Character; -- next character
Z : Boolean; -- flag for zero suppress
N, M : Int; -- temporaries
begin
Z := False;
N := Int (L);
M := 100_000;
while M /= 0 loop
D := Int (N / M);
N := N rem M;
M := M / 10;
if D = 0 then
if Z then
C := '0';
else
C := ' ';
end if;
else
Z := True;
C := Character'Val (D + 48);
end if;
Write_Char (C);
end loop;
Write_Str (". ");
end Output_Line_Number;
---------------------
-- Output_Msg_Text --
---------------------
procedure Output_Msg_Text (E : Error_Msg_Id) is
begin
if Errors.Table (E).Warn then
Write_Str ("warning: ");
elsif System.Parameters.Tag_Errors then
Write_Str ("error: ");
end if;
Write_Name (Errors.Table (E).Text);
end Output_Msg_Text;
------------------------
-- Output_Source_Line --
------------------------
procedure Output_Source_Line
(L : Logical_Line_Number;
Sfile : Source_File_Index;
Errs : Boolean)
is
S : Source_Ptr;
C : Character;
Line_Number_Output : Boolean := False;
-- Set True once line number is output
begin
if Sfile /= Current_Error_Source_File then
Write_Str ("==============Error messages for source file: ");
Write_Name (Reference_Name (Sfile));
Write_Eol;
Current_Error_Source_File := Sfile;
end if;
if Errs or List_Pragmas_Mode then
Output_Line_Number (L);
Line_Number_Output := True;
end if;
S := Line_Start (L, Sfile);
loop
C := Source_Text (Sfile) (S);
exit when C = Ascii.LF or else C = Ascii.CR or else C = EOF;
-- Deal with matching entry in List_Pragmas table
if Full_List
and then List_Pragmas_Index <= List_Pragmas.Last
and then S = List_Pragmas.Table (List_Pragmas_Index).Ploc
then
case List_Pragmas.Table (List_Pragmas_Index).Ptyp is
when Page =>
Write_Char (C);
-- Ignore if on line with errors so that error flags
-- get properly listed with the error line .
if not Errs then
Write_Char (Ascii.FF);
end if;
when List_On =>
List_Pragmas_Mode := True;
if not Line_Number_Output then
Output_Line_Number (L);
Line_Number_Output := True;
end if;
Write_Char (C);
when List_Off =>
Write_Char (C);
List_Pragmas_Mode := False;
end case;
List_Pragmas_Index := List_Pragmas_Index + 1;
-- Normal case (no matching entry in List_Pragmas table)
else
if Errs or List_Pragmas_Mode then
Write_Char (C);
end if;
end if;
S := S + 1;
end loop;
if Line_Number_Output then
Write_Eol;
end if;
end Output_Source_Line;
-------------------
-- Set_Msg_Blank --
-------------------
procedure Set_Msg_Blank is
begin
if Msg_Len > 0
and then Msg_Buffer (Msg_Len) /= ' '
and then Msg_Buffer (Msg_Len) /= '('
then
Set_Msg_Char (' ');
end if;
end Set_Msg_Blank;
-------------------------------
-- Set_Msg_Blank_Conditional --
-------------------------------
procedure Set_Msg_Blank_Conditional is
begin
if Msg_Len > 0
and then Msg_Buffer (Msg_Len) /= ' '
and then Msg_Buffer (Msg_Len) /= '('
and then Msg_Buffer (Msg_Len) /= '"'
then
Set_Msg_Char (' ');
end if;
end Set_Msg_Blank_Conditional;
------------------
-- Set_Msg_Char --
------------------
procedure Set_Msg_Char (C : Character) is
begin
-- The check for message buffer overflow is needed to deal with cases
-- where insertions get too long (in particular a child unit name can
-- be very long).
if Msg_Len < Max_Msg_Length then
Msg_Len := Msg_Len + 1;
Msg_Buffer (Msg_Len) := C;
end if;
end Set_Msg_Char;
------------------------------
-- Set_Msg_Insertion_Column --
------------------------------
procedure Set_Msg_Insertion_Column is
begin
if RM_Column_Check then
Set_Msg_Str (" in column ");
Set_Msg_Int (Int (Error_Msg_Col) + 1);
end if;
end Set_Msg_Insertion_Column;
---------------------------------
-- Set_Msg_Insertion_File_Name --
---------------------------------
procedure Set_Msg_Insertion_File_Name is
begin
if Error_Msg_Name_1 = No_Name then
null;
elsif Error_Msg_Name_1 = Error_Name then
Set_Msg_Blank;
Set_Msg_Str ("<error>");
else
Set_Msg_Blank;
Get_Decoded_Name_String (Error_Msg_Name_1);
Set_Msg_Quote;
Set_Msg_Name_Buffer;
Set_Msg_Quote;
end if;
-- The following assignments ensure that the second and third percent
-- insertion characters will correspond to the Error_Msg_Name_2 and
-- Error_Msg_Name_3 as required.
Error_Msg_Name_1 := Error_Msg_Name_2;
Error_Msg_Name_2 := Error_Msg_Name_3;
end Set_Msg_Insertion_File_Name;
-----------------------------------
-- Set_Msg_Insertion_Line_Number --
-----------------------------------
procedure Set_Msg_Insertion_Line_Number (Loc, Flag : Source_Ptr) is
Sindex : Source_File_Index;
begin
Set_Msg_Blank;
if Loc = No_Location then
Set_Msg_Str ("at unknown location");
elsif Loc <= Standard_Location then
Set_Msg_Str ("in package Standard");
if Loc = Standard_Ascii_Location then
Set_Msg_Str (".Ascii");
end if;
else
Sindex := Get_Source_File_Index (Flag);
-- Add "at file-name:" if reference is to other than the source
-- file in which the error message is placed.
if Loc not in Source_First (Sindex) .. Source_Last (Sindex) then
Set_Msg_Str ("at ");
Get_Decoded_Name_String
(File_Name (Get_Source_File_Index (Loc)));
Set_Msg_Name_Buffer;
Set_Msg_Char (':');
-- If in current file, add text "at line "
else
Set_Msg_Str ("at line ");
end if;
-- Output line and column
Set_Msg_Int (Int (Get_Line_Number (Loc)));
Error_Msg_Loc_Output := No_Location;
end if;
end Set_Msg_Insertion_Line_Number;
----------------------------
-- Set_Msg_Insertion_Name --
----------------------------
procedure Set_Msg_Insertion_Name is
begin
if Error_Msg_Name_1 = No_Name then
null;
elsif Error_Msg_Name_1 = Error_Name then
Set_Msg_Blank;
Set_Msg_Str ("<error>");
else
Set_Msg_Blank_Conditional;
Get_Decoded_Name_String (Error_Msg_Name_1);
-- If operator name or character literal name, just print it as is
-- Also print as is if it ends in a right paren (case of x'val(nnn))
if Name_Buffer (1) = '"'
or else Name_Buffer (1) = '''
or else Name_Buffer (Name_Len) = ')'
then
Set_Msg_Name_Buffer;
-- Else output with surrounding quotes in proper casing mode
else
Set_Casing (Identifier_Casing (Flag_Source), Mixed_Case);
Set_Msg_Quote;
Set_Msg_Name_Buffer;
Set_Msg_Quote;
end if;
end if;
-- The following assignments ensure that the second and third percent
-- insertion characters will correspond to the Error_Msg_Name_2 and
-- Error_Msg_Name_3 as required.
Error_Msg_Name_1 := Error_Msg_Name_2;
Error_Msg_Name_2 := Error_Msg_Name_3;
end Set_Msg_Insertion_Name;
----------------------------
-- Set_Msg_Insertion_Node --
----------------------------
procedure Set_Msg_Insertion_Node is
begin
Suppress_Message :=
Error_Msg_Node_1 = Error
or else Error_Msg_Node_1 = Any_Type;
if Error_Msg_Node_1 = Empty then
Set_Msg_Blank_Conditional;
Set_Msg_Str ("<empty>");
elsif Error_Msg_Node_1 = Error then
Set_Msg_Blank;
Set_Msg_Str ("<error>");
elsif Error_Msg_Node_1 = Standard_Void_Type then
Set_Msg_Blank;
Set_Msg_Str ("procedure name");
else
Set_Msg_Blank_Conditional;
-- Skip quotes for operator case
if Nkind (Error_Msg_Node_1) in N_Op then
Set_Msg_Node (Error_Msg_Node_1);
else
Set_Msg_Quote;
Set_Msg_Node (Error_Msg_Node_1);
Set_Msg_Quote;
end if;
end if;
-- The following assignment ensures that a second ampersand insertion
-- character will correspond to the Error_Msg_Node_2 parameter.
Error_Msg_Node_1 := Error_Msg_Node_2;
end Set_Msg_Insertion_Node;
-------------------------------------
-- Set_Msg_Insertion_Reserved_Name --
-------------------------------------
procedure Set_Msg_Insertion_Reserved_Name is
begin
Set_Msg_Blank_Conditional;
Get_Name_String (Error_Msg_Name_1);
Set_Msg_Quote;
Set_Casing (Keyword_Casing (Flag_Source), All_Lower_Case);
Set_Msg_Name_Buffer;
Set_Msg_Quote;
end Set_Msg_Insertion_Reserved_Name;
-------------------------------------
-- Set_Msg_Insertion_Reserved_Word --
-------------------------------------
procedure Set_Msg_Insertion_Reserved_Word
(Text : String;
J : in out Integer)
is
begin
Set_Msg_Blank_Conditional;
Name_Len := 0;
while J <= Text'Last and then Text (J) in 'A' .. 'Z' loop
Name_Len := Name_Len + 1;
Name_Buffer (Name_Len) := Text (J);
J := J + 1;
end loop;
Set_Casing (Keyword_Casing (Flag_Source), All_Lower_Case);
Set_Msg_Quote;
Set_Msg_Name_Buffer;
Set_Msg_Quote;
end Set_Msg_Insertion_Reserved_Word;
--------------------------------------
-- Set_Msg_Insertion_Type_Reference --
--------------------------------------
procedure Set_Msg_Insertion_Type_Reference (Flag : Source_Ptr) is
begin
Set_Msg_Blank;
if Error_Msg_Node_1 = Standard_Void_Type then
Set_Msg_Str ("procedure name instead of function");
return;
elsif Error_Msg_Node_1 = Standard_Exception_Type then
Set_Msg_Str ("an exception");
return;
elsif Error_Msg_Node_1 = Any_Access
or else Error_Msg_Node_1 = Any_Boolean
or else Error_Msg_Node_1 = Any_Character
or else Error_Msg_Node_1 = Any_Composite
or else Error_Msg_Node_1 = Any_Discrete
or else Error_Msg_Node_1 = Any_Fixed
or else Error_Msg_Node_1 = Any_Integer
or else Error_Msg_Node_1 = Any_Numeric
or else Error_Msg_Node_1 = Any_Real
or else Error_Msg_Node_1 = Any_Scalar
or else Error_Msg_Node_1 = Any_String
then
Get_Decoded_Name_String (Chars (Error_Msg_Node_1));
Set_Msg_Name_Buffer;
return;
elsif Error_Msg_Node_1 = Universal_Real then
Set_Msg_Str ("type universal real");
return;
elsif Error_Msg_Node_1 = Universal_Integer then
Set_Msg_Str ("type universal integer");
return;
elsif Error_Msg_Node_1 = Universal_Fixed then
Set_Msg_Str ("type universal fixed");
return;
end if;
-- If we fall through, it is not a special case, so first output
-- the name of the type, preceded by private for a private type
if Is_Private_Type (Error_Msg_Node_1) then
Set_Msg_Str ("private type ");
else
Set_Msg_Str ("type ");
end if;
if Sloc (Error_Msg_Node_1) <= Standard_Location then
Set_Msg_Quote;
Set_Msg_Str ("Standard.");
Set_Msg_Node (Error_Msg_Node_1);
Set_Msg_Quote;
else
Set_Msg_Quote;
Set_Msg_Node (Error_Msg_Node_1);
Set_Msg_Quote;
Set_Msg_Str (" declared");
Set_Msg_Insertion_Line_Number (Sloc (Error_Msg_Node_1), Flag);
end if;
end Set_Msg_Insertion_Type_Reference;
----------------------------
-- Set_Msg_Insertion_Uint --
----------------------------
procedure Set_Msg_Insertion_Uint is
begin
Set_Msg_Blank;
UI_Image (Error_Msg_Uint_1);
for J in 1 .. UI_Image_Length loop
Set_Msg_Char (UI_Image_Buffer (J));
end loop;
-- The following assignment ensures that a second carret insertion
-- character will correspond to the Error_Msg_Uint_2 parameter.
Error_Msg_Uint_1 := Error_Msg_Uint_2;
end Set_Msg_Insertion_Uint;
---------------------------------
-- Set_Msg_Insertion_Unit_Name --
---------------------------------
procedure Set_Msg_Insertion_Unit_Name is
begin
if Error_Msg_Unit_1 = No_Name then
null;
elsif Error_Msg_Unit_1 = Error_Name then
Set_Msg_Blank;
Set_Msg_Str ("<error>");
else
Get_Unit_Name_String (Error_Msg_Unit_1);
Set_Msg_Blank;
Set_Msg_Quote;
Set_Msg_Name_Buffer;
Set_Msg_Quote;
end if;
-- The following assignment ensures that a second percent insertion
-- character will correspond to the Error_Msg_Unit_2 parameter.
Error_Msg_Unit_1 := Error_Msg_Unit_2;
end Set_Msg_Insertion_Unit_Name;
-----------------
-- Set_Msg_Int --
-----------------
procedure Set_Msg_Int (Line : Int) is
begin
if Line > 9 then
Set_Msg_Int (Line / 10);
end if;
Set_Msg_Char (Character'Val (Character'Pos ('0') + (Line rem 10)));
end Set_Msg_Int;
-------------------------
-- Set_Msg_Name_Buffer --
-------------------------
procedure Set_Msg_Name_Buffer is
begin
for J in 1 .. Name_Len loop
Set_Msg_Char (Name_Buffer (J));
end loop;
end Set_Msg_Name_Buffer;
------------------
-- Set_Msg_Node --
------------------
procedure Set_Msg_Node (Node : Node_Id) is
Ent : Entity_Id;
Old_Ent : Entity_Id;
Mchar : Character;
Derived : Boolean := False;
Class_Flag : Boolean := False;
Nam : Name_Id;
procedure Kill_Type;
-- If message buffer ends with " type ", then remove the last five
-- characters. This is used to avoid a duplication of "type" in the
-- text of the message with "type" generated by the special tests
-- below (e.g. we don't want "expected type type derived from ..")
procedure Kill_Type is
begin
if Msg_Len > 4
and then Msg_Buffer (Msg_Len - 4 .. Msg_Len) = "type "
and then (Msg_Len = 5 or else Msg_Buffer (Msg_Len - 5) = ' ')
then
Msg_Len := Msg_Len - 5;
end if;
end Kill_Type;
-- Start of processing for Set_Msg_Node
begin
if Nkind (Node) = N_Designator then
Set_Msg_Node (Name (Node));
Set_Msg_Char ('.');
Set_Msg_Node (Identifier (Node));
return;
elsif Nkind (Node) = N_Defining_Program_Unit_Name then
Set_Msg_Node (Name (Node));
Set_Msg_Char ('.');
Set_Msg_Node (Defining_Identifier (Node));
return;
elsif Nkind (Node) = N_Selected_Component then
Set_Msg_Node (Prefix (Node));
Set_Msg_Char ('.');
Set_Msg_Node (Selector_Name (Node));
return;
end if;
-- The only remaining possibilities are identifiers, defining
-- identifiers and pragmas, i.e. nodes that have a Chars field.
-- Why is this true ???
-- Internal names generally represent something gone wrong. An exception
-- is the case of internal type names, where we try to find a reasonable
-- external representation for the external name
if Is_Internal_Name (Chars (Node))
and then
((Is_Entity_Name (Node) and then Is_Type (Entity (Node)))
or else
(Nkind (Node) = N_Defining_Identifier and then Is_Type (Node)))
then
if Nkind (Node) = N_Identifier then
Ent := Entity (Node);
else
Ent := Node;
end if;
-- Undo placement of a quote, since we will put it back later
Mchar := Msg_Buffer (Msg_Len);
if Mchar = '"' then
Msg_Len := Msg_Len - 1;
end if;
-- The loop here deals with recursive types, we are trying to
-- find a related entity that is not an implicit type. Note
-- that the check with Old_Ent stops us from getting "stuck".
-- Also, we don't output the "type derived from" message more
-- than once in the case where we climb up multiple levels.
loop
Old_Ent := Ent;
-- Implicit access type, use directly designated type
if Is_Access_Type (Ent) then
Set_Msg_Str ("access to ");
Ent := Directly_Designated_Type (Ent);
-- Classwide type
elsif Is_Class_Wide_Type (Ent) then
Class_Flag := True;
Ent := Root_Type (Ent);
-- Use base type if this is a subtype
elsif Ent /= Base_Type (Ent) then
Kill_Type;
Set_Msg_Str ("subtype of ");
Ent := Base_Type (Ent);
-- If this is a base type with a first named subtype, use the
-- first named subtype instead. This is not quite accurate in
-- all cases, but it makes too much noise to be accurate and
-- add 'Base in all cases. Note that we only do this is the
-- first named subtype is not itself an internal name. This
-- avoids the obvious loop (subtype->basetype->subtype) which
-- would otherwise occur!)
elsif Present (Freeze_Node (Ent))
and then Present (First_Subtype_Link (Freeze_Node (Ent)))
and then
not Is_Internal_Name
(Chars (First_Subtype_Link (Freeze_Node (Ent))))
then
Ent := First_Subtype_Link (Freeze_Node (Ent));
-- Otherwise use root type
else
if not Derived then
Kill_Type;
Set_Msg_Str ("type derived from ");
Derived := True;
end if;
Ent := Etype (Ent);
end if;
-- If we are stuck in a loop, get out and settle for the
-- internal name after all.
exit when Ent = Old_Ent;
-- Get out if we finally found a non-internal name to use
exit when not Is_Internal_Name (Chars (Ent));
end loop;
if Mchar = '"' then
Set_Msg_Char ('"');
end if;
Nam := Chars (Ent);
-- For any other internal names, we settle for using the name
else
Nam := Chars (Node);
end if;
-- If we still have an internal name, then set to kill the message
-- if it is not the first message (we really try hard not to show
-- the dirty laundry of the implementation to the compiler user!)
if Is_Internal_Name (Nam) then
Kill_Message := True;
end if;
-- At this stage, the name to output is in Nam
Get_Decoded_Name_String (Nam);
-- Now we have to set the proper case. If we have a source location
-- then do a check to see if the name in the source is the same name
-- as the name in the Names table, except for possible differences
-- in case, which is the case when we can copy from the source.
declare
Src_Loc : constant Source_Ptr := Sloc (Error_Msg_Node_1);
Sbuffer : Source_Buffer_Ptr;
Ref_Ptr : Integer;
Src_Ptr : Source_Ptr;
begin
Ref_Ptr := 1;
Src_Ptr := Src_Loc;
-- Determine if the reference we are dealing with corresponds
-- to text at the point of the error reference. This will often
-- be the case for simple identifier references, and is the case
-- where we can copy the spelling from the source.
if Src_Loc /= No_Location
and then Src_Loc > Standard_Location
then
Sbuffer := Source_Text (Get_Source_File_Index (Src_Loc));
while Ref_Ptr <= Name_Len loop
exit when
Fold_Lower (Sbuffer (Src_Ptr)) /=
Fold_Lower (Name_Buffer (Ref_Ptr));
Ref_Ptr := Ref_Ptr + 1;
Src_Ptr := Src_Ptr + 1;
end loop;
end if;
-- If we get through the loop without a mismatch, then output
-- the name the way it is spelled in the source program
if Ref_Ptr > Name_Len then
Src_Ptr := Src_Loc;
for J in 1 .. Name_Len loop
Name_Buffer (J) := Sbuffer (Src_Ptr);
Src_Ptr := Src_Ptr + 1;
end loop;
-- Otherwise set the casing using the default identifier casing
else
Set_Casing (Identifier_Casing (Flag_Source), Mixed_Case);
end if;
end;
Set_Msg_Name_Buffer;
-- Add 'Class if class wide type
if Class_Flag then
Set_Msg_Char (''');
Get_Name_String (Name_Class);
Set_Casing (Identifier_Casing (Flag_Source), Mixed_Case);
Set_Msg_Name_Buffer;
end if;
end Set_Msg_Node;
-------------------
-- Set_Msg_Quote --
-------------------
procedure Set_Msg_Quote is
begin
if not Manual_Quote_Mode then
Set_Msg_Char ('"');
end if;
end Set_Msg_Quote;
-----------------
-- Set_Msg_Str --
-----------------
procedure Set_Msg_Str (Text : String) is
begin
for J in Text'Range loop
Set_Msg_Char (Text (J));
end loop;
end Set_Msg_Str;
------------------
-- Set_Msg_Text --
------------------
procedure Set_Msg_Text (Text : String; Flag : Source_Ptr) is
C : Character; -- Current character
I : Natural; -- Current index;
begin
Manual_Quote_Mode := False;
Is_Warning_Msg := False;
Is_Unconditional_Msg := False;
Msg_Len := 0;
Flag_Source := Get_Source_File_Index (Flag);
I := Text'First;
while I <= Text'Last loop
C := Text (I);
I := I + 1;
-- Check for insertion character
if C = '%' then
Set_Msg_Insertion_Name;
elsif C = '$' then
Set_Msg_Insertion_Unit_Name;
elsif C = '{' then
Set_Msg_Insertion_File_Name;
elsif C = '}' then
Set_Msg_Insertion_Type_Reference (Flag);
elsif C = '*' then
Set_Msg_Insertion_Reserved_Name;
elsif C = '&' then
Set_Msg_Insertion_Node;
elsif C = '#' then
Set_Msg_Insertion_Line_Number (Error_Msg_Sloc, Flag);
elsif C = '@' then
Set_Msg_Insertion_Column;
elsif C = '^' then
Set_Msg_Insertion_Uint;
elsif C = '`' then
Manual_Quote_Mode := not Manual_Quote_Mode;
Set_Msg_Char ('"');
elsif C = '!' then
Is_Unconditional_Msg := True;
elsif C = '?' then
Is_Warning_Msg := True;
elsif C = ''' then
Set_Msg_Char (Text (I));
I := I + 1;
-- Upper case letter (start of reserved word if 2 or more)
elsif C in 'A' .. 'Z'
and then I <= Text'Last
and then Text (I) in 'A' .. 'Z'
then
I := I - 1;
Set_Msg_Insertion_Reserved_Word (Text, I);
-- Normal character with no special treatment
else
Set_Msg_Char (C);
end if;
end loop;
end Set_Msg_Text;
---------------------
-- Temporary_Msg_N --
---------------------
procedure Temporary_Msg_N (Msg : String; N : Node_Id) renames Error_Msg_N;
end Errout;