-------------------------------------------------------------------------------
-- (C) Altran Praxis Limited
-------------------------------------------------------------------------------
--
-- The SPARK toolset is free software; you can redistribute it and/or modify it
-- under terms of the GNU General Public License as published by the Free
-- Software Foundation; either version 3, or (at your option) any later
-- version. The SPARK toolset is distributed in the hope that it will be
-- useful, but WITHOUT 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 the SPARK toolset; see file
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
-- the license.
--
--=============================================================================

with SLI;

separate (Sem)
procedure Wf_Justification_Statement
  (Node           : in     STree.SyntaxNode;
   Scope          : in     Dictionary.Scopes;
   Component_Data : in out ComponentManager.ComponentData;
   The_Heap       : in out Heap.HeapRecord)
is
   Next_Node : STree.SyntaxNode;

   --------------------------------------------------------------------------------

   procedure Wf_Start_Justification
     (Node           : in     STree.SyntaxNode;
      Scope          : in     Dictionary.Scopes;
      Component_Data : in out ComponentManager.ComponentData;
      The_Heap       : in out Heap.HeapRecord)
   --# global in     CommandLineData.Content;
   --#        in     ContextManager.Ops.Unit_Stack;
   --#        in     LexTokenManager.State;
   --#        in out Dictionary.Dict;
   --#        in out ErrorHandler.Error_Context;
   --#        in out SPARK_IO.File_Sys;
   --#        in out Statistics.TableUsage;
   --#        in out STree.Table;
   --# derives Component_Data,
   --#         Dictionary.Dict,
   --#         Statistics.TableUsage,
   --#         STree.Table,
   --#         The_Heap                   from *,
   --#                                         CommandLineData.Content,
   --#                                         Component_Data,
   --#                                         ContextManager.Ops.Unit_Stack,
   --#                                         Dictionary.Dict,
   --#                                         LexTokenManager.State,
   --#                                         Node,
   --#                                         Scope,
   --#                                         STree.Table,
   --#                                         The_Heap &
   --#         ErrorHandler.Error_Context,
   --#         SPARK_IO.File_Sys          from CommandLineData.Content,
   --#                                         Component_Data,
   --#                                         ContextManager.Ops.Unit_Stack,
   --#                                         Dictionary.Dict,
   --#                                         ErrorHandler.Error_Context,
   --#                                         LexTokenManager.State,
   --#                                         Node,
   --#                                         Scope,
   --#                                         SPARK_IO.File_Sys,
   --#                                         STree.Table,
   --#                                         The_Heap;
   --# pre Syntax_Node_Type (Node, STree.Table) = SP_Symbols.justification_statement;
   --# post STree.Table = STree.Table~;
   is
      It        : STree.Iterator;
      Next_Node : STree.SyntaxNode;

      --------------------------------------------------------------------------------

      procedure Wf_Justification_Clause
        (Start_Line     : in     LexTokenManager.Line_Numbers;
         Node           : in     STree.SyntaxNode;
         Scope          : in     Dictionary.Scopes;
         Component_Data : in out ComponentManager.ComponentData;
         The_Heap       : in out Heap.HeapRecord)
      --# global in     CommandLineData.Content;
      --#        in     ContextManager.Ops.Unit_Stack;
      --#        in     LexTokenManager.State;
      --#        in out Dictionary.Dict;
      --#        in out ErrorHandler.Error_Context;
      --#        in out SPARK_IO.File_Sys;
      --#        in out Statistics.TableUsage;
      --#        in out STree.Table;
      --# derives Component_Data,
      --#         Dictionary.Dict,
      --#         Statistics.TableUsage,
      --#         STree.Table,
      --#         The_Heap                   from *,
      --#                                         CommandLineData.Content,
      --#                                         Component_Data,
      --#                                         ContextManager.Ops.Unit_Stack,
      --#                                         Dictionary.Dict,
      --#                                         LexTokenManager.State,
      --#                                         Node,
      --#                                         Scope,
      --#                                         STree.Table,
      --#                                         The_Heap &
      --#         ErrorHandler.Error_Context,
      --#         SPARK_IO.File_Sys          from CommandLineData.Content,
      --#                                         Component_Data,
      --#                                         ContextManager.Ops.Unit_Stack,
      --#                                         Dictionary.Dict,
      --#                                         ErrorHandler.Error_Context,
      --#                                         LexTokenManager.State,
      --#                                         Node,
      --#                                         Scope,
      --#                                         SPARK_IO.File_Sys,
      --#                                         Start_Line,
      --#                                         STree.Table,
      --#                                         The_Heap;
      --# pre Syntax_Node_Type (Node, STree.Table) = SP_Symbols.justification_clause;
      --# post STree.Table = STree.Table~;
      is
         Error_Found                    : Boolean;
         Current_Node                   : STree.SyntaxNode;
         Err_Num_Node                   : STree.SyntaxNode;
         Valid                          : Boolean;
         Kind                           : ErrorHandler.Justification_Kinds;
         Val                            : Maths.Value;
         Maths_Valid                    : Maths.ErrorCode;
         Err_Num                        : Natural;
         Err_Num_Int                    : Integer;
         Explanation                    : E_Strings.T;
         Identifiers                    : ErrorHandler.Justification_Identifiers;
         Maximum_Justifications_Reached : Boolean;
         Applies_To_All                 : Boolean;

         --------------------------------------------------------------------------------

         procedure Check_Kind
           (Lex_String : in     LexTokenManager.Lex_String;
            Kind       :    out ErrorHandler.Justification_Kinds;
            Valid      :    out Boolean)
         --# global in LexTokenManager.State;
         --# derives Kind,
         --#         Valid from LexTokenManager.State,
         --#                    Lex_String;
         is
            Flow      : constant String := "FLOW_MESSAGE";
            Warn      : constant String := "WARNING_MESSAGE";
            Ex_String : E_Strings.T;
            Found     : Boolean;
            Start_Pos : E_Strings.Positions;
         begin
            -- The kind of message (Flow or Warning) is in the form of an identifier and therefore is extracted
            -- from the syntax tree as a Lex_String.  We first convert it to an Examiner_String

            -- Then we see if it a unique subset of either "Flow_Message" or "Warning_Message"
            -- Ignore case
            Ex_String := E_Strings.Upper_Case (E_Str => LexTokenManager.Lex_String_To_String (Lex_Str => Lex_String));
            -- Try "flow" first
            E_Strings.Find_Examiner_Sub_String
              (E_Str         => E_Strings.Copy_String (Str => Flow),
               Search_String => Ex_String,
               String_Found  => Found,
               String_Start  => Start_Pos);
            -- To get a match we need Found and Start_Pos = 1
            if Found and then Start_Pos = 1 then
               Kind  := ErrorHandler.Flow_Message;
               Valid := True;
            else
               -- Try "warn"
               E_Strings.Find_Examiner_Sub_String
                 (E_Str         => E_Strings.Copy_String (Str => Warn),
                  Search_String => Ex_String,
                  String_Found  => Found,
                  String_Start  => Start_Pos);
               -- To get a match we need Found and Start_Pos = 1
               if Found and then Start_Pos = 1 then
                  Kind  := ErrorHandler.Warning_Message;
                  Valid := True;
               else
                  Kind  := ErrorHandler.Flow_Message; -- not used, for DF purposes only
                  Valid := False;
               end if;
            end if;
         end Check_Kind;

         --------------------------------------------------------------------------------

         function Is_Disallowed_Warning (Kind    : ErrorHandler.Justification_Kinds;
                                         Err_Num : Natural) return Boolean is
         begin
            -- Initially only prohibit warnings generated by the justification system itself.
            -- Extend here as necessary.
            return Kind = ErrorHandler.Warning_Message and then (Err_Num = 120 or else Err_Num = 121 or else Err_Num = 122);
         end Is_Disallowed_Warning;

         --------------------------------------------------------------------------------

         procedure Check_Identifiers
           (Opt_Node       : in     STree.SyntaxNode;
            Scope          : in     Dictionary.Scopes;
            Kind           : in     ErrorHandler.Justification_Kinds;
            Err_Num        : in     Natural;
            Component_Data : in out ComponentManager.ComponentData;
            The_Heap       : in out Heap.HeapRecord;
            Identifiers    :    out ErrorHandler.Justification_Identifiers;
            Valid          :    out Boolean)
         --# global in     CommandLineData.Content;
         --#        in     ContextManager.Ops.Unit_Stack;
         --#        in     LexTokenManager.State;
         --#        in out Dictionary.Dict;
         --#        in out ErrorHandler.Error_Context;
         --#        in out SPARK_IO.File_Sys;
         --#        in out Statistics.TableUsage;
         --#        in out STree.Table;
         --# derives Component_Data,
         --#         Dictionary.Dict,
         --#         Statistics.TableUsage,
         --#         STree.Table,
         --#         The_Heap                   from *,
         --#                                         CommandLineData.Content,
         --#                                         Component_Data,
         --#                                         ContextManager.Ops.Unit_Stack,
         --#                                         Dictionary.Dict,
         --#                                         LexTokenManager.State,
         --#                                         Opt_Node,
         --#                                         Scope,
         --#                                         STree.Table,
         --#                                         The_Heap &
         --#         ErrorHandler.Error_Context,
         --#         SPARK_IO.File_Sys          from CommandLineData.Content,
         --#                                         Component_Data,
         --#                                         ContextManager.Ops.Unit_Stack,
         --#                                         Dictionary.Dict,
         --#                                         ErrorHandler.Error_Context,
         --#                                         Err_Num,
         --#                                         Kind,
         --#                                         LexTokenManager.State,
         --#                                         Opt_Node,
         --#                                         Scope,
         --#                                         SPARK_IO.File_Sys,
         --#                                         STree.Table,
         --#                                         The_Heap &
         --#         Identifiers                from CommandLineData.Content,
         --#                                         Component_Data,
         --#                                         ContextManager.Ops.Unit_Stack,
         --#                                         Dictionary.Dict,
         --#                                         LexTokenManager.State,
         --#                                         Opt_Node,
         --#                                         Scope,
         --#                                         STree.Table,
         --#                                         The_Heap &
         --#         Valid                      from CommandLineData.Content,
         --#                                         Component_Data,
         --#                                         ContextManager.Ops.Unit_Stack,
         --#                                         Dictionary.Dict,
         --#                                         Err_Num,
         --#                                         Kind,
         --#                                         LexTokenManager.State,
         --#                                         Opt_Node,
         --#                                         Scope,
         --#                                         STree.Table,
         --#                                         The_Heap;
         --# pre Syntax_Node_Type (Opt_Node, STree.Table) = SP_Symbols.justification_opt;
         --# post STree.Table = STree.Table~;
         is
            It                 : STree.Iterator;
            Next_Node          : STree.SyntaxNode;
            Identifier_Count   : Natural := 0;
            Name_Error         : Natural;
            Current_Name_Valid : Boolean;

            --------------------------------------------------------------------------------

            procedure Process_Dotted_Simple_Name_Or_Null
              (DSNON_Node     : in     STree.SyntaxNode;
               Scope          : in     Dictionary.Scopes;
               Component_Data : in out ComponentManager.ComponentData;
               The_Heap       : in out Heap.HeapRecord;
               Valid          :    out Boolean)
            --# global in     CommandLineData.Content;
            --#        in     ContextManager.Ops.Unit_Stack;
            --#        in     LexTokenManager.State;
            --#        in out Dictionary.Dict;
            --#        in out ErrorHandler.Error_Context;
            --#        in out Identifiers;
            --#        in out Identifier_Count;
            --#        in out SPARK_IO.File_Sys;
            --#        in out Statistics.TableUsage;
            --#        in out STree.Table;
            --# derives Component_Data,
            --#         Dictionary.Dict,
            --#         STree.Table,
            --#         The_Heap,
            --#         Valid                      from CommandLineData.Content,
            --#                                         Component_Data,
            --#                                         ContextManager.Ops.Unit_Stack,
            --#                                         Dictionary.Dict,
            --#                                         DSNON_Node,
            --#                                         LexTokenManager.State,
            --#                                         Scope,
            --#                                         STree.Table,
            --#                                         The_Heap &
            --#         ErrorHandler.Error_Context,
            --#         SPARK_IO.File_Sys          from CommandLineData.Content,
            --#                                         Component_Data,
            --#                                         ContextManager.Ops.Unit_Stack,
            --#                                         Dictionary.Dict,
            --#                                         DSNON_Node,
            --#                                         ErrorHandler.Error_Context,
            --#                                         LexTokenManager.State,
            --#                                         Scope,
            --#                                         SPARK_IO.File_Sys,
            --#                                         STree.Table,
            --#                                         The_Heap &
            --#         Identifiers                from *,
            --#                                         CommandLineData.Content,
            --#                                         Component_Data,
            --#                                         ContextManager.Ops.Unit_Stack,
            --#                                         Dictionary.Dict,
            --#                                         DSNON_Node,
            --#                                         Identifier_Count,
            --#                                         LexTokenManager.State,
            --#                                         Scope,
            --#                                         STree.Table,
            --#                                         The_Heap &
            --#         Identifier_Count           from * &
            --#         Statistics.TableUsage      from *,
            --#                                         CommandLineData.Content,
            --#                                         Component_Data,
            --#                                         ContextManager.Ops.Unit_Stack,
            --#                                         Dictionary.Dict,
            --#                                         DSNON_Node,
            --#                                         LexTokenManager.State,
            --#                                         Scope,
            --#                                         STree.Table,
            --#                                         The_Heap;
            --# pre Syntax_Node_Type (DSNON_Node, STree.Table) = SP_Symbols.dotted_simple_name_or_null;
            --# post STree.Table = STree.Table~;
            is
               Name_Node         : STree.SyntaxNode;
               Valid_Simple_Name : Boolean;
               Id_Str            : LexTokenManager.Lex_String;
               Sym               : Dictionary.Symbol;

               --------------------------------------------------------------------------------

               procedure Process_Dotted_Simple_Name
                 (DSN_Node       : in     STree.SyntaxNode;
                  Scope          : in     Dictionary.Scopes;
                  Component_Data : in out ComponentManager.ComponentData;
                  The_Heap       : in out Heap.HeapRecord;
                  Str            :    out LexTokenManager.Lex_String;
                  Sym            :    out Dictionary.Symbol;
                  Valid          :    out Boolean)
               --# global in     CommandLineData.Content;
               --#        in     ContextManager.Ops.Unit_Stack;
               --#        in     LexTokenManager.State;
               --#        in out Dictionary.Dict;
               --#        in out ErrorHandler.Error_Context;
               --#        in out SPARK_IO.File_Sys;
               --#        in out Statistics.TableUsage;
               --#        in out STree.Table;
               --# derives Component_Data,
               --#         Dictionary.Dict,
               --#         Str,
               --#         STree.Table,
               --#         Sym,
               --#         The_Heap,
               --#         Valid                      from CommandLineData.Content,
               --#                                         Component_Data,
               --#                                         ContextManager.Ops.Unit_Stack,
               --#                                         Dictionary.Dict,
               --#                                         DSN_Node,
               --#                                         LexTokenManager.State,
               --#                                         Scope,
               --#                                         STree.Table,
               --#                                         The_Heap &
               --#         ErrorHandler.Error_Context,
               --#         SPARK_IO.File_Sys          from CommandLineData.Content,
               --#                                         Component_Data,
               --#                                         ContextManager.Ops.Unit_Stack,
               --#                                         Dictionary.Dict,
               --#                                         DSN_Node,
               --#                                         ErrorHandler.Error_Context,
               --#                                         LexTokenManager.State,
               --#                                         Scope,
               --#                                         SPARK_IO.File_Sys,
               --#                                         STree.Table,
               --#                                         The_Heap &
               --#         Statistics.TableUsage      from *,
               --#                                         CommandLineData.Content,
               --#                                         Component_Data,
               --#                                         ContextManager.Ops.Unit_Stack,
               --#                                         Dictionary.Dict,
               --#                                         DSN_Node,
               --#                                         LexTokenManager.State,
               --#                                         Scope,
               --#                                         STree.Table,
               --#                                         The_Heap;
               --# pre Syntax_Node_Type (DSN_Node, STree.Table) = SP_Symbols.dotted_simple_name;
               --# post STree.Table = STree.Table~;
               is
                  It                    : STree.Iterator;
                  Dotted                : Boolean;
                  Id_Node               : STree.SyntaxNode;
                  P_Id_Str, Id_Str      : LexTokenManager.Lex_String;
                  Local_Sym, Sym_So_Far : Dictionary.Symbol;

                  --------------------------------------------------------------------------------

                  function Selector_Allowed_For (Sym : Dictionary.Symbol) return Boolean
                  --# global in Dictionary.Dict;
                  is
                  begin
                     return Dictionary.IsPackage (Sym)
                       or else (Dictionary.IsTypeMark (Sym)
                                  and then (Dictionary.TypeIsRecord (Sym) or else Dictionary.IsProtectedTypeMark (Sym)))
                       or else Dictionary.IsRecordComponent (Sym)
                       or else (Dictionary.IsObject (Sym) and then Dictionary.TypeIsRecord (Dictionary.GetType (Sym)))
                       or else (Dictionary.IsFunction (Sym) and then Dictionary.TypeIsRecord (Dictionary.GetType (Sym)))
                       or else (Dictionary.IsObject (Sym) and then Dictionary.IsProtectedType (Dictionary.GetType (Sym)));
                  end Selector_Allowed_For;

               begin -- Process_Dotted_Simple_Name
                  Valid := True; -- default

                  -- See whether it is a simple identifier or not.  If it is we return a Lex_String and a Symbol
                  -- otherwise just a Symbol.  Dotted gets set True if we loop through >1 identifiers
                  Dotted := False;

                  -- Loop through identifiers.  Loop exits prematurely for simple identifier case
                  It      :=
                    Find_First_Node (Node_Kind    => SP_Symbols.identifier,
                                     From_Root    => DSN_Node,
                                     In_Direction => STree.Down);
                  Id_Node := Get_Node (It);
                  -- ASSUME Id_Node = identifier
                  if Syntax_Node_Type (Node => Id_Node) = SP_Symbols.identifier then
                     -- ASSUME Id_Node = identifier
                     Id_Str := Node_Lex_String (Node => Id_Node);
                     -- Note that the lookup uses Proof_Context because we may be trying to justify a flow error
                     -- or warning involving an identifier that is not visible in Program_Context (eg an abstract
                     -- own variable).
                     Local_Sym :=
                       Dictionary.LookupItem
                       (Name              => Id_Str,
                        Scope             => Scope,
                        Context           => Dictionary.ProofContext,
                        Full_Package_Name => False);
                  else
                     Id_Str    := LexTokenManager.Null_String;
                     Local_Sym := Dictionary.NullSymbol;
                     SystemErrors.Fatal_Error
                       (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                        Msg     => "Expect Id_Node = identifier in Process_Dotted_Simple_Name");
                  end if;
                  P_Id_Str := LexTokenManager.Null_String;
                  loop
                     --# assert STree.Table = STree.Table~ and
                     --#   Syntax_Node_Type (Id_Node, STree.Table) = SP_Symbols.identifier and
                     --#   Id_Node = Get_Node (It);
                     -- any time we fail to find something it is an error failure
                     if Dictionary.Is_Null_Symbol (Local_Sym) then
                        ErrorHandler.Semantic_Error2
                          (Err_Num   => 1,
                           Reference => ErrorHandler.No_Reference,
                           Position  => Node_Position (Node => Id_Node),
                           Id_Str1   => Id_Str,
                           Id_Str2   => P_Id_Str);
                        Valid := False;
                        exit;
                     end if;
                     STree.Set_Node_Lex_String (Sym  => Local_Sym,
                                                Node => Id_Node);
                     -- set up next iteration
                     It := STree.NextNode (It);

                     exit when STree.IsNull (It);

                     -- If we get to here then there is more than one identifier

                     -- If there is more than one identifier and we are processing a record object or
                     -- record subcomponent, then there is
                     -- an extra step required:  we do not add symbols for all the components of records to the
                     -- dictionary all the time, but only where they are needed.  Therefore if we try and look
                     -- up R.F here (where R is record object) then the look up will fail because there is no
                     -- subcomponent symbol for R.F.  Therefore we must add the symbols now so that the
                     -- LookUpSelectedItem below will succeed.
                     if Dictionary.IsVariableOrSubcomponent (Local_Sym)
                       and then Dictionary.TypeIsRecord (Dictionary.GetType (Local_Sym)) then
                        Add_Record_Sub_Components
                          (Record_Var_Sym  => Local_Sym,
                           Record_Type_Sym => Dictionary.GetType (Local_Sym),
                           Component_Data  => Component_Data,
                           The_Heap        => The_Heap);
                     end if;
                     -- end of sub component addition

                     -- Because there is more than identifier we save some context for next time round the loop
                     Dotted     := True;
                     P_Id_Str   := Id_Str;
                     Id_Node    := Get_Node (It);
                     Id_Str     := Node_Lex_String (Node => Id_Node);
                     Sym_So_Far := Local_Sym;  -- needed for trapping P.P.P.P.X case later on

                     -- At this point we have a prefix in Local_Sym and we are about to process
                     -- a selector.  Local_Sym had better be the kind of thing that can have a
                     -- selector.
                     if not Selector_Allowed_For (Sym => Local_Sym) then
                        ErrorHandler.Semantic_Error_Sym (9, ErrorHandler.No_Reference, Node_Position (Id_Node), Local_Sym, Scope);
                        Local_Sym := Dictionary.NullSymbol;
                        exit;
                     end if;

                     -- Note that the lookup uses Proof_Context because we may be trying to justify a flow error
                     -- or warning involving an identifier that is not visible in Program_Context (eg an abstract
                     -- own variable).
                     Local_Sym :=
                       Dictionary.LookupSelectedItem
                       (Prefix   => Local_Sym,
                        Selector => Id_Str,
                        Scope    => Scope,
                        Context  => Dictionary.ProofContext);
                     -- check to see if we are getting the same symbol over and again
                     if Local_Sym = Sym_So_Far then            -- P.P.P.P.X case
                        Local_Sym := Dictionary.NullSymbol;  -- to cause "Not visible" error at top of loop
                     end if;
                  end loop;

                  -- return results
                  if Dotted then
                     Str := LexTokenManager.Null_String;
                  else
                     Str := Id_Str;
                  end if;
                  Sym := Local_Sym;
               end Process_Dotted_Simple_Name;

            begin -- Process_Dotted_Simple_Name_Or_Null
               if Identifier_Count <= ErrorHandler.Max_Justification_Identifier_Length then
                  Identifier_Count := Identifier_Count + 1;
               end if;

               Name_Node := Child_Node (Current_Node => DSNON_Node);
               -- ASSUME Name_Node = dotted_simple_name OR null_name
               case Syntax_Node_Type (Node => Name_Node) is
                  when SP_Symbols.dotted_simple_name =>
                     -- ASSUME Name_Node = dotted_simple_name
                     -- We have a single dotted simple name to process.
                     -- If it is OK then it will go in the
                     -- identifier list at index position Identifier_Count
                     Process_Dotted_Simple_Name
                       (DSN_Node       => Name_Node,
                        Scope          => Scope,
                        Component_Data => Component_Data,
                        The_Heap       => The_Heap,
                        Str            => Id_Str,
                        Sym            => Sym,
                        Valid          => Valid_Simple_Name);
                  when SP_Symbols.null_name =>
                     -- ASSUME Name_Node = null_name
                     Id_Str            := LexTokenManager.Null_String;
                     Sym               := Dictionary.GetNullVariable;
                     Valid_Simple_Name := True;
                  when others =>
                     -- Assign well-defined valued here to keep IFA happy.
                     Id_Str            := LexTokenManager.Null_String;
                     Sym               := Dictionary.NullSymbol;
                     Valid_Simple_Name := True;
                     SystemErrors.Fatal_Error
                       (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                        Msg     => "Expect Name_Node = dotted_simple_name OR null_name in Wf_Justification_Clause");
               end case;

               if Valid_Simple_Name then
                  Valid := True;
                  if Identifier_Count in ErrorHandler.Justification_Identifier_Index then
                     Identifiers (Identifier_Count) :=
                       ErrorHandler.Justification_Identifier'(String_Form => Id_Str,
                                                              Symbol_Form => Sym);
                  end if;
               else
                  Valid := False; -- don't add clause at all if any part is malformed
               end if;
            end Process_Dotted_Simple_Name_Or_Null;

            --------------------------------------------------------------------------------
            -- This function checks the number of names in the accept
            -- annotation against the error number. It returns the error message
            -- to report, or 0 if the clause is semantically correct.
            --
            -- Unfortunately Flow Error 10 can have either 0 or 1 identifiers.
            --
            -- Flow Errors 50 and 602 can have 1 or 2 identifiers, depending
            -- on whether the enclosing program unit is a function (1 identifier needed)
            -- or not (2 identifiers needed for procedures or task bodies).
            function Justification_Name_Length_Error
              (Enclosing_Region_Is_A_Function : Boolean;
               Err_Num                        : Natural)
              return                           Natural
            --# global in Identifier_Count;
            --#        in Kind;
            is
               Ret_Val : Natural := 0;
            begin
               case Kind is
                  when ErrorHandler.Flow_Message =>
                     case Err_Num is
                        -- These flow errors require exactly zero names to be justified
                        when 22 | 40 | 41 =>
                           if Identifier_Count /= 0 then
                              Ret_Val := 124;
                           end if;
                           -- These flow errors require exactly two names to be justified
                        when 3 | 4 | 57 | 601 | 605 | 606 =>
                           if Identifier_Count /= 2 then
                              Ret_Val := 126;
                           end if;
                           -- Flow Error 10 (ineffective expression or statement) can require
                           -- either zero or one name
                        when 10 =>
                           if Identifier_Count = 0 or else Identifier_Count = 1 then
                              Ret_Val := 0;
                           else
                              Ret_Val := 127;
                           end if;
                           -- Flow errors 50 and 602 can require one or two names,
                           -- depending on Enclosing_Region_Is_A_Function
                        when 50 | 602 =>
                           if Enclosing_Region_Is_A_Function then
                              -- function - 1 identifier needed
                              if Identifier_Count = 1 then
                                 Ret_Val := 0;
                              else
                                 Ret_Val := 125;
                              end if;
                           else
                              -- procedure or task body - 2 identifiers needed
                              if Identifier_Count = 2 then
                                 Ret_Val := 0;
                              else
                                 Ret_Val := 126;
                              end if;
                           end if;
                           -- All other flow errors require exactly one name
                        when others =>
                           if Identifier_Count /= 1 then
                              Ret_Val := 125;
                           end if;
                     end case;
                  when ErrorHandler.Warning_Message =>
                     case Err_Num is
                        -- The following warnings require exactly 1 name to be justified
                        when 1   |
                          5   |
                          9   |
                          10  |
                          12  |
                          13  |
                          169 |
                          311 |
                          312 |
                          313 |
                          314 |
                          350 |
                          351 |
                          391 |
                          392 |
                          393 |
                          394 |
                          395 |
                          396 |
                          397 |
                          398 |
                          400 |
                          403 |
                          410 |
                          411 |
                          412 |
                          413 =>
                           if Identifier_Count /= 1 then
                              Ret_Val := 125;
                           end if;
                           -- All other warnings require exactly zero names
                        when others =>
                           if Identifier_Count /= 0 then
                              Ret_Val := 124;
                           end if;
                     end case;
               end case;
               return Ret_Val;
            end Justification_Name_Length_Error;

         begin -- Check_Identifiers

            -- Rules:
            --   (1) Between 0 and ErrorHandler.Max_Justification_Identifier_Length identifiers found
            --   (2) Each identifier must be declared and visible in Scope
            --   (3) Identifiers (I) is populated with each legal identifier thus:
            --        (a) If the identifier has no dots in it, then we store the Lex_String AND the looked-up symbol
            --        (b) If it has dots then we store a null lex string and the looked-up symbol
            --            (this complexity is because we don't know whether warnings will be passed to the errohandler
            --             using, for example, Semantic_Warning or Semantic_Warning_Sym so we need to match either)

            -- Establish default result
            Identifiers := ErrorHandler.Null_Justification_Identifiers;
            Valid       := True;

            -- Iterate through dotted_simple_name_or_null nodes
            It :=
              Find_First_Node
              (Node_Kind    => SP_Symbols.dotted_simple_name_or_null,
               From_Root    => Opt_Node,
               In_Direction => STree.Down);
            while not STree.IsNull (It) loop
               Next_Node := Get_Node (It => It);
               --# assert STree.Table = STree.Table~ and
               --#   Syntax_Node_Type (Next_Node, STree.Table) = SP_Symbols.dotted_simple_name_or_null and
               --#   Next_Node = Get_Node (It);

               Process_Dotted_Simple_Name_Or_Null
                 (DSNON_Node     => Next_Node,
                  Scope          => Scope,
                  Component_Data => Component_Data,
                  The_Heap       => The_Heap,
                  Valid          => Current_Name_Valid);

               Valid := Valid and then Current_Name_Valid;
               It    := STree.NextNode (It);
            end loop;

            Name_Error :=
              Justification_Name_Length_Error
              (Enclosing_Region_Is_A_Function => Dictionary.IsFunction (Dictionary.GetRegion (Scope)),
               Err_Num                        => Err_Num);
            if Name_Error /= 0 then
               ErrorHandler.Semantic_Error
                 (Name_Error,
                  ErrorHandler.No_Reference,
                  Node_Position (Next_Sibling (Child_Node (Parent_Node (Opt_Node)))),
                  LexTokenManager.Null_String);
               Valid := False; -- don't add clause at all if any part is malformed
            end if;
         end Check_Identifiers;

         --------------------------------------------------------------------------------

         procedure Handle_Function_Return
           (Kind        : in     ErrorHandler.Justification_Kinds;
            Err_Num     : in     Natural;
            Identifiers : in out ErrorHandler.Justification_Identifiers)
         --# derives Identifiers from *,
         --#                          Err_Num,
         --#                          Kind;
         is

            function Message_Is_IFA (Err_Num : Natural) return Boolean is
            begin
               return Err_Num = 50 or else Err_Num = 602;
            end Message_Is_IFA;

         begin -- Handle_Function_Return

            -- If the users has tried to justify an information flow error where the "export" is the function
            -- return result, then there will only be one variable name in the message (which will say, e.g.,
            -- "The function value is not derived from the imported value(s) of Y.") but the pattern matching
            -- in ErrorHandler.Justification.Check_Whether_Justified will still be expecting an two symbols, an
            -- export followed by an import.  The Examiner's flow analyser uses Null_Symbol to represent the
            -- function return value.  In this procedure we:
            --    (1) Detect cases where only one argument has been supplied for an IFA msg that needs two
            --    (2) Assume in that case that a function return in implicitly intended
            --    (3) Move the given variable to the second, import, slot.
            --    (4) Insert a null identifier in the first slot (this will match Null_Symbol).
            -- Note that this transformation is "safe" even if the user has simply forgotten a variable name
            -- because the transformed annotation will not pattern match any more than it would have before.
            -- e.g. User in intends "F, 50, X, Y" but types "F, 50, X" by mistake.  Transformation gives
            -- "F, 50, Null_Sym, X".  Neither original incorrect form nor transformed form will pattern match
            -- so behaviour is unaltered

            if Kind = ErrorHandler.Flow_Message and then Message_Is_IFA (Err_Num => Err_Num) then
               -- possibly something to do
               if Identifiers (2) = ErrorHandler.Null_Justification_Identifier then
                  -- only one identifier was supplied so transformation is needed
                  Identifiers (2) := Identifiers (1); -- move given variable to second place
                  Identifiers (1) := ErrorHandler.Null_Justification_Identifier; -- to match Null_Sym
               end if;
            end if;
         end Handle_Function_Return;

      begin -- Wf_Justification_Clause
         Error_Found    := False;
         Applies_To_All := False;
         Identifiers    := ErrorHandler.Null_Justification_Identifiers;

         -- Check whether we are dealing with Flow_Message or Warning_Message --------
         Current_Node := Child_Node (Current_Node => Node);
         -- ASSUME Current_Node = identifier
         SystemErrors.RT_Assert
           (C       => Syntax_Node_Type (Node => Current_Node) = SP_Symbols.identifier,
            Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Current_Node = identifier in Wf_Justification_Clause");
         Check_Kind (Lex_String => Node_Lex_String (Node => Current_Node),
                     Kind       => Kind,
                     Valid      => Valid);
         if not Valid then
            Error_Found := True;
            ErrorHandler.Semantic_Error
              (121,
               ErrorHandler.No_Reference,
               Node_Position (Current_Node),
               LexTokenManager.Null_String);
         end if;

         --# assert STree.Table = STree.Table~;

         -- Check error number ---------------------------------------------------------
         Current_Node := Next_Sibling (Current_Node => Current_Node);
         -- ASSUME Current_Node = numeric_literal
         SystemErrors.RT_Assert
           (C       => Syntax_Node_Type (Node => Current_Node) = SP_Symbols.numeric_literal,
            Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Current_Node = numeric_literal in Wf_Justification_Clause");

         Err_Num_Node := Child_Node (Current_Node => Child_Node (Current_Node => Current_Node));
         -- ASSUME Err_Num_Node = integer_number OR real_number OR based_integer OR based_real
         if Syntax_Node_Type (Node => Err_Num_Node) = SP_Symbols.integer_number then
            -- ASSUME Err_Num_Node = integer_number
            Get_Literal_Value (Node => Err_Num_Node,
                               Val  => Val);
            Maths.ValueToInteger (Val, Err_Num_Int, Maths_Valid);

            Valid := Maths_Valid = Maths.NoError and then Err_Num_Int >= 0;
            if not Valid then
               Error_Found := True;
               Err_Num     := 0;
            else
               Err_Num := Err_Num_Int;
            end if;
         elsif Syntax_Node_Type (Node => Err_Num_Node) = SP_Symbols.real_number
           or else Syntax_Node_Type (Node => Err_Num_Node) = SP_Symbols.based_integer
           or else Syntax_Node_Type (Node => Err_Num_Node) = SP_Symbols.based_real then
            -- ASSUME Err_Num_Node = real_number OR based_integer OR based_real
            -- wrong kind of number
            Error_Found := True;
            Err_Num     := 0;
         else
            Error_Found := True;
            Err_Num     := 0;
            SystemErrors.Fatal_Error
              (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
               Msg     => "Expect Err_Num_Node = integer_number OR real_number OR " &
                 "based_integer OR based_real in Wf_Justification_Clause");
         end if;
         -- We should have a valid positive integer value for Err_Num by here.  If not, raise error
         if Err_Num = 0 then
            ErrorHandler.Semantic_Error
              (122,
               ErrorHandler.No_Reference,
               Node_Position (Err_Num_Node),
               LexTokenManager.Null_String);
         elsif Is_Disallowed_Warning (Kind    => Kind,
                                      Err_Num => Err_Num) then
            -- we have a wellformed warning number but we may want to disallow certain warning numbers
            ErrorHandler.Semantic_Error
              (123,
               ErrorHandler.No_Reference,
               Node_Position (Err_Num_Node),
               LexTokenManager.Null_String);
            Error_Found := True;
         end if;

         --# assert STree.Table = STree.Table~;

         -- Check identifiers ------------------------------------------------------------------
         Current_Node := Next_Sibling (Current_Node => Current_Node);
         -- ASSUME Current_Node = justification_opt OR justification_all
         case Syntax_Node_Type (Current_Node) is
            when SP_Symbols.justification_opt =>
               Check_Identifiers
                 (Opt_Node       => Current_Node,
                  Scope          => Scope,
                  Kind           => Kind,
                  Err_Num        => Err_Num,
                  Component_Data => Component_Data,
                  The_Heap       => The_Heap,
                  Identifiers    => Identifiers,
                  Valid          => Valid);
               if not Valid then -- I think this is clearer that Error_FOund := Error_Found or not Valid;
                  Error_Found := True;
               end if;
            when SP_Symbols.justification_all =>
               if CommandLineData.Content.Language_Profile in CommandLineData.Auto_Code_Generators then
                  Applies_To_All := True;
               else
                  ErrorHandler.Semantic_Error
                    (Err_Num   => 175,
                     Reference => ErrorHandler.No_Reference,
                     Position  => Node_Position (Node),
                     Id_Str    => LexTokenManager.Null_String);
               end if;
            when others =>
               SystemErrors.Fatal_Error
                 (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                  Msg     => "Expect Current_Node = justification_opt OR justification_all in Wf_Justification_Clause");
         end case;

         --# assert STree.Table = STree.Table~;

         -- Check explanation ------------------------------------------------------------------
         Current_Node := Next_Sibling (Current_Node => Current_Node);
         -- ASSUME Current_Node = justification_string
         SystemErrors.RT_Assert
           (C       => Syntax_Node_Type (Node => Current_Node) = SP_Symbols.justification_string,
            Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect Current_Node = justification_string in Wf_Justification_Clause");
         Explanation := E_Strings.Empty_String;
         while Syntax_Node_Type (Node => Current_Node) = SP_Symbols.justification_string loop
            --# assert STree.Table = STree.Table~ and
            --#   Syntax_Node_Type (Current_Node, STree.Table) = SP_Symbols.justification_string;
            Current_Node := Child_Node (Current_Node => Current_Node);
            -- ASSUME Current_Node = string_literal
            SystemErrors.RT_Assert
              (C       => Syntax_Node_Type (Node => Current_Node) = SP_Symbols.string_literal,
               Sys_Err => SystemErrors.Invalid_Syntax_Tree,
               Msg     => "Expect Current_Node = string_literal in Wf_Justification_Clause");
            if not E_Strings.Is_Empty (E_Str => Explanation) then
               E_Strings.Append_Char (E_Str => Explanation,
                                      Ch    => ' ');
            end if;
            E_Strings.Append_Examiner_String
              (E_Str1 => Explanation,
               E_Str2 => LexTokenManager.Lex_String_To_String (Node_Lex_String (Node => Current_Node)));
            Current_Node := Next_Sibling (Current_Node => Current_Node);
            -- ASSUME Current_Node = justification_string OR NULL
            SystemErrors.RT_Assert
              (C       => Current_Node = STree.NullNode
                 or else Syntax_Node_Type (Node => Current_Node) = SP_Symbols.justification_string,
               Sys_Err => SystemErrors.Invalid_Syntax_Tree,
               Msg     => "Expect Current_Node = justification_string OR NULL in Wf_Justification_Clause");
         end loop;

         --# assert STree.Table = STree.Table~;

         -- Insert justification data in error handler data table
         if not Error_Found then
            if not Applies_To_All then
               -- See whether Identifiers needs transforming to handle IFA errors on function return
               Handle_Function_Return (Kind        => Kind,
                                       Err_Num     => Err_Num,
                                       Identifiers => Identifiers);
            end if;

            -- Finally, add it to table of justification
            ErrorHandler.Start_Justification
              (Position                       => Node_Position (Node => Node),
               Line                           => Start_Line,
               Kind                           => Kind,
               Err_Num                        => Err_Num,
               Identifiers                    => Identifiers,
               Applies_To_All                 => Applies_To_All,
               Explanation                    => Explanation,
               Maximum_Justifications_Reached => Maximum_Justifications_Reached);

            if Maximum_Justifications_Reached then
               ErrorHandler.Semantic_Warning (122, Node_Position (Node), LexTokenManager.Null_String);
            end if;
         end if;
      end Wf_Justification_Clause;

   begin -- Wf_Start_Justification
      It := Find_First_Node (Node_Kind    => SP_Symbols.justification_clause,
                             From_Root    => Node,
                             In_Direction => STree.Down);
      while not STree.IsNull (It) loop
         Next_Node := Get_Node (It => It);
         --# assert STree.Table = STree.Table~ and
         --#   Syntax_Node_Type (Next_Node, STree.Table) = SP_Symbols.justification_clause and
         --#   Next_Node = Get_Node (It);
         Wf_Justification_Clause
           (Start_Line     => Node_Position (Node).Start_Line_No,
            Node           => Next_Node,
            Scope          => Scope,
            Component_Data => Component_Data,
            The_Heap       => The_Heap);
         It := STree.NextNode (It);
      end loop;
   end Wf_Start_Justification;

   --------------------------------------------------------------------------------

   procedure Wf_End_Justification (Node_Pos : in LexTokenManager.Token_Position)
   --# global in     CommandLineData.Content;
   --#        in     Dictionary.Dict;
   --#        in     LexTokenManager.State;
   --#        in out ErrorHandler.Error_Context;
   --#        in out SPARK_IO.File_Sys;
   --# derives ErrorHandler.Error_Context,
   --#         SPARK_IO.File_Sys          from CommandLineData.Content,
   --#                                         Dictionary.Dict,
   --#                                         ErrorHandler.Error_Context,
   --#                                         LexTokenManager.State,
   --#                                         Node_Pos,
   --#                                         SPARK_IO.File_Sys;
   is
      Unmatched_End : Boolean;
   begin
      ErrorHandler.End_Justification (Node_Pos.Start_Line_No,
                                      -- to get
                                      Unmatched_End);
      if Unmatched_End then
         ErrorHandler.Semantic_Warning (120, Node_Pos, LexTokenManager.Null_String);
      end if;
   end Wf_End_Justification;

begin -- Wf_Justification_Statement
   Next_Node := Child_Node (Current_Node => Node);
   -- ASSUME Next_Node = start_justification OR end_justification
   if Syntax_Node_Type (Node => Next_Node) = SP_Symbols.start_justification then
      -- ASSUME Next_Node = start_justification
      Wf_Start_Justification (Node           => Node,
                              Scope          => Scope,
                              Component_Data => Component_Data,
                              The_Heap       => The_Heap);
   elsif Syntax_Node_Type (Node => Next_Node) = SP_Symbols.end_justification then
      -- ASSUME Next_Node = end_justification
      Wf_End_Justification (Node_Pos => Node_Position (Node => Node));
   else
      SystemErrors.Fatal_Error
        (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Next_Node = start_justification OR end_justification in Wf_Justification_Statement");
   end if;

   if ErrorHandler.Generate_SLI then
      SLI.Generate_Xref_Justification (Comp_Unit  => ContextManager.Ops.Current_Unit,
                                       Parse_Tree => Node,
                                       Scope      => Scope);
   end if;
end Wf_Justification_Statement;
