@script:python@ @@

def outputter(f,pf,ptf,p,s):
    print "* TODO [[view:%s::face=ovl-face1::linb=%s::colb=%s::cole=%s][%s: %s::%s]]" % (pf[0].file,pf[0].line,pf[0].column,pf[0].column_end,s,pf[0].file,pf[0].line)
    for i in p:
       print "[[view:%s::face=ovl-face2::linb=%s::colb=%s::cole=%s][%s: return from %s::%s]]" % (pf[0].file,i.line,i.column,i.column_end,s,f,i.line)

@relevant@
identifier f;
position pf;
position rets;
expression E;
type T;
@@

T *f@pf(...) { <... return E@rets; ...> }

@ t1 using "likely.iso" disable add_parens @
relevant.T *E;
identifier relevant.f;
statement S1, S2;
constant C;
@@

f(...) {
<...
(
if (E == NULL&&...) { ...
   return \(C\|ERR_PTR(...)\); }
|
if (E == NULL&&...)
+{
+ E = NULL;
  S1
+}
else S2
|
if (E != NULL||...) S1
+ else E = NULL;
)
...>
}

@ t2 using "likely.iso" disable int_if_test1, int_if_test2, add_parens @
relevant.T *E;
identifier relevant.f;
statement S1, S2;
constant C;
@@

f(...) {
<...
(
if (IS_ERR(E)&&...) { ...
   return \(C\|ERR_PTR(...)\); }
|
if (IS_ERR(E)&&...)
+{
+ E = ERR_PTR(0);
  S1
+}
else S2
|
if (!IS_ERR(E)||...) S1
+ else E = ERR_PTR(0);
)
...>
}

@ t3 using "likely.iso" disable int_if_test1, int_if_test2, add_parens @
relevant.T *E;
identifier relevant.f;
statement S;
iterator I;
@@

f(...) {
<...
I(E,...)
+{
+ E->fld = 0;
  S
+}
...>
}

@prelevant@
identifier relevant.f;
position pf;
@@

f@pf(...) { ... }

// -------------------------------------------------------------------------
// Identify different kinds of return

@rn using "likely.iso" exists@
identifier relevant.f,fld;
position prelevant.pf;
position ret_null,e_null;
expression E,E1;
@@

f@pf(...) { ... when any
(
return@ret_null NULL;
|
E@e_null = NULL
... when != E=E1
    when != E->fld
return@ret_null E;
)
}

@re using "likely.iso" exists@
identifier relevant.f,fld;
position prelevant.pf;
position ret_err,e_err;
expression E,E1;
@@

f@pf(...) { ... when any
(
return@ret_err ERR_PTR(...);
|
E@e_err = ERR_PTR(...)
... when != E=E1
    when != E->fld
return@ret_err E;
)
}

@conj using "likely.iso"@
identifier relevant.f,fld;
position prelevant.pf;
position e_ptr;
expression E,E1;
@@

f@pf(...) { <... 
(
 E != NULL && (<+...E@e_ptr->fld...+>)
|
 E == NULL || (<+...E@e_ptr->fld...+>)
|
 E != NULL ? <+...E@e_ptr->fld...+> : E1
)
...> }

@rp exists@
identifier relevant.f;
position prelevant.pf;
position ret_ptr,e_ptr!=conj.e_ptr;
expression E,E1,E2;
identifier fld;
@@

f@pf(...) { ... when any
(
return@ret_ptr &E;
|
E@e_ptr = &E1
... when != E=E2
return@ret_ptr E;
|
E@e_ptr->fld
... when != E=E2
return@ret_ptr E;
)
}

// -------------------------------------------------------------------------
// Check for invalid paths

@br exists@
identifier relevant.f;
position prelevant.pf;
position p2 != {rn.e_null,re.e_err,rp.e_ptr}, bad_return;
expression E,E1,E2;
identifier E3 != {NULL};
position p != conj.e_ptr;
identifier fld;
@@

f@pf(...) {
(
  ... when any
  E@p2 = E1
  ... when != E@p->fld
      when != E = E2
  return E@bad_return;
|
  ... when != E3@p->fld // parameter/global variable case
      when != E3 = E2
  return E3@bad_return;
)
}

// -------------------------------------------------------------------------
// Categorize

@always_null depends on !re && !rp@
identifier relevant.f;
position prelevant.pf;
position any rn.ret_null;
position p != br.bad_return;
expression E;
position pf1;
@@

f@pf(...) {@pf1 ... when strict
  return@ret_null E@p;
}

@always_err depends on !rn && !rp@
identifier relevant.f;
position prelevant.pf;
position any re.ret_err;
position p != br.bad_return;
expression E;
position pf1;
@@

f@pf(...) {@pf1 ... when strict
  return@ret_err E@p;
}

@always_ptr depends on !rn && !re@
identifier relevant.f;
position prelevant.pf;
position any rp.ret_ptr;
position p != br.bad_return;
expression E;
position pf1;
@@

f@pf(...) {@pf1 ... when strict
  return@ret_ptr E@p;
}

@precat1 depends on rn && !re && !always_null@
identifier relevant.f;
position prelevant.pf;
position any rn.ret_null;
position any rp.ret_ptr;
position p != br.bad_return;
expression E;
@@

f@pf(...) { ... when strict
(
  return@ret_null E@p;
|
  return@ret_ptr E@p;
)
}

@cat1 depends on precat1 exists@
identifier relevant.f;
position prelevant.pf;
position any rn.ret_null;
position any rn.e_null;
expression E;
statement S;
position pf1;
@@

f@pf(...) {@pf1 <+...
  if (...) { <+...
(
    return@ret_null E;
|
    E@e_null = NULL
) ...+>
  } else S
  ...+>
}

@cat2 depends on re && !rn && !always_err@
identifier relevant.f;
position prelevant.pf;
position any re.ret_err;
position any rp.ret_ptr;
position p != br.bad_return;
expression E;
position pf1;
@@

f@pf(...) {@pf1 ... when strict
(
  return@ret_err E@p;
|
  return@ret_ptr E@p;
)
}

@cat3 depends on rn && re@
identifier relevant.f;
position prelevant.pf;
position pf1;
@@

f@pf(...) {@pf1 ... }

@notcat1@
identifier relevant.f;
position p1 != {cat1.pf1,always_null.pf1};
@@

f(...) {@p1 ... }

@notcat2@
identifier relevant.f;
position p1 != {cat2.pf1,always_err.pf1};
@@

f(...) {@p1 ... }

@notcat3@
identifier relevant.f;
position p1 != {cat3.pf1};
@@

f(...) {@p1 ... }

@notcat4@
identifier relevant.f;
position p1 != {always_ptr.pf1};
@@

f(...) {@p1 ... }

// -------------------------------------------------------------------------
// Print results

@ script:python depends on cat1 && !notcat1 @
f << relevant.f;   // identifier
pf << relevant.pf; // position
ptf << prelevant.pf; // position after trafo
rets << relevant.rets;
@@

print "#%s" % pf[0].file
print "category1: FN:%s" % f
outputter(f,pf,ptf,rets,"category 1")
cocci.include_match(False)

@ script:python depends on always_null && !notcat1 @
f << relevant.f;   // identifier
pf << relevant.pf; // position
ptf << prelevant.pf; // position after trafo
rets << relevant.rets;
@@

print "#%s" % pf[0].file
print "category1: FN:%s" % f
outputter(f,pf,ptf,rets,"category 1")
cocci.include_match(False)

@ script:python depends on cat2 && !notcat2 @
f << relevant.f;   // identifier
pf << relevant.pf; // position
ptf << prelevant.pf; // position after trafo
rets << relevant.rets;
@@

print "#%s" % pf[0].file
print "category2: FN:%s" % f
outputter(f,pf,ptf,rets,"category 2")
cocci.include_match(False)

@ script:python depends on always_err && !notcat2 @
f << relevant.f;   // identifier
pf << relevant.pf; // position
ptf << prelevant.pf; // position after trafo
rets << relevant.rets;
@@

print "#%s" % pf[0].file
print "category2: FN:%s" % f
outputter(f,pf,ptf,rets,"category 2")
cocci.include_match(False)

@ script:python depends on cat3 && !notcat3 @
f << relevant.f;   // identifier
pf << relevant.pf; // position
ptf << prelevant.pf; // position after trafo
rets << relevant.rets;
@@

print "#%s" % pf[0].file
print "category3: FN:%s" % f
outputter(f,pf,ptf,rets,"category 3")
cocci.include_match(False)

@ script:python depends on always_ptr && !notcat4 @
f << relevant.f;   // identifier
pf << relevant.pf; // position
ptf << prelevant.pf; // position after trafo
rets << relevant.rets;
@@

print "#%s" % pf[0].file
print "category4: FN:%s" % f
outputter(f,pf,ptf,rets,"category 4")
cocci.include_match(False)

@ script:python@
f << relevant.f;   // identifier
pf << relevant.pf; // position
@@

print "#%s" % pf[0].file
print "category_none: FN:%s" % f