Re: [cocci] [PATCH 01/36] coccinelle: misc: add cond_return_no_effect.cocci

Julia Lawall <[email protected]> Fri, 24 Jul 2026 16:55:42 +0200 (CEST)
Newsgroups fr.inria.cocci,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
  This message is in MIME format.  The first part should be readable text,
  while the remaining parts are likely unreadable without MIME-aware tools.

--8323329-1189440701-1784904942=:3487
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8BIT



On Fri, 24 Jul 2026, Sang-Heon Jeon wrote:

> On Fri, Jul 24, 2026 at 3:45 AM Sang-Heon Jeon <[email protected]> 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/scripts/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 = {<, <=, >, >=, ==, !=};
> > +constant C;
> > +@@
> > +       ret = E;
> > +       if (\(ret \| !ret \| ret cmp C\))
> > +               return ret;
> > +       return ret;
> > +
> > +@depends on patch@
> > +local idexpression ret;
> > +expression E;
> > +binary operator cmp = {<, <=, >, >=, ==, !=};
> > +constant C;
> > +@@
> > +-      ret = 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.

I don't see an opportunity for more efficiency for the above rules, but go
ahead if you have some idea.

>
> > +@depends on patch@
> > +idexpression ret;
> > +binary operator cmp = {<, <=, >, >=, ==, !=};
> > +constant C;
> > +@@
> > +-      if (\(ret \| !ret \| ret cmp C\))
> > +-              return ret;
> > +       return ret;
> > +
> > +@depends on patch@
> > +type T;
> > +identifier collect.ret;
> > +@@
> > +-      T ret;
> > +       ... when != ret
> > +           when strict
> > +
> > +@depends on patch@
> > +type T;
> > +identifier collect.ret;
> > +constant C;
> > +@@
> > +-      T ret = C;
> > +       ... when != 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.

I think you could avoid matching the path from the top of the function to
the declaration by doing the following:

declaration D;
statement S;

(
- T ret = C
(
  D
|
  S
)
&
  T ret = C;
  ... when != ret
      when strict
)

The first part ensures that it is not a top-level declaration and the
second part does the check you had previously.

To ensure that it doesn't match "static T ret = C;" you can put the
following in the @depends on patch@:

disable optional_storage

julia


> fn(...) {
>       ...
> -     T ret;
>       ... when != ret
>           when strict
> }
>
> > +
> > +//----------------------------------------------------------
> > +//  For context mode
> > +//----------------------------------------------------------
> > +
> > +@depends on context@
> > +idexpression ret;
> > +binary operator cmp = {<, <=, >, >=, ==, !=};
> > +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 = {<, <=, >, >=, ==, !=};
> > +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 branches 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
> >
>
--8323329-1189440701-1784904942=:3487--