analyzer: handle C++ argument numbers and "this" [PR97116]
authorDavid Malcolm <dmalcolm@redhat.com>
Tue, 6 Oct 2020 21:59:07 +0000 (17:59 -0400)
committerDavid Malcolm <dmalcolm@redhat.com>
Wed, 7 Oct 2020 13:39:37 +0000 (09:39 -0400)
gcc/analyzer/ChangeLog:
PR analyzer/97116
* sm-malloc.cc (method_p): New.
(describe_argument_index): New.
(inform_nonnull_attribute): Use describe_argument_index.
(possible_null_arg::describe_final_event): Likewise.
(null_arg::describe_final_event): Likewise.

gcc/testsuite/ChangeLog:
PR analyzer/97116
* g++.dg/analyzer/pr97116.C: New test.

gcc/analyzer/sm-malloc.cc
gcc/testsuite/g++.dg/analyzer/pr97116.C [new file with mode: 0644]

index 6293d78..fd12a35 100644 (file)
@@ -562,15 +562,40 @@ public:
 
 };
 
+/* Return true if FNDECL is a C++ method.  */
+
+static bool
+method_p (tree fndecl)
+{
+  return TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE;
+}
+
+/* Return a 1-based description of ARG_IDX (0-based) of FNDECL.
+   Compare with %P in the C++ FE  (implemented in cp/error.c: parm_to_string
+   as called from cp_printer).  */
+
+static label_text
+describe_argument_index (tree fndecl, int arg_idx)
+{
+  if (method_p (fndecl))
+    if (arg_idx == 0)
+      return label_text::borrow ("'this'");
+  pretty_printer pp;
+  pp_printf (&pp, "%u", arg_idx + 1 - method_p (fndecl));
+  return label_text::take (xstrdup (pp_formatted_text (&pp)));
+}
+
 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
    Issue a note informing that the pertinent argument must be non-NULL.  */
 
 static void
 inform_nonnull_attribute (tree fndecl, int arg_idx)
 {
+  label_text arg_desc = describe_argument_index (fndecl, arg_idx);
   inform (DECL_SOURCE_LOCATION (fndecl),
-         "argument %u of %qD must be non-null",
-         arg_idx + 1, fndecl);
+         "argument %s of %qD must be non-null",
+         arg_desc.m_buffer, fndecl);
+  arg_desc.maybe_free ();
   /* Ideally we would use the location of the parm and underline the
      attribute also - but we don't have the location_t values at this point
      in the middle-end.
@@ -618,15 +643,19 @@ public:
 
   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
   {
+    label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
+    label_text result;
     if (m_origin_of_unchecked_event.known_p ())
-      return ev.formatted_print ("argument %u (%qE) from %@ could be NULL"
-                                " where non-null expected",
-                                m_arg_idx + 1, ev.m_expr,
-                                &m_origin_of_unchecked_event);
+      result = ev.formatted_print ("argument %s (%qE) from %@ could be NULL"
+                                  " where non-null expected",
+                                  arg_desc.m_buffer, ev.m_expr,
+                                  &m_origin_of_unchecked_event);
     else
-      return ev.formatted_print ("argument %u (%qE) could be NULL"
-                                " where non-null expected",
-                                m_arg_idx + 1, ev.m_expr);
+      result = ev.formatted_print ("argument %s (%qE) could be NULL"
+                                  " where non-null expected",
+                                  arg_desc.m_buffer, ev.m_expr);
+    arg_desc.maybe_free ();
+    return result;
   }
 
 private:
@@ -714,13 +743,17 @@ public:
 
   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
   {
+    label_text arg_desc = describe_argument_index (m_fndecl, m_arg_idx);
+    label_text result;
     if (zerop (ev.m_expr))
-      return ev.formatted_print ("argument %u NULL where non-null expected",
-                                m_arg_idx + 1);
+      result = ev.formatted_print ("argument %s NULL where non-null expected",
+                                  arg_desc.m_buffer);
     else
-      return ev.formatted_print ("argument %u (%qE) NULL"
-                                " where non-null expected",
-                                m_arg_idx + 1, ev.m_expr);
+      result = ev.formatted_print ("argument %s (%qE) NULL"
+                                  " where non-null expected",
+                                  arg_desc.m_buffer, ev.m_expr);
+    arg_desc.maybe_free ();
+    return result;
   }
 
 private:
diff --git a/gcc/testsuite/g++.dg/analyzer/pr97116.C b/gcc/testsuite/g++.dg/analyzer/pr97116.C
new file mode 100644 (file)
index 0000000..d8e08a7
--- /dev/null
@@ -0,0 +1,39 @@
+#include <new>
+#include <cstddef>
+
+struct foo
+{
+  foo (int i) : m_i (i) {} // { dg-message "argument 'this' of 'foo::foo\\(int\\)' must be non-null" "note" }
+
+  int get () const { return m_i; } // { dg-message "argument 'this' of '\[^\n\]*' must be non-null" "note" }
+  
+  int meth_1 (int, void *ptr) __attribute__((nonnull)); // { dg-message "argument 2 of '\[^\n\]*' must be non-null" "note" }
+  int meth_2 (int, void *ptr) __attribute__((nonnull(3))); // { dg-message "argument 2 of '\[^\n\]*' must be non-null" "note" }
+
+  int m_i;
+};
+
+void test_1 (void)
+{
+  foo *p = new(NULL) foo (42); // { dg-warning "non-null expected" "warning" }
+  // { dg-message "argument 'this' \\(\[^\n\]*\\) NULL where non-null expected" "final event" { target *-*-* } .-1 }
+}
+
+int test_2 (void)
+{
+  foo *p = NULL;
+  return p->get (); // { dg-warning "non-null expected" "warning" }
+  // { dg-message "argument 'this' \\('p'\\) NULL where non-null expected" "final event" { target *-*-* } .-1 }
+}
+
+int test_meth_1 (foo *f)
+{
+  return f->meth_1 (42, NULL); // { dg-warning "non-null expected" "warning" }
+  // { dg-message "argument 2 NULL where non-null expected" "final event" { target *-*-* } .-1 }
+}
+
+int test_meth_2 (foo *f)
+{
+  return f->meth_2 (42, NULL); // { dg-warning "non-null expected" "warning" }
+  // { dg-message "argument 2 NULL where non-null expected" "final event" { target *-*-* } .-1 }
+}