Re: [cocci] [PATCH 01/36] coccinelle: misc: add cond_return_no_effect.cocci
Sang-Heon Jeon <[email protected]> Fri, 24 Jul 2026 23:41:57 +0900
| Newsgroups | fr.inria.cocci,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CABFDxMEuccFb2zPoA_Fdc6jswhqzMN62At9kEFqGBL5DZOj3DQ@mail.gmail.com> |
On Fri, Jul 24, 2026 at 3:45=E2=80=AFAM Sang-Heon Jeon <[email protected]= om> wrote: > > Add a new Coccinelle script which removes a conditional return that > has no effect: > > if (ret) > return ret; > return ret; > > Both branches return the same value, so the check can be removed. > The condition can also be a negation or a comparison with a > constant. Such code is usually a leftover from removing a > statement between the two returns. > > When a local variable is assigned right before the check, the > assignment and the two returns turn into a single return of the > assigned expression, and the declaration is dropped if nothing > else uses the variable. Otherwise only the check is removed. > > The fold can delete comments between the check and the final > return, so the generated patch should be reviewed. > > Signed-off-by: Sang-Heon Jeon <[email protected]> > --- > .../misc/cond_return_no_effect.cocci | 121 ++++++++++++++++++ > 1 file changed, 121 insertions(+) > create mode 100644 scripts/coccinelle/misc/cond_return_no_effect.cocci > > diff --git a/scripts/coccinelle/misc/cond_return_no_effect.cocci b/script= s/coccinelle/misc/cond_return_no_effect.cocci > new file mode 100644 > index 000000000000..541aafdc32c6 > --- /dev/null > +++ b/scripts/coccinelle/misc/cond_return_no_effect.cocci > @@ -0,0 +1,121 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/// > +/// Remove a conditional return that has no effect: > +/// > +/// if (ret) > +/// return ret; > +/// return ret; > +/// > +/// Both branches return the same variable, so the check has no > +/// effect. It can also be a negation or a comparison with a > +/// constant. > +/// > +/// When a local variable is assigned right before the check, the > +/// assignment and the two returns turn into a single return of the > +/// assigned expression, and the declaration is dropped if nothing > +/// else uses the variable. Otherwise only the check is removed. > +/// > +// Such code is usually a leftover from removing a statement between > +// the two returns. > +// > +// Confidence: High > +// Copyright: (C) 2026 Sang-Heon Jeon > +// Comments: The fold can delete comments between the check and the > +// final return, so review the generated patch. > +// Options: --no-includes --include-headers > + > +virtual patch > +virtual context > +virtual org > +virtual report > + > +//---------------------------------------------------------- > +// For patch mode > +//---------------------------------------------------------- > + > +@collect depends on patch@ > +identifier ret; > +expression E; > +binary operator cmp =3D {<, <=3D, >, >=3D, =3D=3D, !=3D}; > +constant C; > +@@ > + ret =3D E; > + if (\(ret \| !ret \| ret cmp C\)) > + return ret; > + return ret; > + > +@depends on patch@ > +local idexpression ret; > +expression E; > +binary operator cmp =3D {<, <=3D, >, >=3D, =3D=3D, !=3D}; > +constant C; > +@@ > +- ret =3D E; > +- if (\(ret \| !ret \| ret cmp C\)) > +- return ret; > +- return ret; > ++ return E; > + If we can make these rules depend on an earlier rule, it should improve the performance a bit. I will look into whether it is worthwhile. > +@depends on patch@ > +idexpression ret; > +binary operator cmp =3D {<, <=3D, >, >=3D, =3D=3D, !=3D}; > +constant C; > +@@ > +- if (\(ret \| !ret \| ret cmp C\)) > +- return ret; > + return ret; > + > +@depends on patch@ > +type T; > +identifier collect.ret; > +@@ > +- T ret; > + ... when !=3D ret > + when strict > + > +@depends on patch@ > +type T; > +identifier collect.ret; > +constant C; > +@@ > +- T ret =3D C; > + ... when !=3D ret > + when strict > + While testing the script, I found that this rule can unexpectedly remove the declaration of a global or static variable in the same file, even when it is still used elsewhere. There is no such case in this series, but I will fix it in v2 by matching the declaration inside a function. fn(...) { ... - T ret; ... when !=3D ret when strict } > + > +//---------------------------------------------------------- > +// For context mode > +//---------------------------------------------------------- > + > +@depends on context@ > +idexpression ret; > +binary operator cmp =3D {<, <=3D, >, >=3D, =3D=3D, !=3D}; > +constant C; > +@@ > +* if (\(ret \| !ret \| ret cmp C\)) > +* return ret; > + return ret; > + > +//---------------------------------------------------------- > +// For org and report mode > +//---------------------------------------------------------- > + > +@r depends on org || report@ > +idexpression ret; > +binary operator cmp =3D {<, <=3D, >, >=3D, =3D=3D, !=3D}; > +constant C; > +position p; > +@@ > + if@p (\(ret \| !ret \| ret cmp C\)) > + return ret; > + return ret; > + > +@script:python depends on org@ > +p << r.p; > +@@ > +cocci.print_main("WARNING: conditional return with no effect (both branc= hes return the same value)", p) > + > +@script:python depends on report@ > +p << r.p; > +@@ > +coccilib.report.print_report(p[0], "WARNING: conditional return with no = effect (both branches return the same value)") > -- > 2.43.0 >