Re: [cocci] Searching for duplicate exception handling code with SmPL?

Julia Lawall <[email protected]> Sat, 13 Jun 2026 16:26:51 +0200 (CEST)
Newsgroups fr.inria.cocci
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-2042387485-1781360812=:28904
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8BIT

As usual, I have not idea what all your python code is supposed to do.  Is
it something other than the following:

@searching@
expression e, x;
identifier item, rc, work;
position p1, p2;
type T;
@@
 T work(...)
 {
 ... when any
 if (...)
 {
 ... when != rc = e
     when != item = x
 kfree@p1(item);
 return rc;
 }
 ... when any
 if (...)
 {
 ... when != rc = e
     when != item = x
 kfree@p2(item);
 return rc;
 }
 ... when any
 }

@script:ocaml@
p1 << searching.p1;
p2 << searching.p2;
@@

Printf.eprintf "duplicate on lines %d and %d\n" (List.hd p1).line (List.hd p2).line

This does print a report on your test data.

julia




On Sat, 13 Jun 2026, Markus Elfring wrote:

> >     But the desire is growing to reduce the presentation of unwanted
> >     false positives considerably.
> >     Thus there is a need to switch to more appropriate data formats.
> >     It seems then that multiple code occurrences can eventually be taken
> >     better into account with the help of SmPL position variables.
> >     Their information can be stored in databases probably also according
> >     to ACID criteria.
>
> Another SmPL script variant:
> @initialize:python@
> @@
> import sqlalchemy, sys
> sys.stderr.write("\n".join(["Using SQLAlchemy version:",
>                             sqlalchemy.__version__]))
> sys.stderr.write("\n")
> from sqlalchemy import Column, Integer, String, create_engine
> # outdated: from sqlalchemy.ext.declarative import declarative_base
> from sqlalchemy.orm import DeclarativeBase
> from sqlalchemy.orm import Mapped, mapped_column
> from sqlalchemy.orm import sessionmaker
> engine = create_engine("sqlite:///:memory:", echo=False)
> # outdated: base = declarative_base()
>
> class Base(DeclarativeBase):
>    pass
>
> class action(Base):
>    __tablename__ = "statements"
>    name: Mapped[str] = mapped_column(String, primary_key=True)
>    source_file: Mapped[str] = mapped_column(String, primary_key=True)
>    line: Mapped[int] = mapped_column(Integer, primary_key=True)
>    column: Mapped[int] = mapped_column(Integer, primary_key=True)
>    value1: Mapped[str] = mapped_column(String)
>    value2: Mapped[str] = mapped_column(String)
>
>    def __repr__(self):
>       return """<action(name='%s',
> source_file='%s',
> line='%s',
> column='%s',
> value1='%s',
> value2='%s')>""" % (self.name,
>                               self.source_file,
>                               self.line,
>                               self.column,
>                               self.value1,
>                               self.value2)
>
> class action2(Base):
>    __tablename__ = "statements2"
>    name: Mapped[str] = mapped_column(String, primary_key=True)
>    source_file: Mapped[str] = mapped_column(String, primary_key=True)
>    line: Mapped[int] = mapped_column(Integer, primary_key=True)
>    column: Mapped[int] = mapped_column(Integer, primary_key=True)
>    value1: Mapped[str] = mapped_column(String)
>    value2: Mapped[str] = mapped_column(String)
>
>    def __repr__(self):
>       return """<action2(name='%s',
> source_file='%s',
> line='%s',
> column='%s',
> value1='%s',
> value2='%s')>""" % (self.name,
>                               self.source_file,
>                               self.line,
>                               self.column,
>                               self.value1,
>                               self.value2)
>
> configured_session = sessionmaker(bind=engine)
> session = configured_session()
> # base.metadata.create_all(engine)
> #
> # See also:
> # https://stackoverflow.com/questions/70402667/how-to-use-create-all-for-sqlalchemy-orm-objects-across-files
>
> Base.metadata.create_all(engine)
>
> def store_data(fun, source1, source2, x, y):
>     """Add data to internal tables."""
>     for place in source1:
>        entry = action(name = fun,
>                       source_file = place.file,
>                       line = place.line,
>                       column = int(place.column) + 1,
>                       value1 = x,
>                       value2 = y)
>        session.add(entry)
>
>     for place in source2:
>        entry = action2(name = fun,
>                        source_file = place.file,
>                        line = place.line,
>                        column = int(place.column) + 1,
>                        value1 = x,
>                        value2 = y)
>        session.add(entry)
>
> @searching@
> expression e, x;
> identifier item, rc, work;
> position p1, p2;
> type T;
> @@
>  T work(...)
>  {
>  ... when any
>  if (...)
>  {
>  ... when != rc = e
>      when != item = x
>  kfree@p1(item);
>  return rc;
>  }
>  ... when any
>  if (...)
>  {
>  ... when != rc = e
>      when != item = x
>  kfree@p2(item);
>  return rc;
>  }
>  ... when any
>  }
>
> @script:python collection@
> fun << searching.work;
> x << searching.item;
> y << searching.rc;
> p1 << searching.p1;
> p2 << searching.p2;
> @@
> store_data(fun, p1, p2, x, y)
>
> @finalize:python@
> @@
> session.commit()
> from sqlalchemy import func
> entries = session.query(func.count("*")).select_from(action).scalar()
>
> if entries > 0:
>    from sqlalchemy import Index, Table, MetaData, select, text
>    from sqlalchemy.engine.reflection import Inspector
>    from sqlalchemy.sql import literal_column
>
>    pairs = Index("pairs", action.value1, action.value2)
>    pairs.create(engine)
>
>    # See also:
>    # https://stackoverflow.com/questions/30575111/how-to-create-a-new-table-from-select-statement-in-sqlalchemy#answer-30577608
>
>    q = session.query(action.value1, action.value2, action.name, action.source_file,
>                      func.count(literal_column("*")).label("C")
>                     ).group_by(action.value1,
>                                action.value2,
>                                action.name,
>                                action.source_file) \
>                      .having(func.count(literal_column("*")) > literal_column("1"))
>    session.execute(text('create table t2 as ' + str(q.statement)))
>
>    class results(Base):
>        __table__ = Table("t2", Base.metadata, autoload_with = session.connection())
>        __mapper_args__ = {
>            "primary_key": [__table__.c.source_file,
>                            __table__.c.name,
>                            __table__.c.value1,
>                            __table__.c.value2]
>                          }
>
>    entries2 = session.query(func.count("*")).select_from(results).scalar()
>
>    if entries2 > 0:
>       delimiter = "|"
>       sys.stdout.write(delimiter.join(["value1",
>                                        "value2",
>                                        '"function name"',
>                                        '"source file"',
>                                        "incidence"]))
>       sys.stdout.write("\r\n")
>       for value1, value2, name, source_file, incidence \
>           in session.query(results.value1,
>                            results.value2,
>                            results.name,
>                            results.source_file,
>                            results.C).order_by(results.source_file,
>                                                results.name,
>                                                results.value1,
>                                                results.value2):
>          sys.stdout.write(delimiter.join([value1,
>                                           value2,
>                                           name,
>                                           source_file,
>                                           str(incidence)]))
>          sys.stdout.write("\r\n")
>    else:
>       sys.stderr.write("Duplicate statements were not determined from "
>                        + str(entries) + " records.\n")
>       delimiter = "|"
>       sys.stderr.write(delimiter.join(["value1",
>                                        "value2",
>                                        '"function name"',
>                                        '"source file"',
>                                        "line"]))
>       sys.stderr.write("\r\n")
>       for value1, value2, name, source_file, line \
>           in session.query(action.value1,
>                            action.value2,
>                            action.name,
>                            action.source_file,
>                            action.line).order_by(action.source_file,
>                                                action.name,
>                                                action.value1,
>                                                action.value2,
>                                                action.line):
>          sys.stderr.write(delimiter.join([value1,
>                                           value2,
>                                           name,
>                                           source_file,
>                                           str(line)]))
>          sys.stderr.write("\r\n")
> else:
>    sys.stderr.write("No result for this analysis!\n")
>
>
>
> Corresponding source file example:
> https://elixir.bootlin.com/linux/v7.1-rc7/source/sound/core/seq/seq_fifo.c#L17-L47
>
>
> // SPDX-License-Identifier: GPL-2.0-or-later
> // deleted part
> struct snd_seq_fifo *snd_seq_fifo_new(int poolsize)
> {
> 	struct snd_seq_fifo *f;
>
> 	f = kzalloc_obj(*f);
> 	if (!f)
> 		return NULL;
>
> 	f->pool = snd_seq_pool_new(poolsize);
> 	if (f->pool == NULL) {
> 		kfree(f);
> 		return NULL;
> 	}
> 	if (snd_seq_pool_init(f->pool) < 0) {
> 		snd_seq_pool_delete(&f->pool);
> 		kfree(f);
> 		return NULL;
> 	}
> // deleted part
> 	return f;
> }
> // deleted part
>
>
>
> Questionable test results:
> Markus_Elfring@Sonne:…/Projekte/Linux/next-analyses> git checkout next-20260608 && time /usr/bin/spatch …/Projekte/Coccinelle/janitor/list_selected_duplicate_statements_in_if_branches.cocci sound/core/seq/seq_fifo.c
> …
> Using SQLAlchemy version:
> 2.0.49
> …
> Duplicate statements were not determined from 1 records.
> value1|value2|"function name"|"source file"|line
> f|NULL|snd_seq_fifo_new|sound/core/seq/seq_fifo.c|28
>
> real    0m0,816s
> user    0m0,658s
> sys     0m0,148s
>
> Markus_Elfring@Sonne:…/Projekte/Coccinelle/Probe> time /usr/bin/spatch ../janitor/list_selected_duplicate_statements_in_if_branches.cocci snd_seq_fifo_new-excerpt-20260608.c
> …
> Using SQLAlchemy version:
> 2.0.49
> …
> Duplicate statements were not determined from 1 records.
> value1|value2|"function name"|"source file"|line
> f|NULL|snd_seq_fifo_new|snd_seq_fifo_new-excerpt-20260608.c|13
>
> real    0m0,785s
> user    0m0,649s
> sys     0m0,128s
>
>
>
> Would anybody like to explain the different determined values?
>
> How can presented expertise challenges be resolved?
>
> Regards,
> Markus
>
--8323329-2042387485-1781360812=:28904--