// no free
// we are focusing on functions that return NULL, but sometimes there are
// confusions and that is not the point of this rule, so it seems better to
// just deal with them explicitly

@r using "../likely.iso" exists@
local idexpression x;
statement S,S1,S2,S3;
position p1,p2,p3,p4;
expression E,E1;
type T,T1;
expression *ptr != NULL;
@@

(
 if ((x@p1 = ALLOC(...)) == NULL) S
|
 x@p1 = ALLOC(...);
)
 ... when != FREE(...,(T)x,...)
     when != if (...) { <+... FREE(...,(T)x,...) ...+> } else S2
     when != true x == NULL || ...
     when != true (IS_ERR(x)) || ...
     when != x = E
     when != E = (T)x
     when any
(
 if (x == NULL || ...) S1
|
 if ((IS_ERR(x)) || ...) S1
|
 if@p2 (...) {
  ... when != FREE(...,(T1)x,...)
      when != if (...) { <+... FREE(...,(T1)x,...) ...+> } else S3
      when != x = E1
      when != E1 = (T1)x
(
  return@p4 \(0\|<+...x...+>\|ptr\);
|
  return@p3 ...;
)
}
)

@ script:python @
p1 << r.p1;
p2 << r.p2;
p3 << r.p3;
@@

print "* TODO [[view:%s::face=ovl-face1::linb=%s::colb=%s::cole=%s][%s::%s]]" % (p1[0].file,p1[0].line,p1[0].column,p1[0].column_end,p1[0].file,p1[0].line)
for i in p2:
        print "[[view:%s::face=ovl-face3::linb=%s::colb=%s::cole=%s][if::%s]]" % (i.file,i.line,i.column,i.column_end,i.line)
for i in p3:
        print "[[view:%s::face=ovl-face2::linb=%s::colb=%s::cole=%s][return::%s]]" % (i.file,i.line,i.column,i.column_end,i.line)
print "protocol: ALLOC FREE"
cocci.include_match(False)

@ script:python @
p1 << r.p1;
p2 << r.p2;
p4 << r.p4;
@@

cocci.include_match(False)

@ script:python @
p1 << r.p1;
p2 << r.p2;
@@

print "* TODO [[view:%s::face=ovl-face1::linb=%s::colb=%s::cole=%s][%s::%s]]" % (p1[0].file,p1[0].line,p1[0].column,p1[0].column_end,p1[0].file,p1[0].line)
for i in p2:
        print "[[view:%s::face=ovl-face3::linb=%s::colb=%s::cole=%s][if::%s]]" % (i.file,i.line,i.column,i.column_end,i.line)
print "no return"
print "protocol: ALLOC FREE"