// Idea: if there is an early return, then the allocated memory should be
// freed.

@pre exists@
local idexpression x;
expression E,E1;
identifier alloc != {kmalloc,kzalloc,kcalloc},dealloc;
@@

x = alloc(...)
... when != x = E
    when != true x == NULL || ...
dealloc(x);
... when != x
?x= E1

@detect_alloc using "likely.iso"@
type T;
local idexpression T *x;
identifier pre.alloc,pre.dealloc;
position p,p2;
statement S;
expression E1;
identifier f;
@@

(
if (((x@p = alloc(...)) == NULL) || ...) S
|
x@p = alloc(...)
... when != x
if ((x == NULL) || ...) S
)
... when any
    when strict
(
  return <+...x...+>;
|
  x = E1
|
  E1 = x
|
  &x->f
|
  &x
|
(
  dealloc@p2(x);
|
  if (x!=NULL) dealloc@p2(x);
)
  ... when != x
(
  return ...;
|
  x = E1
|
  &x
)
)

// Idea: memory that was just allocated should not be dereferenced

@fields@
detect_alloc.T *x;
identifier fld;
@@

x->fld

@accesses exists@
expression x;
expression E;
identifier fields.fld,pre.alloc;
@@

x = alloc(...)
<+... x->fld ...+>
(
x->fld = E;
|
x->fld = E
|
x = E
|
?&x->fld
)

@missing_dealloc using "likely.iso" exists@
local idexpression x;
identifier pre.alloc,pre.dealloc;
statement S;
expression E1;
identifier f;
@@

(
if (((x = alloc(...)) == NULL) || ...) S
|
x = alloc(...)
... when != x
if ((x == NULL) || ...) S
)
... when != return <+...x...+>;
    when != x = E1
    when != E1 = x
    when != &x->f
    when != &x
    when != dealloc(x);
    when != if (x!=NULL) dealloc(x);
    when any

@ script:python depends on !accesses && !missing_dealloc @
alloc << pre.alloc;
dealloc << pre.dealloc;
p2 << detect_alloc.p2;
@@

print "#%s" % p2[0].file
print "undo: ALLOC:%s FREE:%s" % (alloc,dealloc)
#print "undo: ALLOC:%s" % (alloc)