Hi,

Upon David's request I've joined the in progress patch to the below email.
I hope it makes more sense now.

Best,
Benjamin.

---------- Forwarded message ---------
From: Benjamin Priour <priour...@gmail.com>
Date: Tue, Jul 18, 2023 at 3:30 PM
Subject: [RFC] analyzer: Add optional trim of the analyzer diagnostics
going too deep [PR110543]
To: <gcc-patches@gcc.gnu.org>, David Malcolm <dmalc...@redhat.com>


Hi,

I'd like to request comments on a patch I am writing for PR110543.
The goal of this patch is to reduce the noise of the analyzer emitted
diagnostics when dealing with
system headers, or simply diagnostic paths that are too long. The new
option only affects the display
of the diagnostics, but doesn't hinder the actual analysis.

I've defaulted the new option to "system", thus preventing the diagnostic
paths from showing system headers.
"never" corresponds to the pre-patch behavior, whereas you can also specify
an unsigned value <maxdepth>
that prevents paths to go deeper than <maxdepth> frames.

fanalyzer-trim-diagnostics=
> Common Joined RejectNegative ToLower Var(flag_analyzer_trim_diagnostics)
> Init("system")
> -fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim diagnostics
> path that are too long before emission.
>

Does it sounds reasonable and user-friendly ?

Regstrapping was a success against trunk, although one of the newly added
test case fails for c++14.
Note that the test case below was done with "never", thus behaves exactly
as the pre-patch analyzer
on x86_64-linux-gnu.

/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=never"
> } */
> /* { dg-skip-if "" { c++98_only }  } */
>
> #include <memory>
> struct A {int x; int y;};
>
> int main () {
>   std::shared_ptr<A> a;
>   a->x = 4; /* { dg-line deref_a } */
>   /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
>
>   return 0;
> }
>
> /* { dg-begin-multiline-output "" }
>   'int main()': events 1-2
>     |
>     |
>     +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
> >::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
> false]': events 3-4
>            |
>            |
>            +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
> false; bool <anonymous> = false]': events 5-6
>                   |
>                   |
>                   +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
> std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
>                          |
>                          |
>                   <------+
>                   |
>                 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
> false; bool <anonymous> = false]': event 9
>                   |
>                   |
>            <------+
>            |
>          'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
> >::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
> false]': event 10
>            |
>            |
>     <------+
>     |
>   'int main()': events 11-12
>     |
>     |
>    { dg-end-multiline-output "" } */
>


The first events "'int main()': events 1-2" vary in c++14 (get events 1-3).

>
> // c++14 with fully detailed output
>   ‘int main()’: events 1-3
>     |
>     |    8 | int main () {
>     |      |     ^~~~
>     |      |     |
>     |      |     (1) entry to ‘main’
>     |    9 |   std::shared_ptr<A> a;
>     |      |                      ~
>     |      |                      |
>     |      |                      (2)
> ‘a.std::shared_ptr<A>::<anonymous>.std::__shared_ptr<A,
> __gnu_cxx::_S_atomic>::_M_ptr’ is NULL
>     |   10 |   a->x = 4; /* { dg-line deref_a } */
>     |      |    ~~
>     |      |    |
>     |      |    (3) calling ‘std::__shared_ptr_access<A,
> __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
>

whereas c++17 and posterior give

> // c++17 with fully detailed output
>
// ./xg++ -fanalyzer
>  ../../gcc/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
>  -B. -shared-libgcc -fanalyzer-trim-diagnostics=never -std=c++17
>
  ‘int main()’: events 1-2
>     |
>     |    8 | int main () {
>     |      |     ^~~~
>     |      |     |
>     |      |     (1) entry to ‘main’
>     |    9 |   std::shared_ptr<A> a;
>     |   10 |   a->x = 4; /* { dg-line deref_a } */
>     |      |    ~~
>     |      |    |
>     |      |    (2) calling ‘std::__shared_ptr_access<A,
> __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
>

Is there a way to make dg-multiline-output check for a regex ? Or would
checking the multiline-output only for c++17 and c++20 be acceptable ?
This divergence results from two slightly different IPA:

// c++14 -fdump-ipa-analyzer
> // std::shared_ptr<A> a; becomes
> a.D.29392._M_ptr = 0B;
> a.D.29392._M_refcount._M_pi = 0B;
>

whereas in c++17

> // c++17 -fdump-ipa-analyzer
> // std::shared_ptr<A> a; becomes
> a = {};
>

I know shared_ptr limited support is a coincidence more than a feature, but
maybe there is still a way to make the above test case work ?
Otherwise, I'd fallback to a supported system function. If you have any
that you fancy, do tell me.

Thanks,
Benjamin.

---
 gcc/analyzer/analyzer.cc                      |   2 +-
 gcc/analyzer/analyzer.h                       |   1 +
 gcc/analyzer/analyzer.opt                     |   4 +
 gcc/analyzer/diagnostic-manager.cc            | 132 ++++++++++++++++++
 gcc/analyzer/diagnostic-manager.h             |   7 +
 .../analyzer/fanalyzer-trim-diagnostics-0.C   |  19 +++
 .../analyzer/fanalyzer-trim-diagnostics-1.C   |  28 ++++
 .../analyzer/fanalyzer-trim-diagnostics-2.C   |  44 ++++++
 .../fanalyzer-trim-diagnostics-default.C      |  21 +++
 .../fanalyzer-trim-diagnostics-never.C        |  44 ++++++
 .../fanalyzer-trim-diagnostics-system.C       |  19 +++
 11 files changed, 320 insertions(+), 1 deletion(-)
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C

diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
index 5091fb7a583..b27d8e359db 100644
--- a/gcc/analyzer/analyzer.cc
+++ b/gcc/analyzer/analyzer.cc
@@ -274,7 +274,7 @@ is_named_call_p (const_tree fndecl, const char
*funcname)
    Compare with cp/typeck.cc: decl_in_std_namespace_p, but this doesn't
    rely on being the C++ FE (or handle inline namespaces inside of std).
 */

-static inline bool
+bool
 is_std_function_p (const_tree fndecl)
 {
   tree name_decl = DECL_NAME (fndecl);
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 579517c23e6..31597079153 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -386,6 +386,7 @@ extern bool is_special_named_call_p (const gcall *call,
const char *funcname,
 extern bool is_named_call_p (const_tree fndecl, const char *funcname);
 extern bool is_named_call_p (const_tree fndecl, const char *funcname,
      const gcall *call, unsigned int num_args);
+extern bool is_std_function_p (const_tree fndecl);
 extern bool is_std_named_call_p (const_tree fndecl, const char *funcname);
 extern bool is_std_named_call_p (const_tree fndecl, const char *funcname,
  const gcall *call, unsigned int num_args);
diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index 2760aaa8151..78ff2c07483 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -362,4 +362,8 @@ fdump-analyzer-untracked
 Common RejectNegative Var(flag_dump_analyzer_untracked)
 Emit custom warnings with internal details intended for analyzer
developers.

+fanalyzer-trim-diagnostics=
+Common Joined RejectNegative ToLower Var(flag_analyzer_trim_diagnostics)
Init("system")
+-fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim diagnostics
path that are too long before emission.
+
 ; This comment is to ensure we retain the blank line above.
diff --git a/gcc/analyzer/diagnostic-manager.cc
b/gcc/analyzer/diagnostic-manager.cc
index cfca305d552..d946ad5bac7 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -20,9 +20,11 @@ along with GCC; see the file COPYING3.  If not see

 #include "config.h"
 #define INCLUDE_MEMORY
+#define INCLUDE_VECTOR
 #include "system.h"
 #include "coretypes.h"
 #include "tree.h"
+#include "input.h"
 #include "pretty-print.h"
 #include "gcc-rich-location.h"
 #include "gimple-pretty-print.h"
@@ -60,6 +62,37 @@ along with GCC; see the file COPYING3.  If not see

 #if ENABLE_ANALYZER

+/* Return positive value of fanalyzer-trim-diagnostics if a depth was given
+  or a negative value otherwise.  */
+
+long trim_diagnostics_depth ()
+{
+  if (trim_diagnostics_system_p () ||  trim_diagnostics_never_p ())
+    return -1;
+
+  char *end = NULL;
+  long depth = strtol (flag_analyzer_trim_diagnostics, &end, 10);
+  gcc_assert (end == NULL || *end == '\0');
+  gcc_assert (!(end == flag_analyzer_trim_diagnostics && depth == 0));
+  return depth;
+}
+
+/* Return true if fanalyzer-trim-diagnostics is set to "system".  */
+
+bool trim_diagnostics_system_p ()
+{
+  int len = strlen (flag_analyzer_trim_diagnostics);
+  return len == 6 && strcmp (flag_analyzer_trim_diagnostics, "system") ==
0;
+}
+
+/* Return true if fanalyzer-trim-diagnostics is set to "never".  */
+
+bool trim_diagnostics_never_p ()
+{
+  int len = strlen (flag_analyzer_trim_diagnostics);
+  return len == 5 && strcmp (flag_analyzer_trim_diagnostics, "never") == 0;
+}
+
 namespace ana {

 class feasible_worklist;
@@ -2281,6 +2314,8 @@ diagnostic_manager::prune_path (checker_path *path,
   path->maybe_log (get_logger (), "path");
   prune_for_sm_diagnostic (path, sm, sval, state);
   prune_interproc_events (path);
+  if (! trim_diagnostics_never_p ())
+    trim_diagnostic_path (path);
   consolidate_conditions (path);
   finish_pruning (path);
   path->maybe_log (get_logger (), "pruned");
@@ -2667,6 +2702,103 @@ diagnostic_manager::prune_interproc_events
(checker_path *path) const
   while (changed);
 }

+void
+diagnostic_manager::trim_diagnostic_path (checker_path *path) const
+{
+  if (trim_diagnostics_system_p ())
+    prune_system_headers (path);
+  else
+    prune_events_too_deep (path);
+}
+
+/* Remove everything within ]callsite, IDX].  */
+static void
+prune_within_frame (checker_path *path, int &idx)
+{
+  int nesting = 1;
+  while (idx >= 0 && nesting != 0)
+    {
+      if (path->get_checker_event (idx)->is_call_p ())
+        nesting--;
+      else if (path->get_checker_event (idx)->is_return_p ())
+        nesting++;
+
+      path->delete_event (idx--);
+    }
+}
+
+void
+diagnostic_manager::prune_system_headers (checker_path *path) const
+{
+  int idx = (signed)path->num_events () - 1;
+  while (idx >= 0)
+    {
+      const checker_event *event = path->get_checker_event (idx);
+      /* Prune everything between [..., system call, (...), system return,
...].  */
+      if (event->is_return_p ()
+          && in_system_header_at (event->get_location ()))
+      {
+        int ret_idx = idx;
+        prune_within_frame (path, idx);
+
+        if (get_logger ())
+        {
+          label_text desc
+            (path->get_checker_event (idx)->get_desc (false));
+          log ("filtering event %i-%i:"
+              " system header event: %s",
+              idx, ret_idx, desc.get ());
+        }
+        // delete callsite
+        if (idx >= 0)
+          path->delete_event (idx);
+      }
+
+      idx--;
+    }
+}
+
+void
+diagnostic_manager::prune_events_too_deep (checker_path *path) const
+{
+  long depth = 0;
+  long maxdepth = trim_diagnostics_depth ();
+  gcc_assert (maxdepth >= 0);
+  int idx = (signed)path->num_events () - 1;
+  if (idx >= 0)
+  {
+    depth = path->get_checker_event (0)->get_stack_depth ();
+    maxdepth += depth;
+  }
+
+  while (idx >= 0)
+    {
+      const checker_event *event = path->get_checker_event (idx);
+      /* Prune everything between [..., call too deep, (...), return too
deep, ...].  */
+      if (event->is_return_p ()
+          && event->get_stack_depth () > maxdepth)
+      {
+        int ret_idx = idx;
+        prune_within_frame (path, idx);
+
+        if (get_logger ())
+        {
+          label_text desc
+            (path->get_checker_event (idx)->get_desc (false));
+          log ("filtering event %i-%i:"
+              " event too deep: %s",
+              idx, ret_idx, desc.get ());
+        }
+        // delete callsite
+        if (idx >= 0)
+          path->delete_event (idx);
+      }
+
+      idx--;
+    }
+
+}
+
 /* Return true iff event IDX within PATH is on the same line as
REF_EXP_LOC.  */

 static bool
diff --git a/gcc/analyzer/diagnostic-manager.h
b/gcc/analyzer/diagnostic-manager.h
index 9b3e903fc5d..9afef0975be 100644
--- a/gcc/analyzer/diagnostic-manager.h
+++ b/gcc/analyzer/diagnostic-manager.h
@@ -21,6 +21,10 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_ANALYZER_DIAGNOSTIC_MANAGER_H
 #define GCC_ANALYZER_DIAGNOSTIC_MANAGER_H

+long trim_diagnostics_depth ();
+bool trim_diagnostics_system_p ();
+bool trim_diagnostics_never_p ();
+
 namespace ana {

 class epath_finder;
@@ -180,6 +184,9 @@ private:
  state_machine::state_t state) const;
   void update_for_unsuitable_sm_exprs (tree *expr) const;
   void prune_interproc_events (checker_path *path) const;
+  void trim_diagnostic_path (checker_path *path) const;
+  void prune_system_headers (checker_path *path) const;
+  void prune_events_too_deep (checker_path *path) const;
   void consolidate_conditions (checker_path *path) const;
   void finish_pruning (checker_path *path) const;

diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
new file mode 100644
index 00000000000..ddf39ed1690
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
@@ -0,0 +1,19 @@
+/* { dg-additional-options "-fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=0" } */
+/* { dg-skip-if "" { c++98_only }  } */
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |
+   { dg-end-multiline-output "" } */
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
new file mode 100644
index 00000000000..83a132078a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
@@ -0,0 +1,28 @@
+/* { dg-additional-options "-fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=1" } */
+/* { dg-skip-if "" { c++98_only }  } */
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |
+    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
>::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
_Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
false]': event 3
+           |
+           |
+    <------+
+    |
+  'int main()': events 4-5
+    |
+    |
+   { dg-end-multiline-output "" } */
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
new file mode 100644
index 00000000000..cf065929ec9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
@@ -0,0 +1,44 @@
+/* { dg-additional-options "-fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=2" } */
+/* { dg-skip-if "" { c++98_only }  } */
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |
+    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
>::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
_Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
false]': events 3-4
+           |
+           |
+           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
<anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
false; bool <anonymous> = false]': events 5-6
+                  |
+                  |
+                  +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
+                         |
+                         |
+                  <------+
+                  |
+                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
<anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
false; bool <anonymous> = false]': event 9
+                  |
+                  |
+           <------+
+           |
+         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
>::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
_Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
false]': event 10
+           |
+           |
+    <------+
+    |
+  'int main()': events 11-12
+    |
+    |
+   { dg-end-multiline-output "" } */
\ No newline at end of file
diff --git
a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
new file mode 100644
index 00000000000..fc271309d4e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
@@ -0,0 +1,21 @@
+/* { dg-additional-options "-O0 -fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events" } */
+/* { dg-skip-if "" { c++98_only }  } */
+
+// Must behave like -fanalyzer-trim-diagnostics=system
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |
+   { dg-end-multiline-output "" } */
\ No newline at end of file
diff --git
a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
new file mode 100644
index 00000000000..a69416608da
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
@@ -0,0 +1,44 @@
+/* { dg-additional-options "-fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=never"
} */
+/* { dg-skip-if "" { c++98_only }  } */
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |
+    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
>::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
_Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
false]': events 3-4
+           |
+           |
+           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
<anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
false; bool <anonymous> = false]': events 5-6
+                  |
+                  |
+                  +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
+                         |
+                         |
+                  <------+
+                  |
+                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
<anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
false; bool <anonymous> = false]': event 9
+                  |
+                  |
+           <------+
+           |
+         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
>::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
_Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
false]': event 10
+           |
+           |
+    <------+
+    |
+  'int main()': events 11-12
+    |
+    |
+   { dg-end-multiline-output "" } */
\ No newline at end of file
diff --git
a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
new file mode 100644
index 00000000000..22fadc84e75
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
@@ -0,0 +1,19 @@
+/* { dg-additional-options "-fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=system"
} */
+/* { dg-skip-if "" { c++98_only }  } */
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |
+   { dg-end-multiline-output "" } */
\ No newline at end of file
-- 
2.34.1

Reply via email to