return 0;
}
+/* Return true is SNODE is the EXIT node of a function, or is one
+ of the final snodes within its function.
+
+ Specifically, handle the final supernodes before the EXIT node,
+ for the case of clobbers that happen immediately before exiting.
+ We need a run of snodes leading to the return_p snode, where all edges are
+ intraprocedural, and every snode has just one successor.
+
+ We use this when suppressing leak reports at the end of "main". */
+
+static bool
+returning_from_function_p (const supernode *snode)
+{
+ if (!snode)
+ return false;
+
+ unsigned count = 0;
+ const supernode *iter = snode;
+ while (true)
+ {
+ if (iter->return_p ())
+ return true;
+ if (iter->m_succs.length () != 1)
+ return false;
+ const superedge *sedge = iter->m_succs[0];
+ if (sedge->get_kind () != SUPEREDGE_CFG_EDGE)
+ return false;
+ iter = sedge->m_dest;
+
+ /* Impose a limit to ensure we terminate for pathological cases.
+
+ We only care about the final 3 nodes, due to cases like:
+ BB:
+ (clobber causing leak)
+
+ BB:
+ <label>:
+ return _val;
+
+ EXIT BB.*/
+ if (++count > 3)
+ return false;
+ }
+}
+
/* Find the best tree for SVAL and call SM's on_leak vfunc with it.
If on_leak returns a pending_diagnostic, queue it up to be reported,
so that we potentially complain about a leak of SVAL in the given STATE. */
gcc_assert (m_enode_for_diag);
/* Don't complain about leaks when returning from "main". */
- if (m_enode_for_diag->get_supernode ()
- && m_enode_for_diag->get_supernode ()->return_p ())
+ if (returning_from_function_p (m_enode_for_diag->get_supernode ()))
{
tree fndecl = m_enode_for_diag->get_function ()->decl;
if (id_equal (DECL_NAME (fndecl), "main"))
--- /dev/null
+/* { dg-additional-options "-Wno-analyzer-too-complex -fno-analyzer-call-summaries" } */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+struct list {
+ struct list* next;
+ void *a;
+};
+
+void func(struct list **res)
+{
+ struct list *cur = NULL;
+ do {
+ struct list *n = malloc(sizeof(struct list));
+ void *a = malloc(1);
+ if (n == NULL || a == NULL) {
+ if (n != NULL) free(n);
+ if (a != NULL) free(a);
+ break;
+ }
+
+ if (cur == NULL) {
+ *res = cur = n;
+ } else {
+ cur->next = n;
+ cur = n;
+ }
+ n->a = a;
+ } while (true);
+}
+
+int main()
+{
+ struct list *res;
+ func(&res);
+}
--- /dev/null
+/* { dg-additional-options "-Wno-analyzer-too-complex -fno-analyzer-call-summaries" } */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+struct list {
+ struct list* next;
+ void *a;
+};
+
+void func(struct list **res)
+{
+ struct list *cur = NULL;
+ do {
+ struct list *n = malloc(sizeof(struct list));
+ void *a = malloc(1);
+ if (n == NULL || a == NULL) {
+ if (n != NULL) free(n);
+ if (a != NULL) free(a);
+ break;
+ }
+
+ if (cur == NULL) {
+ *res = cur = n;
+ } else {
+ cur->next = n;
+ cur = n;
+ }
+ n->a = a;
+ } while (true);
+}
+
+int not_main()
+{
+ struct list *res;
+ func(&res);
+} /* { dg-warning "leak of 'res'" "leak of res" } */
+/* { dg-warning "leak of '<unknown>'" "leak of res->a" { target *-*-* } .-1 } */
+/* TODO: we should emit 'res->a' rather than '<unknown>' here. */