On 10/04/2017 10:44 AM, Jason Merrill wrote:

Hmm, I don't think we want to diagnose these; if a parameter-list
follows the parenthesized declarator, it isn't ambiguous.

True.  I went with that approach

3 and 4 seem like false positives.  The problematic cases are all ones
where the parenthesized name is at the end of the declarator; if the
declarator continues after the name, either within or without the
parens, this

I went with this approach.  For an array declarator of the form:
  T (ary...[X]);
we only warn when the '(' and ')' are on the same line.

Thus no triggers on GCC's source base (other than the earlier two I already fixed).

Applying the attached

nathan
--
Nathan Sidwell
2017-10-05  Nathan Sidwell  <nat...@acm.org>

	gcc/cp/
	Warn on MVP declarations
	* cp-tree.h (struct cp_declarator): Add parenthesized field.
	* decl.c (grokdeclarator): Warn about unnecessary parens.
	* parser.c (make_declarator): Init parenthesized field.
	(cp_parser_direct_declarator): Set parenthesized field.

	gcc/
	* doc/invoke.texi (Wparentheses): Document C++ MVP behaviour.

	gcc/testsuite/
	* g++.dg/warn/mvp.C: New.

Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	(revision 253445)
+++ gcc/cp/cp-tree.h	(working copy)
@@ -5671,6 +5671,10 @@ struct cp_declarator {
   /* Whether we parsed an ellipsis (`...') just before the declarator,
      to indicate this is a parameter pack.  */
   BOOL_BITFIELD parameter_pack_p : 1;
+  /* If this declarator is parenthesized, this the open-paren.  It is
+     UNKNOWN_LOCATION when not parenthesized.  */
+  location_t parenthesized;
+
   location_t id_loc; /* Currently only set for cdk_id, cdk_decomp and
 			cdk_function. */
   /* GNU Attributes that apply to this declarator.  If the declarator
Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(revision 253445)
+++ gcc/cp/decl.c	(working copy)
@@ -10807,6 +10807,13 @@ grokdeclarator (const cp_declarator *dec
 					    attr_flags);
 	}
 
+      /* We don't want to warn in parmeter context because we don't
+	 yet know if the parse will succeed, and this might turn out
+	 to be a constructor call.  */
+      if (decl_context != PARM
+	  && declarator->parenthesized != UNKNOWN_LOCATION)
+	warning_at (declarator->parenthesized, OPT_Wparentheses,
+		    "unnecessary parentheses in declaration of %qs", name);
       if (declarator->kind == cdk_id || declarator->kind == cdk_decomp)
 	break;
 
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 253445)
+++ gcc/cp/parser.c	(working copy)
@@ -1451,6 +1451,7 @@ make_declarator (cp_declarator_kind kind
 
   declarator = (cp_declarator *) alloc_declarator (sizeof (cp_declarator));
   declarator->kind = kind;
+  declarator->parenthesized = UNKNOWN_LOCATION;
   declarator->attributes = NULL_TREE;
   declarator->std_attributes = NULL_TREE;
   declarator->declarator = NULL;
@@ -19808,6 +19809,7 @@ cp_parser_direct_declarator (cp_parser*
   bool saved_in_declarator_p = parser->in_declarator_p;
   bool first = true;
   tree pushed_scope = NULL_TREE;
+  cp_token *open_paren = NULL, *close_paren = NULL;
 
   while (true)
     {
@@ -19858,6 +19860,8 @@ cp_parser_direct_declarator (cp_parser*
 	      tree params;
 	      bool is_declarator = false;
 
+	      open_paren = NULL;
+
 	      /* In a member-declarator, the only valid interpretation
 		 of a parenthesis is the start of a
 		 parameter-declaration-clause.  (It is invalid to
@@ -19979,6 +19983,7 @@ cp_parser_direct_declarator (cp_parser*
 	      parser->default_arg_ok_p = saved_default_arg_ok_p;
 	      parser->in_declarator_p = saved_in_declarator_p;
 
+	      open_paren = token;
 	      /* Consume the `('.  */
 	      matching_parens parens;
 	      parens.consume_open (parser);
@@ -19992,6 +19997,7 @@ cp_parser_direct_declarator (cp_parser*
 	      parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
 	      first = false;
 	      /* Expect a `)'.  */
+	      close_paren = cp_lexer_peek_token (parser->lexer);
 	      if (!parens.require_close (parser))
 		declarator = cp_error_declarator;
 	      if (declarator == cp_error_declarator)
@@ -20013,6 +20019,7 @@ cp_parser_direct_declarator (cp_parser*
 	  if (ctor_dtor_or_conv_p)
 	    *ctor_dtor_or_conv_p = 0;
 
+	  open_paren = NULL;
 	  first = false;
 	  parser->default_arg_ok_p = false;
 	  parser->in_declarator_p = true;
@@ -20308,6 +20315,22 @@ cp_parser_direct_declarator (cp_parser*
      point.  That's an error; the declarator is not optional.  */
   if (!declarator)
     cp_parser_error (parser, "expected declarator");
+  else if (open_paren)
+    {
+      /* Record overly parenthesized declarator so we can give a
+	 diagnostic about confusing decl/expr disambiguation.  */
+      if (declarator->kind == cdk_array)
+	{
+	  /* If the open and close parens are on different lines, this
+	     is probably a formatting thing, so ignore.  */
+	  expanded_location open = expand_location (open_paren->location);
+	  expanded_location close = expand_location (close_paren->location);
+	  if (open.line != close.line || open.file != close.file)
+	    open_paren = NULL;
+	}
+      if (open_paren)
+	declarator->parenthesized = open_paren->location;
+    }
 
   /* If we entered a scope, we must exit it now.  */
   if (pushed_scope)
Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi	(revision 253445)
+++ gcc/doc/invoke.texi	(working copy)
@@ -4579,6 +4579,17 @@ in the @code{?}: operator is a boolean e
 always 1.  Often programmers expect it to be a value computed
 inside the conditional expression instead.
 
+For C++ this also warns for some cases of unnecessary parentheses in
+declarations, which can indicate an attempt at a function call instead
+of a declaration:
+@smallexample
+@{
+  // Declares a local variable called mymutex.
+  std::unique_lock<std::mutex> (mymutex);
+  // User meant std::unique_lock<std::mutex> lock (mymutex);
+@}
+@end smallexample
+
 This warning is enabled by @option{-Wall}.
 
 @item -Wsequence-point
Index: gcc/testsuite/g++.dg/warn/mvp.C
===================================================================
--- gcc/testsuite/g++.dg/warn/mvp.C	(revision 0)
+++ gcc/testsuite/g++.dg/warn/mvp.C	(working copy)
@@ -0,0 +1,78 @@
+// { dg-additional-options -Wparentheses }
+
+// Most Vexing Parse warnings
+// in C++ anythig that syntactically looks like a decl IS a decl, this
+// can lead to confused users, but worse silent unexpectedly unsafe
+// code generation.
+
+int (a); // { dg-warning "" }
+int (*b);  // { dg-warning "" }
+extern int (&c);  // { dg-warning "" }
+
+int h1 = 0, h2 = 0;
+struct H { H(...);};
+
+namespace fns 
+{
+  int (*a) ();
+  int (b) ();
+  int (*c ()) ();
+  int (d1 ()); // { dg-warning "" }
+  int (d2 // { dg-warning "" }
+       ());
+  int (e) (int);
+  int g (int (a)); // No warning because ...
+  H h (int (h1), int (h2), 3); // ... not a function decl.
+}
+
+namespace arys
+{
+  int (*a)[1];
+  int (b)[1];
+  int (*c[1])[1];
+  int (d1[1]); // { dg-warning "" }
+  int (d2
+       [1]);
+  int (e[1])[1];
+}
+
+namespace complex
+{
+  int (*a())[1];
+  int (*b[1])();
+  int ((*c1())[1]); // { dg-warning "" }
+  int ((*c2())
+       [1]);
+  int ((*d1[1])()); // { dg-warning "" }
+  int ((*d2[1])	 // { dg-warning "" }
+       ());
+}
+
+namespace motivation
+{
+  typedef int shared_mutex; // for exposition
+  struct locker
+  {
+    locker ();
+    locker (int &r);
+    ~locker ();
+  };
+  class protected_state 
+  {
+    shared_mutex mutex; // not a real mutex type
+    int state;
+
+  public:
+    void not_thread_safe ()
+    {
+      locker (mutex); // { dg-warning "" }
+      state++; // oops
+    }
+    
+    void thread_safe ()
+    {
+      locker lock (mutex);
+      state++; // ok;
+    }
+  };
+}

Reply via email to