-------------------------------------------------------------------------------
-- (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 (Sem)
procedure Check_Valid_Ident (Ident_Node    : in     STree.SyntaxNode;
                             Current_Scope : in     Dictionary.Scopes;
                             Ok            :    out Boolean) is
   Ident_Str : LexTokenManager.Lex_String;

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

   -- If we are declaring a subprogram in a package spec and the spec contains
   -- protected types we search each of these to detect re-use of the subprogram
   -- name.  If we don't trap such re-use at this point then we end up with a
   -- legal package spec for which no legal body could be written (since its
   -- implementation would inevitably involve overload resolution of calls made from
   -- within the protected body.  e.g. type PT in package P declares operation K.  Package
   -- P also declares an operation K.  From inside the body of PT, a call to K could refer
   -- to either of the two Ks since both are directly visible.
   function Is_Defined_In_Protected_Type
     (Name          : LexTokenManager.Lex_String;
      Current_Scope : Dictionary.Scopes;
      Is_Private    : Boolean)
     return          Boolean
   --# global in Dictionary.Dict;
   --#        in LexTokenManager.State;
   is
      Result : Boolean := False;
      It     : Dictionary.Iterator;
   begin
      if Dictionary.IsPackage (Dictionary.GetRegion (Current_Scope)) then
         if Is_Private then
            It := Dictionary.First_Private_Protected_Type (The_Package => Dictionary.GetRegion (Current_Scope));
         else
            It := Dictionary.First_Visible_Protected_Type (The_Package => Dictionary.GetRegion (Current_Scope));
         end if;
         while not Dictionary.IsNullIterator (It) loop
            Result :=
              Dictionary.IsDirectlyDefined
              (Name    => Name,
               Scope   => Dictionary.Set_Visibility (The_Visibility => Dictionary.Visible,
                                                     The_Unit       => Dictionary.CurrentSymbol (It)),
               Context => Dictionary.ProofContext);
            exit when Result;
            It := Dictionary.NextSymbol (It);
         end loop;
      end if;
      return Result;
   end Is_Defined_In_Protected_Type;

begin -- Check_Valid_Ident

   -- check that name is not already in use
   Ident_Str := Node_Lex_String (Node => Ident_Node);
   if Dictionary.IsDefined
     (Name              => Ident_Str,
      Scope             => Current_Scope,
      Context           => Dictionary.ProofContext,
      Full_Package_Name => False) then
      Ok := False;
      ErrorHandler.Semantic_Error
        (Err_Num   => 10,
         Reference => ErrorHandler.No_Reference,
         Position  => Node_Position (Node => Ident_Node),
         Id_Str    => Ident_Str);
   elsif Is_Defined_In_Protected_Type (Name          => Ident_Str,
                                       Current_Scope => Current_Scope,
                                       Is_Private    => False)
     or else Is_Defined_In_Protected_Type (Name          => Ident_Str,
                                           Current_Scope => Current_Scope,
                                           Is_Private    => True) then
      Ok := False;
      ErrorHandler.Semantic_Error
        (Err_Num   => 988,
         Reference => ErrorHandler.No_Reference,
         Position  => Node_Position (Node => Ident_Node),
         Id_Str    => Ident_Str);
   else
      Ok := True;
   end if;
end Check_Valid_Ident;
