This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: cpplib: Stricter syntax checking


OK, this is the final patch where we only warn if -pedantic.  It looks
like it'll bootstrap, if it does I'll commit it.

I tripped over a null dereference using CPP_PEDANTIC for command-line
stuff, so I'm using CPP_OPTION instead.  I think that area needs a
tidy up in addition to the error interface; it's too easy to screw up.

Neil.

Index: cpphash.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cpphash.c,v
retrieving revision 1.95
diff -u -p -r1.95 cpphash.c
--- cpphash.c	2000/07/05 05:33:56	1.95
+++ cpphash.c	2000/07/08 01:40:05
@@ -52,6 +52,7 @@ static void dump_funlike_macro	PARAMS ((
 static const cpp_token *count_params PARAMS ((cpp_reader *,
 					      const cpp_token *,
 					      cpp_toklist *));
+static int is__va_args__ PARAMS ((cpp_reader *, const cpp_token *));
 static cpp_toklist *parse_define PARAMS((cpp_reader *));
 static int check_macro_redefinition PARAMS((cpp_reader *, cpp_hashnode *hp,
 					     const cpp_toklist *));
@@ -197,6 +198,26 @@ find_param (first, token)
   return 0;
 }
 
+/* Constraint 6.10.3.5: __VA_ARGS__ should only appear in the
+   replacement list of a variable-arguments macro.  TOKEN is assumed
+   to be of type CPP_NAME.  */
+static int
+is__va_args__ (pfile, token)
+     cpp_reader *pfile;
+     const cpp_token *token;
+{
+  if (!CPP_OPTION (pfile, pedantic)
+      || token->val.name.len != sizeof (var_args_str) - 1
+      || ustrncmp (token->val.name.text, var_args_str,
+		   sizeof (var_args_str) - 1))
+    return 0;
+
+  cpp_pedwarn_with_line (pfile, token->line, token->col,
+       "\"%s\" is only valid in the replacement list of a function-like macro",
+		       var_args_str);
+  return 1;
+}
+
 /* Counts the parameters to a function like macro, and saves their
    spellings if necessary.  Returns the token that we stopped scanning
    at; if it's type isn't CPP_CLOSE_PAREN there was an error, which
@@ -208,7 +229,6 @@ count_params (pfile, first, list)
      cpp_toklist *list;
 {
   unsigned int params_len = 0, prev_ident = 0;
-  unsigned int line = pfile->token_list.line;
   const cpp_token *token, *temp;
 
   list->paramc = 0;
@@ -217,7 +237,8 @@ count_params (pfile, first, list)
       switch (token->type)
 	{
 	case CPP_EOF:
-	  cpp_error_with_line (pfile, line, token->col,
+	missing_paren:
+	  cpp_error_with_line (pfile, token->line, token->col,
 			       "missing ')' in macro parameter list");
 	  goto out;
 
@@ -227,21 +248,14 @@ count_params (pfile, first, list)
 	case CPP_NAME:
 	  if (prev_ident)
 	    {
-	      cpp_error_with_line (pfile, line, token->col,
+	      cpp_error_with_line (pfile, token->line, token->col,
 			   "macro parameters must be comma-separated");
 	      goto out;
 	    }
 
 	  /* Constraint 6.10.3.5  */
-	  if (token->val.name.len == sizeof (var_args_str) - 1
-	      && !ustrncmp (token->val.name.text, var_args_str,
-			    sizeof (var_args_str) - 1))
-	    {
-	      cpp_error_with_line (pfile, line, token->col,
-				   "\"%s\" is not a valid parameter name",
-				   var_args_str);
-	      goto out;
-	    }
+	  if (is__va_args__ (pfile, token))
+	    goto out;
 
 	  params_len += token->val.name.len + 1;
 	  prev_ident = 1;
@@ -250,7 +264,7 @@ count_params (pfile, first, list)
 	  /* Constraint 6.10.3.6 - duplicate parameter names.  */
 	  if (find_param (first, token))
 	    {
-	      cpp_error_with_line (pfile, line, token->col,
+	      cpp_error_with_line (pfile, token->line, token->col,
 				   "duplicate macro parameter \"%.*s\"",
 				   (int) token->val.name.len,
 				   token->val.name.text);
@@ -259,7 +273,7 @@ count_params (pfile, first, list)
 	  break;
 
 	default:
-	  cpp_error_with_line (pfile, line, token->col,
+	  cpp_error_with_line (pfile, token->line, token->col,
 			       "illegal token in macro parameter list");
 	  goto out;
 
@@ -271,8 +285,10 @@ count_params (pfile, first, list)
 	case CPP_COMMA:
 	  if (!prev_ident)
 	    {
-	      cpp_error_with_line (pfile, line, token->col,
-				   "missing parameter name");
+	      cpp_error_with_line (pfile, token->line, token->col,
+				   "parameter name expected");
+	      if (token->type == CPP_CLOSE_PAREN)
+		token--;		/* Return the ',' not ')'.  */
 	      goto out;
 	    }
 	  prev_ident = 0;
@@ -293,22 +309,24 @@ count_params (pfile, first, list)
 	      tok->val.name.len = sizeof (var_args_str) - 1;
 	      tok->val.name.text = var_args_str; /* Safe.  */
 	      list->paramc++;
-
+	  
 	      if (CPP_PEDANTIC (pfile) && ! CPP_OPTION (pfile, c99))
 		cpp_pedwarn (pfile,
 			     "C89 does not permit anon varargs macros");
 	    }
-	  else if (CPP_PEDANTIC (pfile))
-	    cpp_pedwarn (pfile,
-			 "ISO C does not permit named varargs parameters");
+	  else
+	    {
+	      list->flags |= GNU_REST_ARGS;
+	      if (CPP_PEDANTIC (pfile))
+		cpp_pedwarn (pfile,
+			     "ISO C does not permit named varargs parameters");
+	    }
 
 	  list->flags |= VAR_ARGS;
 	  token++;
 	  if (token->type == CPP_CLOSE_PAREN)
 	    goto scanned;
-	  cpp_error_with_line (pfile, line, token->col,
-			       "')' expected after \"...\"");
-	  goto out;
+	  goto missing_paren;
 	}
     }
 
@@ -344,9 +362,16 @@ parse_define (pfile)
   const cpp_token *token, *first_param;
   cpp_toklist *list;
   int prev_white = 0;
+
+  /* The first token after the macro's name.  */
+  token = cpp_get_token (pfile);
 
-  while ((token = cpp_get_token (pfile))->type == CPP_COMMENT)
-    prev_white = 1;
+  /* Constraint 6.10.3.5  */
+  if (is__va_args__ (pfile, token - 1))
+    return 0;
+
+  while (token->type == CPP_COMMENT)
+    token++, prev_white = 1;
 
   /* Allocate the expansion's list.  It will go in the hash table.  */
   list = (cpp_toklist *) xmalloc (sizeof (cpp_toklist));
@@ -461,6 +486,12 @@ save_expansion (pfile, list, first, firs
 		}
 	    }
 	}
+      else if (token->type == CPP_NAME)
+	{
+	  /* Constraint 6.10.3.5  */
+	  if (!(list->flags & VAR_ARGS) && is__va_args__ (pfile, token))
+	    return 1;
+	}
       ntokens++;
       if (token_spellings[token->type].type > SPELL_NONE)
 	len += token->val.name.len;
@@ -497,10 +528,6 @@ save_expansion (pfile, list, first, firs
 	    dest->flags = token[-1].flags | STRINGIFY_ARG;
 	  else
 	    dest->flags = token->flags;  /* Particularly PREV_WHITE.  */
-
-	  if ((int) param_no == list->paramc && list->flags & VAR_ARGS
-	      && dest != list->tokens && dest[-1].flags & PASTE_LEFT)
-	    dest[-1].flags |= GNU_VARARGS;
 	  dest++;
 	  continue;
 
Index: cpplex.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cpplex.c,v
retrieving revision 1.62
diff -u -p -r1.62 cpplex.c
--- cpplex.c	2000/07/07 14:29:03	1.62
+++ cpplex.c	2000/07/08 01:40:09
@@ -2222,9 +2222,9 @@ parse_args (pfile, hp, args)
 	 e.g. #define debug(format, args...) ...
 	 debug("string");
 	 This is exactly the same as if the rest argument had received no
-	 tokens - debug("string",);  */
+	 tokens - debug("string",);  This extension is deprecated.  */
 	
-      if (argc + 1 == macro->paramc && (macro->flags & VAR_ARGS))
+      if (argc + 1 == macro->paramc && (macro->flags & GNU_REST_ARGS))
 	{
 	  /* Duplicate the placemarker.  Then we can set its flags and
              position and safely be using more than one.  */
@@ -2525,15 +2525,19 @@ maybe_paste_with_next (pfile, token)
   second = cpp_get_token (pfile);
   pfile->paste_level = 0;
 
-  /* Ignore placemarker argument tokens.  */
+  /* Ignore placemarker argument tokens (cannot be from an empty macro
+     since macros are not expanded).  */
   if (token->type == CPP_PLACEMARKER)
      pasted = duplicate_token (pfile, second);
   else if (second->type == CPP_PLACEMARKER)
     {
+      cpp_context *mac_context = CURRENT_CONTEXT (pfile) - 1;
       /* GCC has special extended semantics for a ## b where b is a
 	 varargs parameter: a disappears if b consists of no tokens.
 	 This extension is deprecated.  */
-      if (token->flags & GNU_VARARGS)
+      if ((mac_context->u.list->flags & GNU_REST_ARGS)
+	  && (mac_context->u.list->tokens[mac_context->posn - 1].val.aux + 1
+	      == (unsigned) mac_context->u.list->paramc))
 	{
 	  cpp_warning (pfile, "deprecated GNU ## extension used");
 	  pasted = duplicate_token (pfile, second);
Index: cpplib.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cpplib.h,v
retrieving revision 1.104
diff -u -p -r1.104 cpplib.h
--- cpplib.h	2000/07/05 05:33:56	1.104
+++ cpplib.h	2000/07/08 01:40:10
@@ -158,7 +158,6 @@ struct cpp_name
 #define STRINGIFY_ARG	(1 << 3) /* If macro argument to be stringified.  */
 #define PASTE_LEFT	(1 << 4) /* If on LHS of a ## operator.  */
 #define PASTED		(1 << 5) /* The result of a ## operator.  */
-#define GNU_VARARGS	(1 << 6) /* GNU ## kludge.   */
 
 /* A preprocessing token.  This has been carefully packed and should
    occupy 16 bytes on 32-bit hosts and 24 bytes on 64-bit hosts.  */
@@ -181,7 +180,8 @@ struct cpp_token
 /* cpp_toklist flags.  */
 #define LIST_OFFSET     (1 << 0)
 #define VAR_ARGS	(1 << 1)
-#define BEG_OF_FILE	(1 << 2)
+#define GNU_REST_ARGS	(1 << 2) /* Set in addition to VAR_ARGS.  */
+#define BEG_OF_FILE	(1 << 3)
 
 struct directive;		/* These are deliberately incomplete.  */
 struct answer;
Index: testsuite/gcc.dg/cpp/macsyntx.c
===================================================================
RCS file: macsyntx.c
diff -N macsyntx.c
--- /dev/null	Tue May  5 13:32:27 1998
+++ macsyntx.c	Fri Jul  7 18:40:47 2000
@@ -0,0 +1,72 @@
+/* Copyright (C) 2000 Free Software Foundation, Inc.  */
+
+/* { dg-do preprocess } */
+/* { dg-options -pedantic } */
+
+/* Tests macro syntax, for both definition and invocation, including:-
+
+   o Full range of macro definition semantics.
+   o No. of arguments supplied to function-like macros.
+   o Odd GNU rest args behaviour.
+   o Macro arguments do not flow into the rest of the file.  */
+
+
+/* Test basic macro definition syntax.  The macros are all called
+   "foo" deliberately to provoke an (excess) redefinition warning in
+   case the macros succeed in being entered in the macro hash table
+   despite being an error.
+
+   Split a couple of the lines to check that the errors appear on the
+   right line (i.e. are associated with the correct token).  */
+
+#define ;			/* { dg-error "identifier" } */
+#define SEMI;			/* { dg-warning "space" } */
+#define foo(X			/* { dg-error "missing" } */
+#define foo\
+(X,)				/* { dg-error "parameter name" } */
+#define foo(, X)		/* { dg-error "parameter name" } */
+#define foo(X, X)		/* { dg-error "duplicate" } */
+#define foo(X Y)		/* { dg-error "comma" } */
+#define foo(()			/* { dg-error "illegal token" } */
+#define foo(..., X)		/* { dg-error "missing" } */
+#define foo \
+__VA_ARGS__			/* { dg-warning "__VA_ARGS__" } */
+#define foo(__VA_ARGS__)	/* { dg-warning "__VA_ARGS__" } */
+#define foo(...) __VA_ARGS__	/* OK.  */
+#define __VA_ARGS__		/* { dg-warning "__VA_ARGS__" } */
+
+/* test # of supplied arguments.  */
+#define none()
+#define one(x)
+#define two(x, y)
+#define var0(...)
+#define var1(x, ...)
+none()				/* OK.  */
+none(ichi)			/* { dg-error "too many" } */
+one()				/* OK.  */
+one(ichi)			/* OK.  */
+one(ichi\
+, ni)				/* { dg-error "too many" } */
+two(ichi)			/* { dg-error "insufficient" } */
+var0()				/* OK.  */
+var0(ichi)			/* OK.  */
+var1()				/* { dg-error "insufficient" } */
+var1(ichi)			/* { dg-error "insufficient" } */
+var1(ichi, ni)			/* OK.  */
+
+/* This tests two deprecated oddities of GNU rest args - omitting a
+   comma is OK, and backtracking a token on pasting an empty rest
+   args.  */
+#define rest(x, y...) x ## y
+rest(ichi,)			/* { dg-warning "deprecated" } */
+rest(ichi)			/* { dg-warning "deprecated" } */
+#if 23 != rest(2, 3)		/* OK, no warning.  */
+#error 23 != 23 !!
+#endif
+
+/* Test that we don't allow arguments to flow into the rest of the
+   file.  */
+#define half_invocation do_nowt(2
+#define do_nowt(x) x
+half_invocation )		/* OK.  */
+do_nowt (half_invocation))	/* { dg-error "unterminated invocation" } */

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]