https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125223
Bug ID: 125223
Summary: Poor UX when analyzer reports on issues inside C++ std
types
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: analyzer
Assignee: dmalcolm at gcc dot gnu.org
Reporter: dmalcolm at gcc dot gnu.org
CC: redi at gcc dot gnu.org
Blocks: 97110
Target Milestone: ---
Consider e.g.:
$ cat ../../src/gcc/testsuite/g++.dg/analyzer/std-unique_ptr-2.C
// { dg-do compile { target c++11 } }
// { dg-additional-options "-Wno-analyzer-too-complex" } */
#include <memory>
struct A {int x; int y;};
extern std::unique_ptr<A> make_ptr ();
int test (int flag) {
std::unique_ptr<A> a;
if (flag)
a = make_ptr ();
a->x = 12; // { dg-warning "dereference of NULL" "" { xfail *-*-*} }
// TODO: this is failing due to "too complex" warnings
return 0;
}
The analyzer finds the problem if we enable optimization, but the messages are
horrible.
At -O we get:
../../src/gcc/testsuite/g++.dg/analyzer/std-unique_ptr-2.C: In function ‘int
test(int)’:
../../src/gcc/testsuite/g++.dg/analyzer/std-unique_ptr-2.C:14:8: warning:
dereference of NULL ‘<unknown>’ [CWE-476] [-Wanalyzer-null-dereference]
14 | a->x = 12; // { dg-warning "dereference of NULL" "" { xfail *-*-*} }
| ~~~~~^~~~
‘int test(int)’: events 1-3
12 | if (flag)
| ^~
| |
| (1) following ‘false’ branch (when ‘flag == 0’)... ─>─┐
| │
| │
|┌────────────────────────────────────────────────────────┘
13 |│ a = make_ptr ();
14 |│ a->x = 12; // { dg-warning "dereference of NULL" "" { xfail *-*-*} }
|│ ~~~~~~~~~
|│ |
|└──────>(2) ...to here
| (3) ⚠️ dereference of NULL
‘a.std::unique_ptr<A>::_M_t.std::__uniq_ptr_data<A, std::default_delete<A>,
true, true>::std::__uniq_ptr_impl<A, std::default_delete<A>
>.std::__uniq_ptr_impl<A, std::default_delete<A> >::_M_t.std::tuple<A*,
std::default_delete<A> >::std::_Tuple_impl<0, A*, std::default_delete<A>
>.std::_Tuple_impl<0, A*, std::default_delete<A> >::std::_Head_base<0, A*,
false>.std::_Head_base<0, A*, false>::_M_head_impl’
At -Og we get:
../../src/gcc/testsuite/g++.dg/analyzer/std-unique_ptr-2.C: In function ‘int
test(int)’:
../../src/gcc/testsuite/g++.dg/analyzer/std-unique_ptr-2.C:14:8: warning:
dereference of NULL ‘0’ [CWE-476] [-Wanalyzer-null-dereference]
14 | a->x = 12; // { dg-warning "dereference of NULL" "" { xfail *-*-*} }
| ~~~~~^~~~
‘int test(int)’: events 1-4
│
│ 10 | int test (int flag) {
│ | ^~~~
│ | |
│ | (1) entry to ‘test’
│ 11 | std::unique_ptr<A> a;
│ | ~
│ | |
│ | (2) using NULL here
│ 12 | if (flag)
│ | ~~
│ | |
│ | (3) following ‘false’ branch (when ‘flag == 0’)... ─>─┐
│ | │
│ | │
│ |┌────────────────────────────────────────────────────────┘
│ 13 |│ a = make_ptr ();
│ 14 |│ a->x = 12; // { dg-warning "dereference of NULL" "" { xfail
*-*-*} }
│ |│ ~
│ |│ |
│ |└──>(4) inlined call to ‘std::unique_ptr<A>::operator->’ from
‘test’
│
└──> ‘std::unique_ptr<_Tp, _Dp>::pointer std::unique_ptr<_Tp,
_Dp>::operator->() const [with _Tp = A; _Dp = std::default_delete<A>]’: event 5
│
│../x86_64-pc-linux-gnu/libstdc++-v3/include/bits/unique_ptr.h:484:19:
│ 484 | return get();
│ | ^
│ | |
│ | (5) inlined call to
‘std::unique_ptr<A>::get’ from ‘std::unique_ptr<A>::operator->’
│
└──> ‘std::unique_ptr<_Tp, _Dp>::pointer std::unique_ptr<_Tp,
_Dp>::get() const [with _Tp = A; _Dp = std::default_delete<A>]’: event 6
│
│ 491 | { return _M_t._M_ptr(); }
│ | ^
│ | |
│ | (6) inlined call to
‘std::__uniq_ptr_impl<A, std::default_delete<A> >::_M_ptr’ from
‘std::unique_ptr<A>::get’
│
└──> ‘std::__uniq_ptr_impl<_Tp, _Dp>::pointer
std::__uniq_ptr_impl<_Tp, _Dp>::_M_ptr() const [with _Tp = A; _Dp =
std::default_delete<A>]’: event 7
│
│ 192 | pointer _M_ptr() const noexcept {
return std::get<0>(_M_t); }
│ |
^
│ |
|
│ |
(7) ...to here
│
<────────────────────┘
│
‘int test(int)’: event 8
│
│../../src/gcc/testsuite/g++.dg/analyzer/std-unique_ptr-2.C:14:8:
│ 14 | a->x = 12; // { dg-warning "dereference of NULL" "" { xfail
*-*-*} }
│ | ~~~~~^~~~
│ | |
│ | (8) ⚠️ dereference of NULL ‘a.std::unique_ptr<A,
std::default_delete<A> >::_M_t.std::__uniq_ptr_data<A, std::default_delete<A>,
true, true>::<unnamed>.std::__uniq_ptr_impl<A, std::default_delete<A>
>::_M_t.std::tuple<A*, std::default_delete<A> >::<unnamed>.std::_Tuple_impl<0,
A*, std::default_delete<A> >::<unnamed>.std::_Head_base<0, A*,
false>::_M_head_impl’
│
where _M_head_impl is an implementation detail of std::unique_ptr and that
typename at (8) is indecipherably long.
A C++ developer presumably thinks of this as a std::unique_ptr "being nullptr",
so presumably we should emit something like:
../../src/gcc/testsuite/g++.dg/analyzer/std-unique_ptr-2.C: In function ‘int
test(int)’:
../../src/gcc/testsuite/g++.dg/analyzer/std-unique_ptr-2.C:14:8: warning:
dereference of NULL ‘a’ [CWE-476] [-Wanalyzer-null-dereference]
14 | a->x = 12; // { dg-warning "dereference of NULL" "" { xfail *-*-*} }
| ~~~~~^~~~
‘int test(int)’: event 1
12 | if (flag)
| ^~
| |
| (1) following ‘false’ branch (when ‘flag == 0’)... ─>─┐
| │
‘int test(int)’: events 2-3
| │
|┌────────────────────────────────────────────────────────┘
12 |│ if (flag)
|│ ^~
|│ |
|└─>(2) ...to here
13 | a = make_ptr ();
14 | a->x = 12; // { dg-warning "dereference of NULL" "" { xfail *-*-*} }
| ~~~~~~~~~
| |
| (3) ⚠️ dereference of NULL ‘a’
We already have a way to decode the implementation details of various std::
types in terms the user understands: gdb pretty-printers.
(Possibly) crazy idea: have some way to run those pretty-printers on the
analyzer's model of the simulated state of memory along the given execution
path.
But maybe that doesn't give us the information we want.
Possible interaction here with bug 106386, and that we should instead complain
when preconditions don't hold.
Referenced Bugs:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97110
[Bug 97110] [meta-bug] tracker bug for supporting C++ in -fanalyzer