-------------------------------------------------------------------------------
-- (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.
--
--=============================================================================

separate (FlowAnalyser.FlowAnalyse.AnalyseRelations)
procedure CheckUsages is
   AffectedExports, ExpOrRefSeq, ImpOrDefSeq, RhoRow : SeqAlgebra.Seq;
   M                                                 : SeqAlgebra.MemberOfSeq;
   Variable                                          : Natural;
   VarSymbol                                         : Dictionary.Symbol;
   PreservedVars                                     : SeqAlgebra.Seq;

   TempSeq    : SeqAlgebra.Seq;
   TempMem    : SeqAlgebra.MemberOfSeq;
   TempLeaves : SeqAlgebra.Seq;
   TempSym    : Dictionary.Symbol;

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

   procedure AddError
     (Err      : in ErrorHandler.Usage_Err_Type;
      Position : in LexTokenManager.Token_Position;
      Sym      : in Dictionary.Symbol;
      Scope    : in Dictionary.Scopes)
   --# global in     CommandLineData.Content;
   --#        in     ComponentData;
   --#        in     Dictionary.Dict;
   --#        in     LexTokenManager.State;
   --#        in out ErrorHandler.Error_Context;
   --#        in out SPARK_IO.File_Sys;
   --#        in out Statistics.TableUsage;
   --#        in out TheErrorHeap;
   --#        in out TheHeap;
   --# derives ErrorHandler.Error_Context,
   --#         SPARK_IO.File_Sys          from CommandLineData.Content,
   --#                                         Dictionary.Dict,
   --#                                         Err,
   --#                                         ErrorHandler.Error_Context,
   --#                                         LexTokenManager.State,
   --#                                         Position,
   --#                                         Scope,
   --#                                         SPARK_IO.File_Sys,
   --#                                         Sym &
   --#         Statistics.TableUsage,
   --#         TheHeap                    from *,
   --#                                         ComponentData,
   --#                                         Dictionary.Dict,
   --#                                         Err,
   --#                                         Position,
   --#                                         Sym,
   --#                                         TheErrorHeap,
   --#                                         TheHeap &
   --#         TheErrorHeap               from *,
   --#                                         Dictionary.Dict,
   --#                                         Err,
   --#                                         Position,
   --#                                         Sym,
   --#                                         TheHeap;
   --#
   is
      NewError : Natural;
   begin
      if Dictionary.IsSubcomponent (Sym) then
         ComponentErrors.CreateError
           (TheErrorHeap,
            TheHeap,
            ComponentErrors.Usage,
            ErrorHandler.Usage_Err_Type'Pos (Err),
            Position,
            Dictionary.NullSymbol,
            --to get
            NewError);

         ComponentManager.AddError
           (TheHeap,
            TheErrorHeap,
            ComponentData,
            ComponentManager.GetComponentNode (ComponentData, Sym),
            NewError);
      else
         ErrorHandler.Usage_Error (Err_Type => Err,
                                   Position => Position,
                                   Var_Sym  => Sym,
                                   Scope    => Scope);
      end if;
   end AddError;

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

begin --CheckUsages
   SeqAlgebra.Union (TheHeap, SeqOfInitVars, ExpSeqOfImports, ImpOrDefSeq);
   SeqAlgebra.AugmentSeq (TheHeap, ImpOrDefSeq, IFA_Stack.Top (S).DefinedVars);
   SeqAlgebra.Union (TheHeap, ExpSeqOfExports, ReferencedVars, ExpOrRefSeq);
   SeqAlgebra.AugmentSeq (TheHeap, IFA_Stack.Top (S).AllVars, SeqOfInitVars);
   SeqAlgebra.AugmentSeq (TheHeap, IFA_Stack.Top (S).AllVars, ExpSeqOfImports);
   SeqAlgebra.AugmentSeq (TheHeap, IFA_Stack.Top (S).AllVars, ExpSeqOfExports);
   SeqAlgebra.CreateSeq (TheHeap, PreservedVars);
   SeqAlgebra.Intersection (TheHeap, IFA_Stack.Top (S).DefinedVars, ExpSeqOfImports, TempSeq);
   TempMem := SeqAlgebra.FirstMember (TheHeap, TempSeq);
   while not SeqAlgebra.IsNullMember (TempMem) loop
      TempSym :=
        Dictionary.ConvertSymbolRef (ExaminerConstants.RefType (SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                                                                            M        => TempMem)));

      if Dictionary.IsSubcomponent (TempSym) then
         ComponentManager.GetLeaves
           (TheHeap,
            ComponentData,
            ComponentManager.GetRoot (ComponentData, ComponentManager.GetComponentNode (ComponentData, TempSym)),
            --to get
            TempLeaves);

         SeqAlgebra.AugmentSeq (TheHeap, PreservedVars, TempLeaves);
         SeqAlgebra.DisposeOfSeq (TheHeap, TempLeaves);
      end if;

      TempMem := SeqAlgebra.NextMember (TheHeap, TempMem);
   end loop;

   SeqAlgebra.DisposeOfSeq (TheHeap, TempSeq);
   TempSeq := IFA_Stack.Top (S).DefinedVars;
   SeqAlgebra.Reduction (TheHeap, PreservedVars, TempSeq);

   M := SeqAlgebra.FirstMember (TheHeap, IFA_Stack.Top (S).AllVars);
   while not SeqAlgebra.IsNullMember (M) loop

      Variable := SeqAlgebra.Value_Of_Member (The_Heap => TheHeap,
                                              M        => M);
      if Variable /= FnResultRepn then
         VarSymbol := Dictionary.ConvertSymbolRef (ExaminerConstants.RefType (Variable));
         if not SeqAlgebra.IsMember (TheHeap, ImpOrDefSeq, Variable) and then not Dictionary.IsImplicitInStream (VarSymbol) then
            AddError (ErrorHandler.Undefined_Var, EndPosition, VarSymbol, Scope);
         end if;
         if SeqAlgebra.IsMember (TheHeap, ExpSeqOfImports, Variable) then
            if SeqAlgebra.IsMember (TheHeap, IFA_Stack.Top (S).DefinedVars, Variable)
              and then not SeqAlgebra.IsMember (TheHeap, ExpSeqOfExports, Variable)
              and then not Dictionary.Is_Null_Variable (VarSymbol) then
               AddError (ErrorHandler.Redefined_Import, EndPosition, VarSymbol, Scope);
            end if;
            if not SeqAlgebra.IsMember (TheHeap, ExpOrRefSeq, Variable) and then not Dictionary.Is_Null_Variable (VarSymbol) then
               AddError (ErrorHandler.Unused_Import, EndPosition, VarSymbol, Scope);
            else
               RelationAlgebra.RowExtraction (TheHeap, IFA_Stack.Top (S).Rho, Variable, RhoRow);
               SeqAlgebra.Intersection (TheHeap, RhoRow, ExpSeqOfExports, AffectedExports);
               -- Remove any affected exports that are implicit in streams of protected state since they are
               -- not significant or genuine exports
               SeqAlgebra.Reduction (TheHeap, AffectedExports, InStreamsOfShareableProtectedVars);
               SeqAlgebra.DisposeOfSeq (TheHeap, RhoRow);
               if SeqAlgebra.IsEmptySeq (TheHeap, AffectedExports)
                 and then (not (Dictionary.IsSubcomponent (VarSymbol)
                                  and then SeqAlgebra.IsMember (TheHeap, PreservedVars, Variable)))
                 and then
                 -- following covers implicit importing of out streams that should not be reported
                 (Dictionary.GetOwnVariableOrConstituentMode (Dictionary.GetMostEnclosingObject (VarSymbol)) /=
                    Dictionary.OutMode) then
                  AddError (ErrorHandler.Ineffective_Import, EndPosition, VarSymbol, Scope);
               end if;
               SeqAlgebra.DisposeOfSeq (TheHeap, AffectedExports);
            end if;
         elsif not SeqAlgebra.IsMember (TheHeap, ExpOrRefSeq, Variable) then
            AddError (ErrorHandler.Unreferenced_Var, EndPosition, VarSymbol, Scope);
         end if;
         if SeqAlgebra.IsMember (TheHeap, ExpSeqOfExports, Variable)
           and then (not SeqAlgebra.IsMember (TheHeap, IFA_Stack.Top (S).DefinedVars, Variable))
           and then (not (Dictionary.IsSubcomponent (VarSymbol) and then SeqAlgebra.IsMember (TheHeap, PreservedVars, Variable)))
           and then
           -- following covers implicit updating of in streams that should not be reported
           (Dictionary.GetOwnVariableOrConstituentMode (Dictionary.GetMostEnclosingObject (VarSymbol)) /= Dictionary.InMode)
           and then
           -- neither should undefinedness of null (data sink) variable
           not Dictionary.Is_Null_Variable (VarSymbol)
           and then not Dictionary.IsImplicitInStream (VarSymbol) then
            AddError (ErrorHandler.Undefined_Export, EndPosition, VarSymbol, Scope);
         end if;
      end if;
      M := SeqAlgebra.NextMember (TheHeap, M);
   end loop;
   SeqAlgebra.DisposeOfSeq (TheHeap, ExpOrRefSeq);
   SeqAlgebra.DisposeOfSeq (TheHeap, ImpOrDefSeq);
   SeqAlgebra.DisposeOfSeq (TheHeap, PreservedVars);
end CheckUsages;
