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]
Other format: [Raw text]

Re: [PATCH] New C++ warning option '-Wduplicated-access-specifiers'


On Thu, 2017-07-20 at 18:35 +0200, Volker Reichelt wrote:
> Hi,
> 
> the following patch introduces a new C++ warning option
> -Wduplicated-access-specifiers that warns about redundant
> access-specifiers in classes, e.g.
> 
>   class B
>   {
>     public:
>       B();
> 
>     private:
>       void foo();
>     private:
>       int i;
>   };
> 
> test.cc:8:5: warning: duplicate 'private' access-specifier [
> -Wduplicated-access-specifiers]
>      private:
>      ^~~~~~~
>      -------
> test.cc:6:5: note: access-specifier was previously given here
>      private:
>      ^~~~~~~

Thanks for working on this.

I'm not able to formally review, but you did CC me; various comments below throughout.

> The test is implemented by tracking the location of the last
> access-specifier together with the access-specifier itself.
> The location serves two purposes: the obvious one is to be able to
> print the location in the note that comes with the warning.
> The second purpose is to be able to distinguish an access-specifier
> given by the user from the default of the class type (i.e. public for
> 'struct' and private for 'class') where the location is set to
> UNKNOWN_LOCATION. The warning is only issued if the user gives the
> access-specifier twice, i.e. if the previous location is not
> UNKNOWN_LOCATION.

Presumably given

struct foo
{
public:
  /* ... *
};

we could issue something like:

  warning: access-specifier 'public' is redundant within 'struct'

for the first; similarly for:

class bar
{
private:
};

we could issue:

  warning: access-specifier 'private' is redundant within 'class'


> One could actually make this a two-level warning so that on the
> higher level also the default class-type settings are taken into
> account. Would that be helpful? I could prepare a second patch for
> that.

I'm not sure how best to structure it.

FWIW, when I first saw the patch, I wasn't a big fan of the warning, as I like to use access-specifiers to break up the kinds of entities within a class.

For example, our coding guidelines 
  https://gcc.gnu.org/codingconventions.html#Class_Form
recommend:

"first define all public types,
then define all non-public types,
then declare all public constructors,
...
then declare all non-public member functions, and
then declare all non-public member variables."

I find it's useful to put a redundant "private:" between the last two,
e.g.:

class baz
{
 public:
  ...

 private:
  void some_private_member ();

 private:
  int m_some_field;
};

to provide a subdivision between the private member functions and the
private data fields.

This might be a violation of our guidelines (though if so, I'm not sure
it's explicitly stated), but I find it useful, and the patch would warn
about it.

Having said that, looking at e.g. the "jit" subdir, I see lots of
places where the warning would fire.  In some of these, the code has a
bit of a "smell", so maybe I like the warning after all... in that it
can be good for a new warning to draw attention to code that might need
work.

Sorry that this is rambling; comments on the patch inline below.

Bootstrapped and regtested on x86_64-pc-linux-gnu.
> OK for trunk?
> 
> Btw, there are about 50 places in our libstdc++ headers where this
> warning triggers. I'll come up with a patch for this later.
> 
> Regards,
> Volker
> 
> 
> 2017-07-20  Volker Reichelt  <v.reichelt@netcologne.de>
> 
>         * doc/invoke.texi (-Wduplicated-access-specifiers): Document
> new
>         warning option.
> 
> Index: gcc/doc/invoke.texi
> ===================================================================
> --- gcc/doc/invoke.texi (revision 250356)
> +++ gcc/doc/invoke.texi (working copy)
> @@ -275,7 +275,7 @@
>  -Wdisabled-optimization @gol
>  -Wno-discarded-qualifiers  -Wno-discarded-array-qualifiers @gol
>  -Wno-div-by-zero  -Wdouble-promotion @gol
> --Wduplicated-branches  -Wduplicated-cond @gol
> +-Wduplicated-access-specifiers  -Wduplicated-branches  -Wduplicated
> -cond @gol
>  -Wempty-body  -Wenum-compare  -Wno-endif-labels  -Wexpansion-to
> -defined @gol
>  -Werror  -Werror=*  -Wextra-semi  -Wfatal-errors @gol
>  -Wfloat-equal  -Wformat  -Wformat=2 @gol
> @@ -5388,6 +5388,12 @@
>  
>  This warning is enabled by @option{-Wall}.
>  
> +@item -Wduplicated-access-specifiers
> +@opindex Wno-duplicated-access-specifiers
> +@opindex Wduplicated-access-specifiers
> +Warn when an access-specifier is redundant because it was already
> given
> +before.

Presumably this should be marked as C++-specific.

I think it's best to give an example for any warning, though we don't
do that currently.

>  @item -Wduplicated-branches
>  @opindex Wno-duplicated-branches
>  @opindex Wduplicated-branches
> ===================================================================
> 
> 
> 2017-07-20  Volker Reichelt  <v.reichelt@netcologne.de>
> 
>         * c.opt (Wduplicated-access-specifiers): New C++ warning
> flag.
> 
> Index: gcc/c-family/c.opt
> ===================================================================
> --- gcc/c-family/c.opt  (revision 250356)
> +++ gcc/c-family/c.opt  (working copy)
> @@ -476,6 +476,10 @@
>  C ObjC C++ ObjC++ Var(warn_div_by_zero) Init(1) Warning
>  Warn about compile-time integer division by zero.
>  
> +Wduplicated-access-specifiers
> +C++ ObjC++ Var(warn_duplicated_access) Warning
> +Warn about duplicated access-specifiers.
> +
>  Wduplicated-branches
>  C ObjC C++ ObjC++ Var(warn_duplicated_branches) Init(0) Warning
>  Warn about duplicated branches in if-else statements.
> ===================================================================
> 
> 
> 2017-07-20  Volker Reichelt  <v.reichelt@netcologne.de>
> 
>         * cp-tree.h (struct saved_scope): New access_specifier_loc
> variable.
>         (current_access_specifier_loc): New macro.
>         * class.c (struct class_stack_node): New access_loc variable.
>         (pushclass): Push current_access_specifier_loc.  Set new
>         initial value.
>         (popclass): Pop current_access_specifier_loc.
>         * parser.c (cp_parser_member_specification_opt): Warn about
>         duplicated access-specifier.  Set
> current_access_specifier_loc.
> 
> Index: gcc/cp/cp-tree.h
> ===================================================================
> --- gcc/cp/cp-tree.h    (revision 250356)
> +++ gcc/cp/cp-tree.h    (working copy)
> @@ -1531,6 +1531,7 @@
>    tree class_name;
>    tree class_type;
>    tree access_specifier;
> +  source_location access_specifier_loc;
>    tree function_decl;
>    vec<tree, va_gc> *lang_base;
>    tree lang_name;
> @@ -1592,6 +1593,7 @@
>     class, or union).  */
>  
>  #define current_access_specifier scope_chain->access_specifier
> +#define current_access_specifier_loc scope_chain
> ->access_specifier_loc
>  
>  /* Pointer to the top of the language name stack.  */
>  
> Index: gcc/cp/class.c
> ===================================================================
> --- gcc/cp/class.c      (revision 250356)
> +++ gcc/cp/class.c      (working copy)
> @@ -60,6 +60,7 @@
>    /* The access specifier pending for new declarations in the scope
> of
>       this class.  */
>    tree access;
> +  location_t access_loc;
>  
>    /* If were defining TYPE, the names used in this class.  */
>    splay_tree names_used;
> @@ -7723,6 +7724,7 @@
>    csn->name = current_class_name;
>    csn->type = current_class_type;
>    csn->access = current_access_specifier;
> +  csn->access_loc = current_access_specifier_loc;
>    csn->names_used = 0;
>    csn->hidden = 0;
>    current_class_depth++;
> @@ -7738,6 +7740,7 @@
>    current_access_specifier = (CLASSTYPE_DECLARED_CLASS (type)
>                               ? access_private_node
>                               : access_public_node);
> +  current_access_specifier_loc = UNKNOWN_LOCATION;
>  
>    if (previous_class_level
>        && type != previous_class_level->this_entity
> @@ -7777,6 +7780,7 @@
>    current_class_name =
> current_class_stack[current_class_depth].name;
>    current_class_type =
> current_class_stack[current_class_depth].type;
>    current_access_specifier =
> current_class_stack[current_class_depth].access;
> +  current_access_specifier_loc =
> current_class_stack[current_class_depth].access_loc;
>    if (current_class_stack[current_class_depth].names_used)
>      splay_tree_delete
> (current_class_stack[current_class_depth].names_used);
>  }
> Index: gcc/cp/parser.c
> ===================================================================
> --- gcc/cp/parser.c     (revision 250356)
> +++ gcc/cp/parser.c     (working copy)
> @@ -23113,8 +23113,24 @@
>         case RID_PRIVATE:
>           /* Consume the access-specifier.  */
>           cp_lexer_consume_token (parser->lexer);
> +
> +         /* Warn if the same access-specifier has been given
> already.  */
> +         if (warn_duplicated_access
> +             && current_access_specifier == token->u.value
> +             && current_access_specifier_loc != UNKNOWN_LOCATION)
> +           {
> +             gcc_rich_location richloc (token->location);
> +             richloc.add_fixit_remove ();

If the fix-it hint is to work, it presumably needs to remove two
tokens: both the "private" (or whatever), *and* the colon.

You can probably do this via:

  richloc.add_fixit_remove ();  /* for the primary location */
  richloc.add_fixit_remove (colon_loc);  /* for the colon */

and the rich_location ought to do the right thing, consolidating the removals if they are adjacent (and coping with e.g. a comment between them if they're not).

> +             warning_at_rich_loc (&richloc,
> OPT_Wduplicated_access_specifiers,
> +                                  "duplicate %qE access-specifier",
> +                                  token->u.value);
> +             inform (current_access_specifier_loc,
> +                     "access-specifier was previously given here");
> +           }
> +
>           /* Remember which access-specifier is active.  */
>           current_access_specifier = token->u.value;
> +         current_access_specifier_loc = token->location;
>           /* Look for the `:'.  */
>           cp_parser_require (parser, CPP_COLON, RT_COLON);
>           break;
> ===================================================================
> 
> 
> 2017-07-20  Volker Reichelt  <v.reichelt@netcologne.de>
> 
>         * g++.dg/warn/Wduplicated-access-specifiers-1.C: New test.
> 
> Index: gcc/testsuite/g++.dg/warn/Wduplicated-access-specifiers-1.C
> ===================================================================
> --- gcc/testsuite/g++.dg/warn/Wduplicated-access-specifiers-1.C 2017
> -07-20
> +++ gcc/testsuite/g++.dg/warn/Wduplicated-access-specifiers-1.C 2017
> -07-20
> @@ -0,0 +1,35 @@
> +// { dg-options "-Wduplicated-access-specifiers" }
> +
> +struct A
> +{
> +    int i;
> +  public:       // { dg-message "previously" }
> +    int j;
> +  public:       // { dg-warning "access-specifier" }
> +    int k;
> +  protected:    // { dg-message "previously" }
> +
> +    class B
> +    {
> +      private:  // { dg-message "previously" }
> +      private:  // { dg-warning "access-specifier" }
> +      public:
> +    };
> +
> +  protected:    // { dg-warning "access-specifier" }
> +    void a();
> +  public:
> +    void b();
> +  private:      // { dg-message "previously" }
> +    void c();
> +  private:      // { dg-warning "access-specifier" }
> +    void d();
> +  public:
> +    void e();
> +  protected:    // { dg-message "previously" }
> +    void f();
> +  protected:    // { dg-warning "access-specifier" }
> +                // { dg-message "previously" "" { target *-*-* } .-1
> }
> +    void g();
> +  protected:    // { dg-warning "access-specifier" }
> +};

If you're going to implement a fix-it hint for this, there should be a
test case that tests it (probably as a separate file, e.g. Wduplicated
-access-specifiers-2.C, to allow for the extra option --fdiagnostics
-show-caret, covering just one instance of the diagnostic firing, for
simplicity).

Hope this is constructive.
Dave


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