This patch modifies the parser to detect missing parentheses on SPARK aspects
Global, Depends, Refined_Global and Refined_Depends.

------------
-- Source --
------------

--  malformed_contracts.ads

package Malformed_Contracts
  with Abstract_State => (State_1, State_2)
is
   procedure OK_1
     with Global => State_1;

   procedure OK_2
     with Global => (State_1, State_2);

   procedure Error_0
     with Global => State_1, State_2;

   procedure Error_1
     with Global => Input => State_1;

   procedure Error_2
     with Global => (Input => State_1;

   procedure Error_3
     with Global => Input => State_1, In_Out => State_2;

   procedure Error_4
     with Global => (Input => State_1, In_Out => State_2;

   procedure Error_5
     with Global  => (In_Out  => State_1),
          Depends =>  State_1 => State_1;

   procedure Error_6
     with Global  => (In_Out  => State_1),
          Depends => (State_1 => State_1;

   procedure Error_7
     with Global  => (Input   => State_1, In_Out => State_2),
          Depends =>  State_2 => State_1, null   => State_2;

   procedure Error_8
     with Global  => (Input   => State_1, In_Out => State_2),
          Depends => (State_2 => State_1, null   => State_2;
end Malformed_Contracts;

----------------------------
-- Compilation and output --
----------------------------

$ gcc -c malformed_contracts.ads
malformed_contracts.ads:11:21: missing "("
malformed_contracts.ads:14:21: missing "("
malformed_contracts.ads:17:38: ";" should be ","
malformed_contracts.ads:20:21: missing "("
malformed_contracts.ads:23:57: ";" should be ","
malformed_contracts.ads:27:23: missing "("
malformed_contracts.ads:31:41: ";" should be ","
malformed_contracts.ads:35:23: missing "("
malformed_contracts.ads:39:60: missing ")"

Tested on x86_64-pc-linux-gnu, committed on trunk

2014-02-19  Hristian Kirtchev  <kirtc...@adacore.com>

        * par.adb Alphabetize the routines in Par.Sync.
        (Resync_Past_Malformed_Aspect): New routine.
        * par-ch13.adb (Get_Aspect_Specifications): Alphabetize local
        variables. Code and comment reformatting. Detect missing
        parentheses on aspects [Refined_]Global and [Refined_]Depends
        with a non-null definition.
        * par-sync.adb: Alphabetize all routines in this separate unit.
        (Resync_Past_Malformed_Aspect): New routine.

Index: par-sync.adb
===================================================================
--- par-sync.adb        (revision 207879)
+++ par-sync.adb        (working copy)
@@ -148,47 +148,75 @@
       end if;
    end Resync_Init;
 
-   ---------------------------
-   -- Resync_Past_Semicolon --
-   ---------------------------
+   ----------------------------------
+   -- Resync_Past_Malformed_Aspect --
+   ----------------------------------
 
-   procedure Resync_Past_Semicolon is
+   procedure Resync_Past_Malformed_Aspect is
    begin
       Resync_Init;
 
       loop
-         --  Done if we are at a semicolon
+         --  A comma may separate two aspect specifications, but it may also
+         --  delimit multiple arguments of a single aspect.
 
-         if Token = Tok_Semicolon then
-            Scan; -- past semicolon
+         if Token = Tok_Comma then
+            declare
+               Scan_State : Saved_Scan_State;
+
+            begin
+               Save_Scan_State (Scan_State);
+               Scan; -- past comma
+
+               --  The identifier following the comma is a valid aspect, the
+               --  current malformed aspect has been successfully skipped.
+
+               if Token = Tok_Identifier
+                 and then Get_Aspect_Id (Token_Name) /= No_Aspect
+               then
+                  Restore_Scan_State (Scan_State);
+                  exit;
+
+               --  The comma is delimiting multiple arguments of an aspect
+
+               else
+                  Restore_Scan_State (Scan_State);
+               end if;
+            end;
+
+         --  An IS signals the last aspect specification when the related
+         --  context is a body.
+
+         elsif Token = Tok_Is then
             exit;
 
-         --  Done if we are at a token which normally appears only after
-         --  a semicolon. One special glitch is that the keyword private is
-         --  in this category only if it does NOT appear after WITH.
+         --  A semicolon signals the last aspect specification
 
-         elsif Token in Token_Class_After_SM
-            and then (Token /= Tok_Private or else Prev_Token /= Tok_With)
-         then
+         elsif Token = Tok_Semicolon then
             exit;
 
-         --  Otherwise keep going
+         --  In the case of a mistyped semicolon, any token which follows a
+         --  semicolon signals the last aspect specification.
 
-         else
-            Scan;
+         elsif Token in Token_Class_After_SM then
+            exit;
          end if;
+
+         --  Keep on resyncing
+
+         Scan;
       end loop;
 
       --  Fall out of loop with resynchronization complete
 
       Resync_Resume;
-   end Resync_Past_Semicolon;
+   end Resync_Past_Malformed_Aspect;
 
-   -------------------------
-   -- Resync_To_Semicolon --
-   -------------------------
+   ---------------------------
+   -- Resync_Past_Semicolon --
+   ---------------------------
 
-   procedure Resync_To_Semicolon is
+   procedure Resync_Past_Semicolon is
    begin
       Resync_Init;
 
@@ -196,6 +224,7 @@
          --  Done if we are at a semicolon
 
          if Token = Tok_Semicolon then
+            Scan; -- past semicolon
             exit;
 
          --  Done if we are at a token which normally appears only after
@@ -217,7 +246,7 @@
       --  Fall out of loop with resynchronization complete
 
       Resync_Resume;
-   end Resync_To_Semicolon;
+   end Resync_Past_Semicolon;
 
    ----------------------------------------------
    -- Resync_Past_Semicolon_Or_To_Loop_Or_Then --
@@ -275,35 +304,6 @@
       end if;
    end Resync_Resume;
 
-   --------------------
-   -- Resync_To_When --
-   --------------------
-
-   procedure Resync_To_When is
-   begin
-      Resync_Init;
-
-      loop
-         --  Done if at semicolon, WHEN or IS
-
-         if Token = Tok_Semicolon
-           or else Token = Tok_When
-           or else Token = Tok_Is
-         then
-            exit;
-
-         --  Otherwise keep going
-
-         else
-            Scan;
-         end if;
-      end loop;
-
-      --  Fall out of loop with resynchronization complete
-
-      Resync_Resume;
-   end Resync_To_When;
-
    ---------------------------
    -- Resync_Semicolon_List --
    ---------------------------
@@ -340,4 +340,68 @@
       Resync_Resume;
    end Resync_Semicolon_List;
 
+   -------------------------
+   -- Resync_To_Semicolon --
+   -------------------------
+
+   procedure Resync_To_Semicolon is
+   begin
+      Resync_Init;
+
+      loop
+         --  Done if we are at a semicolon
+
+         if Token = Tok_Semicolon then
+            exit;
+
+         --  Done if we are at a token which normally appears only after
+         --  a semicolon. One special glitch is that the keyword private is
+         --  in this category only if it does NOT appear after WITH.
+
+         elsif Token in Token_Class_After_SM
+           and then (Token /= Tok_Private or else Prev_Token /= Tok_With)
+         then
+            exit;
+
+         --  Otherwise keep going
+
+         else
+            Scan;
+         end if;
+      end loop;
+
+      --  Fall out of loop with resynchronization complete
+
+      Resync_Resume;
+   end Resync_To_Semicolon;
+
+   --------------------
+   -- Resync_To_When --
+   --------------------
+
+   procedure Resync_To_When is
+   begin
+      Resync_Init;
+
+      loop
+         --  Done if at semicolon, WHEN or IS
+
+         if Token = Tok_Semicolon
+           or else Token = Tok_When
+           or else Token = Tok_Is
+         then
+            exit;
+
+         --  Otherwise keep going
+
+         else
+            Scan;
+         end if;
+      end loop;
+
+      --  Fall out of loop with resynchronization complete
+
+      Resync_Resume;
+   end Resync_To_When;
+
 end Sync;
Index: par-ch13.adb
===================================================================
--- par-ch13.adb        (revision 207879)
+++ par-ch13.adb        (working copy)
@@ -149,9 +149,9 @@
    function Get_Aspect_Specifications
      (Semicolon : Boolean := True) return List_Id
    is
+      A_Id    : Aspect_Id;
+      Aspect  : Node_Id;
       Aspects : List_Id;
-      Aspect  : Node_Id;
-      A_Id    : Aspect_Id;
       OK      : Boolean;
 
    begin
@@ -173,9 +173,13 @@
       loop
          OK := True;
 
+         --  The aspect mark is not an identifier
+
          if Token /= Tok_Identifier then
             Error_Msg_SC ("aspect identifier expected");
 
+            --  Skip the whole aspect specification list
+
             if Semicolon then
                Resync_Past_Semicolon;
             end if;
@@ -183,17 +187,16 @@
             return Aspects;
          end if;
 
-         --  We have an identifier (which should be an aspect identifier)
-
          A_Id := Get_Aspect_Id (Token_Name);
          Aspect :=
            Make_Aspect_Specification (Token_Ptr,
              Identifier => Token_Node);
 
-         --  No valid aspect identifier present
+         --  The aspect mark is not recognized
 
          if A_Id = No_Aspect then
             Error_Msg_SC ("aspect identifier expected");
+            OK := False;
 
             --  Check bad spelling
 
@@ -209,18 +212,24 @@
             Scan; -- past incorrect identifier
 
             if Token = Tok_Apostrophe then
-               Scan; -- past '
+               Scan; -- past apostrophe
                Scan; -- past presumably CLASS
             end if;
 
+            --  Attempt to parse the aspect definition by assuming it is an
+            --  expression.
+
             if Token = Tok_Arrow then
-               Scan; -- Past arrow
+               Scan; -- past arrow
                Set_Expression (Aspect, P_Expression);
-               OK := False;
 
+            --  The aspect may behave as a boolean aspect
+
             elsif Token = Tok_Comma then
-               OK := False;
+               null;
 
+            --  Otherwise the aspect contains a junk definition
+
             else
                if Semicolon then
                   Resync_Past_Semicolon;
@@ -229,7 +238,7 @@
                return Aspects;
             end if;
 
-         --  OK aspect scanned
+         --  Aspect mark is OK
 
          else
             Scan; -- past identifier
@@ -237,60 +246,58 @@
             --  Check for 'Class present
 
             if Token = Tok_Apostrophe then
-               if not Class_Aspect_OK (A_Id) then
-                  Error_Msg_Node_1 := Identifier (Aspect);
-                  Error_Msg_SC ("aspect& does not permit attribute here");
+               if Class_Aspect_OK (A_Id) then
                   Scan; -- past apostrophe
-                  Scan; -- past presumed CLASS
-                  OK := False;
 
-               else
-                  Scan; -- past apostrophe
-
-                  if Token /= Tok_Identifier
-                    or else Token_Name /= Name_Class
+                  if Token = Tok_Identifier
+                    and then Token_Name = Name_Class
                   then
+                     Scan; -- past CLASS
+                     Set_Class_Present (Aspect);
+                  else
                      Error_Msg_SC ("Class attribute expected here");
                      OK := False;
 
                      if Token = Tok_Identifier then
                         Scan; -- past identifier not CLASS
                      end if;
+                  end if;
 
-                  else
-                     Scan; -- past CLASS
-                     Set_Class_Present (Aspect);
-                  end if;
+               --  The aspect does not allow 'Class
+
+               else
+                  Error_Msg_Node_1 := Identifier (Aspect);
+                  Error_Msg_SC ("aspect& does not permit attribute here");
+                  OK := False;
+
+                  Scan; -- past apostrophe
+                  Scan; -- past presumably CLASS
                end if;
             end if;
 
-            --  Test case of missing aspect definition
+            --  Check for a missing aspect definition. Aspects with optional
+            --  definitions are not considered.
 
-            if Token = Tok_Comma
-              or else Token = Tok_Semicolon
-            then
+            if Token = Tok_Comma or else Token = Tok_Semicolon then
                if Aspect_Argument (A_Id) /= Optional_Expression
-                    and then
-                  Aspect_Argument (A_Id) /= Optional_Name
+                 and then Aspect_Argument (A_Id) /= Optional_Name
                then
                   Error_Msg_Node_1 := Identifier (Aspect);
                   Error_Msg_AP ("aspect& requires an aspect definition");
                   OK := False;
                end if;
 
+            --  Check for a missing arrow when the aspect has a definition
+
             elsif not Semicolon and then Token /= Tok_Arrow then
                if Aspect_Argument (A_Id) /= Optional_Expression
-                    and then
-                  Aspect_Argument (A_Id) /= Optional_Name
+                 and then Aspect_Argument (A_Id) /= Optional_Name
                then
-                  --  The name or expression may be there, but the arrow is
-                  --  missing. Skip to the end of the declaration.
-
                   T_Arrow;
                   Resync_To_Semicolon;
                end if;
 
-            --  Here we have an aspect definition
+            --  Otherwise we have an aspect definition
 
             else
                if Token = Tok_Arrow then
@@ -300,9 +307,107 @@
                   OK := False;
                end if;
 
+               --  Detect a common error where the non-null definition of
+               --  aspect Depends, Global, Refined_Depends or Refined_Global
+               --  must be enclosed in parentheses.
+
+               if Token /= Tok_Left_Paren and then Token /= Tok_Null then
+
+                  --  [Refined_]Depends
+
+                  if A_Id = Aspect_Depends
+                       or else
+                     A_Id = Aspect_Refined_Depends
+                  then
+                     Error_Msg_SC -- CODEFIX
+                       ("missing ""(""");
+                     Resync_Past_Malformed_Aspect;
+
+                     --  Return when the current aspect is the last in the list
+                     --  of specifications and the list applies to a body.
+
+                     if Token = Tok_Is then
+                        return Aspects;
+                     end if;
+
+                  --  [Refined_]Global
+
+                  elsif A_Id = Aspect_Global
+                          or else
+                        A_Id = Aspect_Refined_Global
+                  then
+                     declare
+                        Scan_State : Saved_Scan_State;
+
+                     begin
+                        Save_Scan_State (Scan_State);
+                        Scan; -- past item or mode_selector
+
+                        --  Emit an error when the aspect has a mode_selector
+                        --  as the moded_global_list must be parenthesized:
+                        --    with Global => Output => Item
+
+                        if Token = Tok_Arrow then
+                           Restore_Scan_State (Scan_State);
+                           Error_Msg_SC -- CODEFIX
+                             ("missing ""(""");
+                           Resync_Past_Malformed_Aspect;
+
+                           --  Return when the current aspect is the last in
+                           --  the list of specifications and the list applies
+                           --  to a body.
+
+                           if Token = Tok_Is then
+                              return Aspects;
+                           end if;
+
+                        elsif Token = Tok_Comma then
+                           Scan; -- past comma
+
+                           --  An item followed by a comma does not need to
+                           --  be parenthesized if the next token is a valid
+                           --  aspect name:
+                           --    with Global => Item,
+                           --         Aspect => ...
+
+                           if Token = Tok_Identifier
+                             and then Get_Aspect_Id (Token_Name) /= No_Aspect
+                           then
+                              Restore_Scan_State (Scan_State);
+
+                           --  Otherwise this is a list of items in which case
+                           --  the list must be parenthesized.
+
+                           else
+                              Restore_Scan_State (Scan_State);
+                              Error_Msg_SC -- CODEFIX
+                                ("missing ""(""");
+                              Resync_Past_Malformed_Aspect;
+
+                              --  Return when the current aspect is the last
+                              --  in the list of specifications and the list
+                              --  applies to a body.
+
+                              if Token = Tok_Is then
+                                 return Aspects;
+                              end if;
+                           end if;
+
+                        --  The definition of [Refined_]Global does not need to
+                        --  be parenthesized.
+
+                        else
+                           Restore_Scan_State (Scan_State);
+                        end if;
+                     end;
+                  end if;
+               end if;
+
+               --  Parse the aspect definition depening on the expected
+               --  argument kind.
+
                if Aspect_Argument (A_Id) = Name
-                    or else
-                  Aspect_Argument (A_Id) = Optional_Name
+                 or else Aspect_Argument (A_Id) = Optional_Name
                then
                   Set_Expression (Aspect, P_Name);
 
@@ -315,18 +420,21 @@
                end if;
             end if;
 
-            --  If OK clause scanned, add it to the list
+            --  Add the aspect to the resulting list only when it was properly
+            --  parsed.
 
             if OK then
                Append (Aspect, Aspects);
             end if;
 
+            --  The aspect specification list contains more than one aspect
+
             if Token = Tok_Comma then
                Scan; -- past comma
                goto Continue;
 
-            --  Recognize the case where a comma is missing between two
-            --  aspects, issue an error and proceed with next aspect.
+            --  Check for a missing comma between two aspects. Emit an error
+            --  and proceed to the next aspect.
 
             elsif Token = Tok_Identifier
               and then Get_Aspect_Id (Token_Name) /= No_Aspect
@@ -338,20 +446,25 @@
                   Save_Scan_State (Scan_State);
                   Scan; -- past identifier
 
-                  if Token = Tok_Arrow then
+                  --  Attempt to detect ' or => following a potential aspect
+                  --  mark.
+
+                  if Token = Tok_Apostrophe or else Token = Tok_Arrow then
                      Restore_Scan_State (Scan_State);
                      Error_Msg_AP -- CODEFIX
                        ("|missing "",""");
                      goto Continue;
 
+                  --  The construct following the current aspect is not an
+                  --  aspect.
+
                   else
                      Restore_Scan_State (Scan_State);
                   end if;
                end;
 
-            --  Recognize the case where a semicolon was mistyped for a comma
-            --  between two aspects, issue an error and proceed with next
-            --  aspect.
+            --  Check for a mistyped semicolon in place of a comma between two
+            --  aspects. Emit an error and proceed to the next aspect.
 
             elsif Token = Tok_Semicolon then
                declare
@@ -366,20 +479,22 @@
                   then
                      Scan; -- past identifier
 
-                     if Token = Tok_Arrow then
+                     --  Attempt to detect ' or => following a potential aspect
+                     --  mark.
+
+                     if Token = Tok_Apostrophe or else Token = Tok_Arrow then
                         Restore_Scan_State (Scan_State);
                         Error_Msg_SC -- CODEFIX
                           ("|"";"" should be "",""");
                         Scan; -- past semicolon
                         goto Continue;
-
-                     else
-                        Restore_Scan_State (Scan_State);
                      end if;
+                  end if;
 
-                  else
-                     Restore_Scan_State (Scan_State);
-                  end if;
+                  --  The construct following the current aspect is not an
+                  --  aspect.
+
+                  Restore_Scan_State (Scan_State);
                end;
             end if;
 
@@ -397,7 +512,6 @@
       end loop;
 
       return Aspects;
-
    end Get_Aspect_Specifications;
 
    --------------------------------------------
Index: par.adb
===================================================================
--- par.adb     (revision 207879)
+++ par.adb     (working copy)
@@ -1079,6 +1079,10 @@
       --  advanced to the next vertical bar, arrow, or semicolon, whichever
       --  comes first. We also quit if we encounter an end of file.
 
+      procedure Resync_Cunit;
+      --  Synchronize to next token which could be the start of a compilation
+      --  unit, or to the end of file token.
+
       procedure Resync_Expression;
       --  Used if an error is detected during the parsing of an expression.
       --  It skips past tokens until either a token which cannot be part of
@@ -1087,6 +1091,11 @@
       --  current parenthesis level (a parenthesis level counter is maintained
       --  to carry out this test).
 
+      procedure Resync_Past_Malformed_Aspect;
+      --  Used when parsing aspect specifications to skip a malformed aspect.
+      --  The scan pointer is positioned next to a comma, a semicolon or "is"
+      --  when the aspect applies to a body.
+
       procedure Resync_Past_Semicolon;
       --  Used if an error occurs while scanning a sequence of declarations.
       --  The scan pointer is positioned past the next semicolon and the scan
@@ -1094,30 +1103,26 @@
       --  starts a declaration (but we make sure to skip at least one token
       --  in this case, to avoid getting stuck in a loop).
 
-      procedure Resync_To_Semicolon;
-      --  Similar to Resync_Past_Semicolon, except that the scan pointer is
-      --  left pointing to the semicolon rather than past it.
-
       procedure Resync_Past_Semicolon_Or_To_Loop_Or_Then;
       --  Used if an error occurs while scanning a sequence of statements. The
       --  scan pointer is positioned past the next semicolon, or to the next
       --  occurrence of either then or loop, and the scan resumes.
 
-      procedure Resync_To_When;
-      --  Used when an error occurs scanning an entry index specification. The
-      --  scan pointer is positioned to the next WHEN (or to IS or semicolon if
-      --  either of these appear before WHEN, indicating another error has
-      --  occurred).
-
       procedure Resync_Semicolon_List;
       --  Used if an error occurs while scanning a parenthesized list of items
       --  separated by semicolons. The scan pointer is advanced to the next
       --  semicolon or right parenthesis at the outer parenthesis level, or
       --  to the next is or RETURN keyword occurrence, whichever comes first.
 
-      procedure Resync_Cunit;
-      --  Synchronize to next token which could be the start of a compilation
-      --  unit, or to the end of file token.
+      procedure Resync_To_Semicolon;
+      --  Similar to Resync_Past_Semicolon, except that the scan pointer is
+      --  left pointing to the semicolon rather than past it.
+
+      procedure Resync_To_When;
+      --  Used when an error occurs scanning an entry index specification. The
+      --  scan pointer is positioned to the next WHEN (or to IS or semicolon if
+      --  either of these appear before WHEN, indicating another error has
+      --  occurred).
    end Sync;
 
    --------------

Reply via email to