From 2f7c50b7091c09d665aaf27173aacf34c9904e4c Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Tue, 6 Oct 2020 17:59:07 -0400 Subject: [PATCH] analyzer: handle C++ argument numbers and "this" [PR97116] 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 | 61 +++++++++++++++++++------ gcc/testsuite/g++.dg/analyzer/pr97116.C | 39 ++++++++++++++++ 2 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/g++.dg/analyzer/pr97116.C diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index 6293d7885cd..fd12a358176 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -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 index 00000000000..d8e08a73172 --- /dev/null +++ b/gcc/testsuite/g++.dg/analyzer/pr97116.C @@ -0,0 +1,39 @@ +#include +#include + +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 } +} -- 2.30.2