]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: adjust accessor fixits for explicit object parm
authorJason Merrill <jason@redhat.com>
Mon, 8 Jan 2024 22:09:54 +0000 (17:09 -0500)
committerJason Merrill <jason@redhat.com>
Tue, 9 Jan 2024 21:09:23 +0000 (16:09 -0500)
In a couple of places in the xobj patch I noticed that is_this_parameter
probably wanted to change to is_object_parameter; this implements that and
does the additional adjustments needed to make the accessor fixits handle
xobj parms.

gcc/cp/ChangeLog:

* semantics.cc (is_object_parameter): New.
* cp-tree.h (is_object_parameter): Declare.
* call.cc (maybe_warn_class_memaccess): Use it.
* search.cc (field_access_p): Use it.
(class_of_object_parm): New.
(field_accessor_p): Adjust for explicit object parms.

gcc/testsuite/ChangeLog:

* g++.dg/torture/accessor-fixits-9-xobj.C: New test.

gcc/cp/call.cc
gcc/cp/cp-tree.h
gcc/cp/search.cc
gcc/cp/semantics.cc
gcc/testsuite/g++.dg/torture/accessor-fixits-9-xobj.C [new file with mode: 0644]

index 7d3d67600c8f5050caff213e38aee02c16a40cba..191664ee227a0fd65f2bdc3f51d93742599e5ec2 100644 (file)
@@ -10815,8 +10815,7 @@ maybe_warn_class_memaccess (location_t loc, tree fndecl,
      be more permissive.  */
   if (current_function_decl
       && DECL_OBJECT_MEMBER_FUNCTION_P (current_function_decl)
-      /* ??? is_object_parameter?  */
-      && is_this_parameter (tree_strip_nop_conversions (dest)))
+      && is_object_parameter (tree_strip_nop_conversions (dest)))
     {
       tree ctx = DECL_CONTEXT (current_function_decl);
       bool special = same_type_ignoring_top_level_qualifiers_p (ctx, desttype);
index dbc71776be3c71fe3366f9911deef85769dda6f7..f3f265a3db83df2a64fc1ca13e106eb9a65d0b21 100644 (file)
@@ -7784,6 +7784,7 @@ extern void finish_handler_parms          (tree, tree);
 extern void finish_handler                     (tree);
 extern void finish_cleanup                     (tree, tree);
 extern bool is_this_parameter                   (tree);
+extern bool is_object_parameter                 (tree);
 
 enum {
   BCS_NORMAL = 0,
index dc76714351f9e33c40318d4d1acf2ab56a289b0b..2b4ed5d024ea4111cfbb90c108bf9d9adbe1e37a 100644 (file)
@@ -1737,8 +1737,7 @@ field_access_p (tree component_ref, tree field_decl, tree field_type)
     return false;
 
   tree ptr = STRIP_NOPS (TREE_OPERAND (indirect_ref, 0));
-  /* ??? is_object_parameter?  */
-  if (!is_this_parameter (ptr))
+  if (!is_object_parameter (ptr))
     return false;
 
   /* Must access the correct field.  */
@@ -1818,6 +1817,17 @@ reference_accessor_p (tree init_expr, tree field_decl, tree field_type,
   return true;
 }
 
+/* Return the class of the `this' or explicit object parameter of FN.  */
+
+static tree
+class_of_object_parm (const_tree fn)
+{
+  tree fntype = TREE_TYPE (fn);
+  if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+    return non_reference (TREE_VALUE (TYPE_ARG_TYPES (fntype)));
+  return class_of_this_parm (fntype);
+}
+
 /* Return true if FN is an accessor method for FIELD_DECL.
    i.e. a method of the form { return FIELD; }, with no
    conversions.
@@ -1835,15 +1845,14 @@ field_accessor_p (tree fn, tree field_decl, bool const_p)
   if (TREE_CODE (field_decl) != FIELD_DECL)
     return false;
 
-  tree fntype = TREE_TYPE (fn);
-  if (TREE_CODE (fntype) != METHOD_TYPE)
+  if (!DECL_OBJECT_MEMBER_FUNCTION_P (fn))
     return false;
 
   /* If the field is accessed via a const "this" argument, verify
      that the "this" parameter is const.  */
   if (const_p)
     {
-      tree this_class = class_of_this_parm (fntype);
+      tree this_class = class_of_object_parm (fn);
       if (!TYPE_READONLY (this_class))
        return false;
     }
index 86e1adf1e71d15c38e291ee901ca5eabf0be208d..3299e2704465b498001842f0904285be4b756e6f 100644 (file)
@@ -12832,6 +12832,20 @@ is_this_parameter (tree t)
   return true;
 }
 
+/* As above, or a C++23 explicit object parameter.  */
+
+bool
+is_object_parameter (tree t)
+{
+  if (is_this_parameter (t))
+    return true;
+  if (TREE_CODE (t) != PARM_DECL)
+    return false;
+  tree ctx = DECL_CONTEXT (t);
+  return (ctx && DECL_XOBJ_MEMBER_FUNCTION_P (ctx)
+         && t == DECL_ARGUMENTS (ctx));
+}
+
 /* Insert the deduced return type for an auto function.  */
 
 void
diff --git a/gcc/testsuite/g++.dg/torture/accessor-fixits-9-xobj.C b/gcc/testsuite/g++.dg/torture/accessor-fixits-9-xobj.C
new file mode 100644 (file)
index 0000000..89be978
--- /dev/null
@@ -0,0 +1,119 @@
+// PR c++/84993
+// { dg-options "-fdiagnostics-show-caret -std=c++23" }
+
+/* Misspelling (by omitting a leading "m_") of a private member for which
+   there's a public accessor.
+
+   We expect a fix-it hint suggesting the accessor.  */
+
+class t1
+{
+public:
+  int get_ratio (this const t1& x) { return x.m_ratio; }
+
+private:
+  int m_ratio;
+};
+
+int test (t1 *ptr_1)
+{
+  return ptr_1->ratio; // { dg-error "'class t1' has no member named 'ratio'; did you mean 'int t1::m_ratio'\\? \\(accessible via 'int t1::get_ratio\\(this const t1&\\)'\\)" }
+  /* { dg-begin-multiline-output "" }
+   return ptr_1->ratio;
+                 ^~~~~
+                 get_ratio()
+     { dg-end-multiline-output "" } */
+}
+
+
+/* Misspelling of a private member for which there's a public accessor.
+
+   We expect a fix-it hint suggesting the accessor.  */
+
+class t2
+{
+public:
+  int get_color (this const t2& x) { return x.m_color; }
+
+private:
+  int m_color;
+};
+
+int test (t2 *ptr_2)
+{
+  return ptr_2->m_colour; // { dg-error "'class t2' has no member named 'm_colour'; did you mean 'int t2::m_color'\\? \\(accessible via 'int t2::get_color\\(this const t2&\\)'\\)" }
+  /* { dg-begin-multiline-output "" }
+   return ptr_2->m_colour;
+                 ^~~~~~~~
+                 get_color()
+     { dg-end-multiline-output "" } */
+}
+
+
+/* Misspelling of a private member via a subclass pointer, for which there's
+   a public accessor in the base class.
+
+   We expect a fix-it hint suggesting the accessor.  */
+
+class t3 : public t2 {};
+
+int test (t3 *ptr_3)
+{
+  return ptr_3->m_colour; // { dg-error "'class t3' has no member named 'm_colour'; did you mean 'int t2::m_color'\\? \\(accessible via 'int t2::get_color\\(this const t2&\\)'\\)" }
+  /* { dg-begin-multiline-output "" }
+   return ptr_3->m_colour;
+                 ^~~~~~~~
+                 get_color()
+     { dg-end-multiline-output "" } */
+}
+
+
+/* Misspelling of a protected member, for which there's isn't a public
+   accessor.
+
+   We expect no fix-it hint; instead a message identifying where the
+   data member was declared.  */
+
+class t4
+{
+protected:
+  int m_color; // { dg-message "declared protected here" }
+};
+
+int test (t4 *ptr_4)
+{
+  return ptr_4->m_colour; // { dg-error "'class t4' has no member named 'm_colour'; did you mean 'int t4::m_color'\\? \\(not accessible from this context\\)" }
+  /* { dg-begin-multiline-output "" }
+   return ptr_4->m_colour;
+                 ^~~~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   int m_color;
+       ^~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+
+/* Misspelling of a private member, for which the accessor is also private.
+
+   We expect no fix-it hint; instead a message identifying where the
+   data member was declared.  */
+
+class t5
+{
+  int get_color (this const t5& x) { return x.m_color; }
+  int m_color; // { dg-message "declared private here" }
+};
+
+int test (t5 *ptr_5)
+{
+  return ptr_5->m_colour; // { dg-error "'class t5' has no member named 'm_colour'; did you mean 'int t5::m_color'\\? \\(not accessible from this context\\)" }
+  /* { dg-begin-multiline-output "" }
+   return ptr_5->m_colour;
+                 ^~~~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   int m_color;
+       ^~~~~~~
+     { dg-end-multiline-output "" } */
+}