C-family, Objective-C [1/3] : Implement Wobjc-root-class [PR77404].
authorIain Sandoe <iain@sandoe.co.uk>
Sun, 25 Oct 2020 07:49:16 +0000 (07:49 +0000)
committerIain Sandoe <iain@sandoe.co.uk>
Fri, 13 Nov 2020 10:39:48 +0000 (10:39 +0000)
This warning catches the case that the user has left the
superclass specification from a class interface.  Root
classes are, of course, permitted and an attribute is added
to mark these so that the diagnostic is suppressed.

The warning and attribute spellings have been kept in sync
with the language reference implementation (clang).

The diagnostic location information present in the objective-c
interface and class definitions is relatively poor.  This patch
adds a location for the class name to the interface and makes use
of it in existing warnings.

Part 1 is the changes to code and added tests.

Many entries in the testsuite make use of root classes so
there are a large number of mechanical changes there adding
"-Wno-objc-root-class" to the options.

The test changes are parts 2 (objective-c) and 3 (objective-c++)
in the patch series.

gcc/c-family/ChangeLog:

PR objc/77404
* c-attribs.c (handle_objc_root_class_attribute): New
* c-objc.h (objc_start_class_interface): Add a location
value for the position of the class name.
* c.opt: Add Wobjc-root-class.
* stub-objc.c (objc_start_class_interface): Add a location
value for the position of the class name.

gcc/c/ChangeLog:

PR objc/77404
* c-parser.c (c_parser_objc_class_definition): Pass the
location of the class name to the interface declaration.

gcc/cp/ChangeLog:

PR objc/77404
* parser.c (cp_parser_objc_class_interface): Pass the
location of the class name to the interface declaration.

gcc/objc/ChangeLog:

PR objc/77404
* objc-act.c (objc_start_class_interface): Accept the location
of the class name, use it in existing diagnostic.
(start_class): Accept obj_root_class type attributes.  Warn when
the interface for an implementation does not contain a super
class (unless the diagnostic is suppressed by the the command
line flag or the objc_root_class type attribute).

gcc/testsuite/ChangeLog:

PR objc/77404
* objc.dg/attributes/root-class-01.m: New test.
* objc.dg/root-class-00.m: New test.
* obj-c++.dg/attributes/root-class-01.mm: New test.
* obj-c++.dg/root-class-00.mm: New test.

gcc/ChangeLog:

PR objc/77404
* doc/extend.texi: Document the objc_root_class attribute.
* doc/invoke.texi: Document -Wobjc-root-class.

13 files changed:
gcc/c-family/c-attribs.c
gcc/c-family/c-objc.h
gcc/c-family/c.opt
gcc/c-family/stub-objc.c
gcc/c/c-parser.c
gcc/cp/parser.c
gcc/doc/extend.texi
gcc/doc/invoke.texi
gcc/objc/objc-act.c
gcc/testsuite/obj-c++.dg/attributes/root-class-01.mm [new file with mode: 0644]
gcc/testsuite/obj-c++.dg/root-class-00.mm [new file with mode: 0644]
gcc/testsuite/objc.dg/attributes/root-class-01.m [new file with mode: 0644]
gcc/testsuite/objc.dg/root-class-00.m [new file with mode: 0644]

index f1680820ecd6a2761939a196955ffaad003018c3..24bcd70b26cb0c4afb453b8202c29dec046edb13 100644 (file)
@@ -158,6 +158,7 @@ static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
                                                       int, bool *);
 static tree handle_copy_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nsobject_attribute (tree *, tree, tree, int, bool *);
+static tree handle_objc_root_class_attribute (tree *, tree, tree, int, bool *);
 
 /* Helper to define attribute exclusions.  */
 #define ATTR_EXCL(name, function, type, variable)      \
@@ -513,6 +514,8 @@ const struct attribute_spec c_common_attribute_table[] =
   /* Attributes used by Objective-C.  */
   { "NSObject",                      0, 0, true, false, false, false,
                              handle_nsobject_attribute, NULL },
+  { "objc_root_class",       0, 0, true, false, false, false,
+                             handle_objc_root_class_attribute, NULL },
   { NULL,                     0, 0, false, false, false, false, NULL, NULL }
 };
 
@@ -5163,6 +5166,22 @@ handle_nsobject_attribute (tree *node, tree name, tree args,
   return NULL_TREE;
 }
 
+/* Handle a "objc_root_class" attributes; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_objc_root_class_attribute (tree */*node*/, tree name, tree /*args*/,
+                                 int /*flags*/, bool *no_add_attrs)
+{
+  /* This has no meaning outside Objective-C.  */
+  if (!c_dialect_objc())
+    warning (OPT_Wattributes, "%qE is only applicable to Objective-C"
+            " class interfaces, attribute ignored", name);
+
+  *no_add_attrs = true;
+  return NULL_TREE;
+}
+
 /* Attempt to partially validate a single attribute ATTR as if
    it were to be applied to an entity OPER.  */
 
index 6e96731882b02163673460b68228c724ddcf1046..9414aecc668a7d9d537c92c2a6fe5ef23c63431d 100644 (file)
@@ -124,7 +124,7 @@ extern tree objc_get_protocol_qualified_type (tree, tree);
 extern tree objc_get_class_reference (tree);
 extern tree objc_get_class_ivars (tree);
 extern bool objc_detect_field_duplicates (bool);
-extern void objc_start_class_interface (tree, tree, tree, tree);
+extern void objc_start_class_interface (tree, location_t, tree, tree, tree);
 extern void objc_start_category_interface (tree, tree, tree, tree);
 extern void objc_start_protocol (tree, tree, tree);
 extern void objc_continue_interface (void);
index fe16357db85c88ea42cf00715b72c606b6079064..a0083636aed4375080526c47db7c8142ca37f5f0 100644 (file)
@@ -1002,6 +1002,11 @@ Enum(cpp_normalize_level) String(id) Value(normalized_identifier_C)
 EnumValue
 Enum(cpp_normalize_level) String(nfc) Value(normalized_C)
 
+Wobjc-root-class
+ObjC ObjC++ Var(warn_objc_root_class) Warning Init(1)
+Warn if a class interface has no superclass.  Root classes may use an attribute
+to suppress this warning.
+
 Wold-style-cast
 C++ ObjC++ Var(warn_old_style_cast) Warning
 Warn if a C-style cast is used in a program.
index 2f535578789712c987d457cbdb319f8c9d3d8aef..26941aa2cb4cd8e881ea706b79f701f345f4b81e 100644 (file)
@@ -137,6 +137,7 @@ objc_set_method_opt (bool ARG_UNUSED (optional))
 
 void
 objc_start_class_interface (tree ARG_UNUSED (name),
+                           location_t /*name_loc*/,
                            tree ARG_UNUSED (super),
                            tree ARG_UNUSED (protos),
                            tree ARG_UNUSED (attribs))
index 377914cad165f5bb43cdf6a37b922301fc7d7e3f..f4c4cf7bf8f9a1e56fd1133ec625dd8cff9bf606 100644 (file)
@@ -10801,6 +10801,7 @@ c_parser_objc_class_definition (c_parser *parser, tree attributes)
       return;
     }
   id1 = c_parser_peek_token (parser)->value;
+  location_t loc1 = c_parser_peek_token (parser)->location;
   c_parser_consume_token (parser);
   if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
     {
@@ -10860,7 +10861,7 @@ c_parser_objc_class_definition (c_parser *parser, tree attributes)
       tree proto = NULL_TREE;
       if (c_parser_next_token_is (parser, CPP_LESS))
        proto = c_parser_objc_protocol_refs (parser);
-      objc_start_class_interface (id1, superclass, proto, attributes);
+      objc_start_class_interface (id1, loc1, superclass, proto, attributes);
     }
   else
     objc_start_class_implementation (id1, superclass);
index efcdce0477732f1c6412d9efa15c5c217efd58de..6b0447ea496b4659df0e517a5c41195385376144 100644 (file)
@@ -33574,6 +33574,7 @@ cp_parser_objc_class_interface (cp_parser* parser, tree attributes)
   bool is_class_extension;
 
   cp_lexer_consume_token (parser->lexer);  /* Eat '@interface'.  */
+  location_t nam_loc = cp_lexer_peek_token (parser->lexer)->location;
   name = cp_parser_identifier (parser);
   if (name == error_mark_node)
     {
@@ -33593,7 +33594,7 @@ cp_parser_objc_class_interface (cp_parser* parser, tree attributes)
     objc_start_category_interface (name, categ, protos, attributes);
   else
     {
-      objc_start_class_interface (name, super, protos, attributes);
+      objc_start_class_interface (name, nam_loc, super, protos, attributes);
       /* Handle instance variable declarations, if any.  */
       cp_parser_objc_class_ivars (parser);
       objc_continue_interface ();
index 420a14b66b065464af08c2dd41df8422e8c26b4a..c353eb432b961512500e1d0b7c9928b3cef342da 100644 (file)
@@ -8518,6 +8518,12 @@ and caught in another, the class must have default visibility.
 Otherwise the two shared objects are unable to use the same
 typeinfo node and exception handling will break.
 
+@item objc_root_class @r{(Objective-C and Objective-C++ only)}
+@cindex @code{objc_root_class} type attribute
+This attribute marks a class as being a root class, and thus allows
+the compiler to elide any warnings about a missing superclass and to
+make additional checks for mandatory methods as needed.
+
 @end table
 
 To specify multiple attributes, separate them by commas within the
index 69bf1fa89dd390635b5084b049664675c179a9f5..85f7969d87f86522c4eec1599453a8970c51e9f7 100644 (file)
@@ -276,7 +276,7 @@ Objective-C and Objective-C++ Dialects}.
 -fzero-link @gol
 -gen-decls @gol
 -Wassign-intercept  -Wno-property-assign-default @gol
--Wno-protocol  -Wselector @gol
+-Wno-protocol -Wobjc-root-class -Wselector @gol
 -Wstrict-selector-match @gol
 -Wundeclared-selector}
 
@@ -4348,6 +4348,13 @@ from the superclass.  If you use the @option{-Wno-protocol} option, then
 methods inherited from the superclass are considered to be implemented,
 and no warning is issued for them.
 
+@item -Wobjc-root-class @r{(Objective-C and Objective-C++ only)}
+@opindex Wobjc-root-class
+Warn if a class interface lacks a superclass. Most classes will inherit
+from @code{NSObject} (or @code{Object}) for example.  When declaring
+classes intended to be root classes, the warning can be suppressed by
+marking their interfaces with @code{__attribute__((objc_root_class))}.
+
 @item -Wselector @r{(Objective-C and Objective-C++ only)}
 @opindex Wselector
 @opindex Wno-selector
index b9ed32d51d07ea1d421d49f66dc1b0b9cc10cc1f..e4103860aae98b24745c7239d00b0124eebc7240 100644 (file)
@@ -571,11 +571,11 @@ lookup_protocol_in_reflist (tree rproto_list, tree lproto)
 }
 
 void
-objc_start_class_interface (tree klass, tree super_class,
+objc_start_class_interface (tree klass, location_t name_loc, tree super_class,
                            tree protos, tree attributes)
 {
   if (flag_objc1_only && attributes)
-    error_at (input_location, "class attributes are not available in Objective-C 1.0");
+    error_at (name_loc, "class attributes are not available in Objective-C 1.0");
 
   objc_interface_context
     = objc_ivar_context
@@ -7014,6 +7014,12 @@ start_class (enum tree_code code, tree class_name, tree super_name,
          CLASS_SUPER_NAME (objc_implementation_context)
            = CLASS_SUPER_NAME (implementation_template);
        }
+
+      if (!CLASS_SUPER_NAME (objc_implementation_context)
+         && !lookup_attribute ("objc_root_class",
+                               TYPE_ATTRIBUTES (implementation_template)))
+         warning (OPT_Wobjc_root_class, "class %qE defined without"
+                     " specifying a base class", class_name);
       break;
 
     case CLASS_INTERFACE_TYPE:
@@ -7044,6 +7050,8 @@ start_class (enum tree_code code, tree class_name, tree super_name,
                TREE_DEPRECATED (klass) = 1;
              else if (is_attribute_p  ("objc_exception", name))
                CLASS_HAS_EXCEPTION_ATTR (klass) = 1;
+             else if (is_attribute_p  ("objc_root_class", name))
+               ;
              else if (is_attribute_p  ("visibility", name))
                ;
              else
diff --git a/gcc/testsuite/obj-c++.dg/attributes/root-class-01.mm b/gcc/testsuite/obj-c++.dg/attributes/root-class-01.mm
new file mode 100644 (file)
index 0000000..84da94a
--- /dev/null
@@ -0,0 +1,11 @@
+/* Test Wobjc-root-class warning is suppressed by the objc_root_class attr.
+   Note that we don't issue a warning unless the TU contains an implementation
+   for the class.  This should compile without warning.  */
+/*  { dg-additional-options "-fsyntax-only " } */
+
+__attribute__((objc_root_class))
+@interface ARootObject
+@end
+
+@implementation ARootObject
+@end
diff --git a/gcc/testsuite/obj-c++.dg/root-class-00.mm b/gcc/testsuite/obj-c++.dg/root-class-00.mm
new file mode 100644 (file)
index 0000000..f951b0d
--- /dev/null
@@ -0,0 +1,10 @@
+/* Test Wobjc-root-class.
+   Note that we don't issue a warning unless the TU contains an implementation
+   for the class.  */
+/*  { dg-additional-options "-fsyntax-only " } */
+
+@interface ARootObject
+@end
+
+@implementation ARootObject /* { dg-warning {class 'ARootObject' defined without specifying a base class} } */
+@end
diff --git a/gcc/testsuite/objc.dg/attributes/root-class-01.m b/gcc/testsuite/objc.dg/attributes/root-class-01.m
new file mode 100644 (file)
index 0000000..84da94a
--- /dev/null
@@ -0,0 +1,11 @@
+/* Test Wobjc-root-class warning is suppressed by the objc_root_class attr.
+   Note that we don't issue a warning unless the TU contains an implementation
+   for the class.  This should compile without warning.  */
+/*  { dg-additional-options "-fsyntax-only " } */
+
+__attribute__((objc_root_class))
+@interface ARootObject
+@end
+
+@implementation ARootObject
+@end
diff --git a/gcc/testsuite/objc.dg/root-class-00.m b/gcc/testsuite/objc.dg/root-class-00.m
new file mode 100644 (file)
index 0000000..1f73f73
--- /dev/null
@@ -0,0 +1,10 @@
+/* Test Wobjc-root-class.
+   Note that we don't issue a warning unless the TU contains an implementation
+   for the class.  */
+/*  { dg-additional-options "-fsyntax-only " } */
+
+@interface ARootObject
+@end
+
+@implementation ARootObject
+@end /* { dg-warning {class 'ARootObject' defined without specifying a base class} } */