summaryrefslogtreecommitdiffstats
path: root/contrib/coccinelle
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 14:47:53 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 14:47:53 +0000
commitc8bae7493d2f2910b57f13ded012e86bdcfb0532 (patch)
tree24e09d9f84dec336720cf393e156089ca2835791 /contrib/coccinelle
parentInitial commit. (diff)
downloadgit-c8bae7493d2f2910b57f13ded012e86bdcfb0532.tar.xz
git-c8bae7493d2f2910b57f13ded012e86bdcfb0532.zip
Adding upstream version 1:2.39.2.upstream/1%2.39.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/coccinelle')
-rw-r--r--contrib/coccinelle/.gitignore1
-rw-r--r--contrib/coccinelle/README92
-rw-r--r--contrib/coccinelle/array.cocci96
-rw-r--r--contrib/coccinelle/commit.cocci52
-rw-r--r--contrib/coccinelle/equals-null.cocci30
-rw-r--r--contrib/coccinelle/flex_alloc.cocci13
-rw-r--r--contrib/coccinelle/free.cocci45
-rw-r--r--contrib/coccinelle/hashmap.cocci16
-rw-r--r--contrib/coccinelle/index-compatibility.cocci139
-rw-r--r--contrib/coccinelle/index-compatibility.pending.cocci24
-rw-r--r--contrib/coccinelle/object_id.cocci75
-rw-r--r--contrib/coccinelle/preincr.cocci5
-rw-r--r--contrib/coccinelle/qsort.cocci37
-rwxr-xr-xcontrib/coccinelle/spatchcache304
-rw-r--r--contrib/coccinelle/strbuf.cocci62
-rw-r--r--contrib/coccinelle/swap.cocci28
-rw-r--r--contrib/coccinelle/tests/free.c11
-rw-r--r--contrib/coccinelle/tests/free.res9
-rw-r--r--contrib/coccinelle/tests/unused.c82
-rw-r--r--contrib/coccinelle/tests/unused.res45
-rw-r--r--contrib/coccinelle/the_repository.pending.cocci128
-rw-r--r--contrib/coccinelle/unused.cocci43
-rw-r--r--contrib/coccinelle/xcalloc.cocci10
-rw-r--r--contrib/coccinelle/xopen.cocci19
-rw-r--r--contrib/coccinelle/xstrdup_or_null.cocci5
25 files changed, 1371 insertions, 0 deletions
diff --git a/contrib/coccinelle/.gitignore b/contrib/coccinelle/.gitignore
new file mode 100644
index 0000000..1d45c0a
--- /dev/null
+++ b/contrib/coccinelle/.gitignore
@@ -0,0 +1 @@
+*.patch
diff --git a/contrib/coccinelle/README b/contrib/coccinelle/README
new file mode 100644
index 0000000..d1daa1f
--- /dev/null
+++ b/contrib/coccinelle/README
@@ -0,0 +1,92 @@
+This directory provides examples of Coccinelle (http://coccinelle.lip6.fr/)
+semantic patches that might be useful to developers.
+
+There are two types of semantic patches:
+
+ * Using the semantic transformation to check for bad patterns in the code;
+ The target 'make coccicheck' is designed to check for these patterns and
+ it is expected that any resulting patch indicates a regression.
+ The patches resulting from 'make coccicheck' are small and infrequent,
+ so once they are found, they can be sent to the mailing list as per usual.
+
+ Example for introducing new patterns:
+ 67947c34ae (convert "hashcmp() != 0" to "!hasheq()", 2018-08-28)
+ b84c783882 (fsck: s/++i > 1/i++/, 2018-10-24)
+
+ Example of fixes using this approach:
+ 248f66ed8e (run-command: use strbuf_addstr() for adding a string to
+ a strbuf, 2018-03-25)
+ f919ffebed (Use MOVE_ARRAY, 2018-01-22)
+
+ These types of semantic patches are usually part of testing, c.f.
+ 0860a7641b (travis-ci: fail if Coccinelle static analysis found something
+ to transform, 2018-07-23)
+
+ * Using semantic transformations in large scale refactorings throughout
+ the code base.
+
+ When applying the semantic patch into a real patch, sending it to the
+ mailing list in the usual way, such a patch would be expected to have a
+ lot of textual and semantic conflicts as such large scale refactorings
+ change function signatures that are used widely in the code base.
+ A textual conflict would arise if surrounding code near any call of such
+ function changes. A semantic conflict arises when other patch series in
+ flight introduce calls to such functions.
+
+ So to aid these large scale refactorings, semantic patches can be used.
+ However we do not want to store them in the same place as the checks for
+ bad patterns, as then automated builds would fail.
+ That is why semantic patches 'contrib/coccinelle/*.pending.cocci'
+ are ignored for checks, and can be applied using 'make coccicheck-pending'.
+
+ This allows to expose plans of pending large scale refactorings without
+ impacting the bad pattern checks.
+
+Git-specific tips & things to know about how we run "spatch":
+
+ * The "make coccicheck" will piggy-back on
+ "COMPUTE_HEADER_DEPENDENCIES". If you've built a given object file
+ the "coccicheck" target will consider its depednency to decide if
+ it needs to re-run on the corresponding source file.
+
+ This means that a "make coccicheck" will re-compile object files
+ before running. This might be unexpected, but speeds up the run in
+ the common case, as e.g. a change to "column.h" won't require all
+ coccinelle rules to be re-run against "grep.c" (or another file
+ that happens not to use "column.h").
+
+ To disable this behavior use the "SPATCH_USE_O_DEPENDENCIES=NoThanks"
+ flag.
+
+ * To speed up our rules the "make coccicheck" target will by default
+ concatenate all of the *.cocci files here into an "ALL.cocci", and
+ apply it to each source file.
+
+ This makes the run faster, as we don't need to run each rule
+ against each source file. See the Makefile for further discussion,
+ this behavior can be disabled with "SPATCH_CONCAT_COCCI=".
+
+ But since they're concatenated any <id> in the <rulname> (e.g. "@
+ my_name", v.s. anonymous "@@") needs to be unique across all our
+ *.cocci files. You should only need to name rules if other rules
+ depend on them (currently only one rule is named).
+
+ * To speed up incremental runs even more use the "spatchcache" tool
+ in this directory as your "SPATCH". It aimns to be a "ccache" for
+ coccinelle, and piggy-backs on "COMPUTE_HEADER_DEPENDENCIES".
+
+ It caches in Redis by default, see it source for a how-to.
+
+ In one setup with a primed cache "make coccicheck" followed by a
+ "make clean && make" takes around 10s to run, but 2m30s with the
+ default of "SPATCH_CONCAT_COCCI=Y".
+
+ With "SPATCH_CONCAT_COCCI=" the total runtime is around ~6m, sped
+ up to ~1m with "spatchcache".
+
+ Most of the 10s (or ~1m) being spent on re-running "spatch" on
+ files we couldn't cache, as we didn't compile them (in contrib/*
+ and compat/* mostly).
+
+ The absolute times will differ for you, but the relative speedup
+ from caching should be on that order.
diff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci
new file mode 100644
index 0000000..aa75937
--- /dev/null
+++ b/contrib/coccinelle/array.cocci
@@ -0,0 +1,96 @@
+@@
+type T;
+T *dst_ptr;
+T *src_ptr;
+expression n;
+@@
+- memcpy(dst_ptr, src_ptr, (n) * \( sizeof(T)
+- \| sizeof(*(dst_ptr))
+- \| sizeof(*(src_ptr))
+- \| sizeof(dst_ptr[...])
+- \| sizeof(src_ptr[...])
+- \) )
++ COPY_ARRAY(dst_ptr, src_ptr, n)
+
+@@
+type T;
+T *dst_ptr;
+T[] src_arr;
+expression n;
+@@
+- memcpy(dst_ptr, src_arr, (n) * \( sizeof(T)
+- \| sizeof(*(dst_ptr))
+- \| sizeof(*(src_arr))
+- \| sizeof(dst_ptr[...])
+- \| sizeof(src_arr[...])
+- \) )
++ COPY_ARRAY(dst_ptr, src_arr, n)
+
+@@
+type T;
+T[] dst_arr;
+T *src_ptr;
+expression n;
+@@
+- memcpy(dst_arr, src_ptr, (n) * \( sizeof(T)
+- \| sizeof(*(dst_arr))
+- \| sizeof(*(src_ptr))
+- \| sizeof(dst_arr[...])
+- \| sizeof(src_ptr[...])
+- \) )
++ COPY_ARRAY(dst_arr, src_ptr, n)
+
+@@
+type T;
+T[] dst_arr;
+T[] src_arr;
+expression n;
+@@
+- memcpy(dst_arr, src_arr, (n) * \( sizeof(T)
+- \| sizeof(*(dst_arr))
+- \| sizeof(*(src_arr))
+- \| sizeof(dst_arr[...])
+- \| sizeof(src_arr[...])
+- \) )
++ COPY_ARRAY(dst_arr, src_arr, n)
+
+@@
+type T;
+T *dst;
+T *src;
+expression n;
+@@
+(
+- memmove(dst, src, (n) * sizeof(*dst));
++ MOVE_ARRAY(dst, src, n);
+|
+- memmove(dst, src, (n) * sizeof(*src));
++ MOVE_ARRAY(dst, src, n);
+|
+- memmove(dst, src, (n) * sizeof(T));
++ MOVE_ARRAY(dst, src, n);
+)
+
+@@
+type T;
+T *ptr;
+expression n;
+@@
+- ptr = xmalloc((n) * sizeof(*ptr));
++ ALLOC_ARRAY(ptr, n);
+
+@@
+type T;
+T *ptr;
+expression n;
+@@
+- ptr = xmalloc((n) * sizeof(T));
++ ALLOC_ARRAY(ptr, n);
+
+@@
+type T;
+T *ptr;
+expression n != 1;
+@@
+- ptr = xcalloc(n, \( sizeof(*ptr) \| sizeof(T) \) )
++ CALLOC_ARRAY(ptr, n)
diff --git a/contrib/coccinelle/commit.cocci b/contrib/coccinelle/commit.cocci
new file mode 100644
index 0000000..af6dd4c
--- /dev/null
+++ b/contrib/coccinelle/commit.cocci
@@ -0,0 +1,52 @@
+@@
+expression c;
+@@
+- &c->maybe_tree->object.oid
++ get_commit_tree_oid(c)
+
+@@
+expression c;
+@@
+- c->maybe_tree->object.oid.hash
++ get_commit_tree_oid(c)->hash
+
+@@
+identifier f !~ "^set_commit_tree$";
+expression c;
+expression s;
+@@
+ f(...) {<...
+- c->maybe_tree = s
++ set_commit_tree(c, s)
+ ...>}
+
+// These excluded functions must access c->maybe_tree directly.
+// Note that if c->maybe_tree is written somewhere outside of these
+// functions, then the recommended transformation will be bogus with
+// repo_get_commit_tree() on the LHS.
+@@
+identifier f !~ "^(repo_get_commit_tree|get_commit_tree_in_graph_one|load_tree_for_commit|set_commit_tree)$";
+expression c;
+@@
+ f(...) {<...
+- c->maybe_tree
++ repo_get_commit_tree(specify_the_right_repo_here, c)
+ ...>}
+
+@@
+struct commit *c;
+expression E;
+@@
+(
+- c->generation = E;
++ commit_graph_data_at(c)->generation = E;
+|
+- c->graph_pos = E;
++ commit_graph_data_at(c)->graph_pos = E;
+|
+- c->generation
++ commit_graph_generation(c)
+|
+- c->graph_pos
++ commit_graph_position(c)
+)
diff --git a/contrib/coccinelle/equals-null.cocci b/contrib/coccinelle/equals-null.cocci
new file mode 100644
index 0000000..92c7054
--- /dev/null
+++ b/contrib/coccinelle/equals-null.cocci
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+@@
+expression e;
+statement s;
+@@
+if (
+(
+!e
+|
+- e == NULL
++ !e
+)
+ )
+ {...}
+else s
+
+@@
+expression e;
+statement s;
+@@
+if (
+(
+e
+|
+- e != NULL
++ e
+)
+ )
+ {...}
+else s
diff --git a/contrib/coccinelle/flex_alloc.cocci b/contrib/coccinelle/flex_alloc.cocci
new file mode 100644
index 0000000..e9f7f6d
--- /dev/null
+++ b/contrib/coccinelle/flex_alloc.cocci
@@ -0,0 +1,13 @@
+@@
+expression str;
+identifier x, flexname;
+@@
+- FLEX_ALLOC_MEM(x, flexname, str, strlen(str));
++ FLEX_ALLOC_STR(x, flexname, str);
+
+@@
+expression str;
+identifier x, ptrname;
+@@
+- FLEXPTR_ALLOC_MEM(x, ptrname, str, strlen(str));
++ FLEXPTR_ALLOC_STR(x, ptrname, str);
diff --git a/contrib/coccinelle/free.cocci b/contrib/coccinelle/free.cocci
new file mode 100644
index 0000000..6fb9eb6
--- /dev/null
+++ b/contrib/coccinelle/free.cocci
@@ -0,0 +1,45 @@
+@@
+expression E;
+@@
+- if (E)
+(
+ free(E);
+|
+ free_commit_list(E);
+)
+
+@@
+expression E;
+@@
+- if (!E)
+(
+ free(E);
+|
+ free_commit_list(E);
+)
+
+@@
+expression E;
+@@
+- free(E);
++ FREE_AND_NULL(E);
+- E = NULL;
+
+@@
+expression E;
+@@
+- if (E)
+- {
+ free_commit_list(E);
+ E = NULL;
+- }
+
+@@
+expression E;
+statement S;
+@@
+- if (E) {
++ if (E)
+ S
+ free_commit_list(E);
+- }
diff --git a/contrib/coccinelle/hashmap.cocci b/contrib/coccinelle/hashmap.cocci
new file mode 100644
index 0000000..c5dbb45
--- /dev/null
+++ b/contrib/coccinelle/hashmap.cocci
@@ -0,0 +1,16 @@
+@@
+expression E;
+struct hashmap_entry HME;
+@@
+- HME.hash = E;
++ hashmap_entry_init(&HME, E);
+
+@@
+identifier f !~ "^hashmap_entry_init$";
+expression E;
+struct hashmap_entry *HMEP;
+@@
+ f(...) {<...
+- HMEP->hash = E;
++ hashmap_entry_init(HMEP, E);
+ ...>}
diff --git a/contrib/coccinelle/index-compatibility.cocci b/contrib/coccinelle/index-compatibility.cocci
new file mode 100644
index 0000000..8520f03
--- /dev/null
+++ b/contrib/coccinelle/index-compatibility.cocci
@@ -0,0 +1,139 @@
+// the_index.* variables
+@@
+identifier AC = active_cache;
+identifier ACC = active_cache_changed;
+identifier ACT = active_cache_tree;
+@@
+(
+- AC
++ the_index.cache
+|
+- ACC
++ the_index.cache_changed
+|
+- ACT
++ the_index.cache_tree
+)
+
+@@
+identifier AN = active_nr;
+identifier f != prepare_to_commit;
+@@
+ f(...) {<...
+- AN
++ the_index.cache_nr
+ ...>}
+
+// "the_repository" simple cases
+@@
+@@
+(
+- read_cache_unmerged
++ repo_read_index_unmerged
+|
+- hold_locked_index
++ repo_hold_locked_index
+)
+ (
++ the_repository,
+ ...)
+
+// "the_repository" special-cases
+@@
+@@
+(
+- read_cache_preload
++ repo_read_index_preload
+)
+ (
++ the_repository,
+ ...
++ , 0
+ )
+
+// "the_index" simple cases
+@@
+@@
+(
+- is_cache_unborn
++ is_index_unborn
+|
+- unmerged_cache
++ unmerged_index
+|
+- rename_cache_entry_at
++ rename_index_entry_at
+|
+- chmod_cache_entry
++ chmod_index_entry
+|
+- cache_file_exists
++ index_file_exists
+|
+- cache_name_is_other
++ index_name_is_other
+|
+- unmerge_cache_entry_at
++ unmerge_index_entry_at
+|
+- add_to_cache
++ add_to_index
+|
+- add_file_to_cache
++ add_file_to_index
+|
+- add_cache_entry
++ add_index_entry
+|
+- remove_file_from_cache
++ remove_file_from_index
+|
+- ce_match_stat
++ ie_match_stat
+|
+- ce_modified
++ ie_modified
+|
+- resolve_undo_clear
++ resolve_undo_clear_index
+)
+ (
++ &the_index,
+ ...)
+
+@@
+@@
+(
+- refresh_and_write_cache
++ repo_refresh_and_write_index
+)
+ (
++ the_repository,
+ ...
++ , NULL, NULL, NULL
+ )
+
+// "the_index" special-cases
+@@
+@@
+(
+- read_cache_from
++ read_index_from
+)
+ (
++ &the_index,
+ ...
++ , get_git_dir()
+ )
+
+@@
+@@
+(
+- refresh_cache
++ refresh_index
+)
+ (
++ &the_index,
+ ...
++ , NULL, NULL, NULL
+ )
diff --git a/contrib/coccinelle/index-compatibility.pending.cocci b/contrib/coccinelle/index-compatibility.pending.cocci
new file mode 100644
index 0000000..01f875d
--- /dev/null
+++ b/contrib/coccinelle/index-compatibility.pending.cocci
@@ -0,0 +1,24 @@
+// "the_repository" simple cases
+@@
+@@
+(
+- read_cache
++ repo_read_index
+)
+ (
++ the_repository,
+ ...)
+
+// "the_index" simple cases
+@@
+@@
+(
+- discard_cache
++ discard_index
+|
+- cache_name_pos
++ index_name_pos
+)
+ (
++ &the_index,
+ ...)
diff --git a/contrib/coccinelle/object_id.cocci b/contrib/coccinelle/object_id.cocci
new file mode 100644
index 0000000..01f8d69
--- /dev/null
+++ b/contrib/coccinelle/object_id.cocci
@@ -0,0 +1,75 @@
+@@
+struct object_id OID;
+@@
+- hashclr(OID.hash)
++ oidclr(&OID)
+
+@@
+identifier f != oidclr;
+struct object_id *OIDPTR;
+@@
+ f(...) {<...
+- hashclr(OIDPTR->hash)
++ oidclr(OIDPTR)
+ ...>}
+
+@@
+struct object_id OID1, OID2;
+@@
+- hashcmp(OID1.hash, OID2.hash)
++ oidcmp(&OID1, &OID2)
+
+@@
+identifier f != oidcmp;
+struct object_id *OIDPTR1, OIDPTR2;
+@@
+ f(...) {<...
+- hashcmp(OIDPTR1->hash, OIDPTR2->hash)
++ oidcmp(OIDPTR1, OIDPTR2)
+ ...>}
+
+@@
+struct object_id *OIDPTR;
+struct object_id OID;
+@@
+- hashcmp(OIDPTR->hash, OID.hash)
++ oidcmp(OIDPTR, &OID)
+
+@@
+struct object_id *OIDPTR;
+struct object_id OID;
+@@
+- hashcmp(OID.hash, OIDPTR->hash)
++ oidcmp(&OID, OIDPTR)
+
+@@
+struct object_id *OIDPTR1;
+struct object_id *OIDPTR2;
+@@
+- oidcmp(OIDPTR1, OIDPTR2) == 0
++ oideq(OIDPTR1, OIDPTR2)
+
+@@
+identifier f != hasheq;
+expression E1, E2;
+@@
+ f(...) {<...
+- hashcmp(E1, E2) == 0
++ hasheq(E1, E2)
+ ...>}
+
+@@
+struct object_id *OIDPTR1;
+struct object_id *OIDPTR2;
+@@
+- oidcmp(OIDPTR1, OIDPTR2) != 0
++ !oideq(OIDPTR1, OIDPTR2)
+
+@@
+identifier f != hasheq;
+expression E1, E2;
+@@
+ f(...) {<...
+- hashcmp(E1, E2) != 0
++ !hasheq(E1, E2)
+ ...>}
diff --git a/contrib/coccinelle/preincr.cocci b/contrib/coccinelle/preincr.cocci
new file mode 100644
index 0000000..ae42cb0
--- /dev/null
+++ b/contrib/coccinelle/preincr.cocci
@@ -0,0 +1,5 @@
+@@
+identifier i;
+@@
+- ++i > 1
++ i++
diff --git a/contrib/coccinelle/qsort.cocci b/contrib/coccinelle/qsort.cocci
new file mode 100644
index 0000000..22b93a9
--- /dev/null
+++ b/contrib/coccinelle/qsort.cocci
@@ -0,0 +1,37 @@
+@@
+expression base, nmemb, compar;
+@@
+- qsort(base, nmemb, sizeof(*base), compar);
++ QSORT(base, nmemb, compar);
+
+@@
+expression base, nmemb, compar;
+@@
+- qsort(base, nmemb, sizeof(base[0]), compar);
++ QSORT(base, nmemb, compar);
+
+@@
+type T;
+T *base;
+expression nmemb, compar;
+@@
+- qsort(base, nmemb, sizeof(T), compar);
++ QSORT(base, nmemb, compar);
+
+@@
+expression base, nmemb, compar;
+@@
+- if (nmemb)
+ QSORT(base, nmemb, compar);
+
+@@
+expression base, nmemb, compar;
+@@
+- if (nmemb > 0)
+ QSORT(base, nmemb, compar);
+
+@@
+expression base, nmemb, compar;
+@@
+- if (nmemb > 1)
+ QSORT(base, nmemb, compar);
diff --git a/contrib/coccinelle/spatchcache b/contrib/coccinelle/spatchcache
new file mode 100755
index 0000000..29e9352
--- /dev/null
+++ b/contrib/coccinelle/spatchcache
@@ -0,0 +1,304 @@
+#!/bin/sh
+#
+# spatchcache: a poor-man's "ccache"-alike for "spatch" in git.git
+#
+# This caching command relies on the peculiarities of the Makefile
+# driving "spatch" in git.git, in particular if we invoke:
+#
+# make
+# # See "spatchCache.cacheWhenStderr" for why "--very-quiet" is
+# # used
+# make coccicheck SPATCH_FLAGS=--very-quiet
+#
+# We can with COMPUTE_HEADER_DEPENDENCIES (auto-detected as true with
+# "gcc" and "clang") write e.g. a .depend/grep.o.d for grep.c, when we
+# compile grep.o.
+#
+# The .depend/grep.o.d will have the full header dependency tree of
+# grep.c, and we can thus cache the output of "spatch" by:
+#
+# 1. Hashing all of those files
+# 2. Hashing our source file, and the *.cocci rule we're
+# applying
+# 3. Running spatch, if suggests no changes (by far the common
+# case) we invoke "spatchCache.getCmd" and
+# "spatchCache.setCmd" with a hash SHA-256 to ask "does this
+# ID have no changes" or "say that ID had no changes>
+# 4. If no "spatchCache.{set,get}Cmd" is specified we'll use
+# "redis-cli" and maintain a SET called "spatch-cache". Set
+# appropriate redis memory policies to keep it from growing
+# out of control.
+#
+# This along with the general incremental "make" support for
+# "contrib/coccinelle" makes it viable to (re-)run coccicheck
+# e.g. when merging integration branches.
+#
+# Note that the "--very-quiet" flag is currently critical. The cache
+# will refuse to cache anything that has output on STDERR (which might
+# be errors from spatch), but see spatchCache.cacheWhenStderr below.
+#
+# The STDERR (and exit code) could in principle be cached (as with
+# ccache), but then the simple structure in the Redis cache would need
+# to change, so just supply "--very-quiet" for now.
+#
+# To use this, simply set SPATCH to
+# contrib/coccinelle/spatchcache. Then optionally set:
+#
+# [spatchCache]
+# # Optional: path to a custom spatch
+# spatch = ~/g/coccicheck/spatch.opt
+#
+# As well as this trace config (debug implies trace):
+#
+# cacheWhenStderr = true
+# trace = false
+# debug = false
+#
+# The ".depend/grep.o.d" can also be customized, as a string that will
+# be eval'd, it has access to a "$dirname" and "$basename":
+#
+# [spatchCache]
+# dependFormat = "$dirname/.depend/${basename%.c}.o.d"
+#
+# Setting "trace" to "true" allows for seeing when we have a cache HIT
+# or MISS. To debug whether the cache is working do that, and run e.g.:
+#
+# redis-cli FLUSHALL
+# <make && make coccicheck, as above>
+# grep -hore HIT -e MISS -e SET -e NOCACHE -e CANTCACHE .build/contrib/coccinelle | sort | uniq -c
+# 600 CANTCACHE
+# 7365 MISS
+# 7365 SET
+#
+# A subsequent "make cocciclean && make coccicheck" should then have
+# all "HIT"'s and "CANTCACHE"'s.
+#
+# The "spatchCache.cacheWhenStderr" option is critical when using
+# spatchCache.{trace,debug} to debug whether something is set in the
+# cache, as we'll write to the spatch logs in .build/* we'd otherwise
+# always emit a NOCACHE.
+#
+# Reading the config can make the command much slower, to work around
+# this the config can be set in the environment, with environment
+# variable name corresponding to the config key. "default" can be used
+# to use whatever's the script default, e.g. setting
+# spatchCache.cacheWhenStderr=true and deferring to the defaults for
+# the rest is:
+#
+# export GIT_CONTRIB_SPATCHCACHE_DEBUG=default
+# export GIT_CONTRIB_SPATCHCACHE_TRACE=default
+# export GIT_CONTRIB_SPATCHCACHE_CACHEWHENSTDERR=true
+# export GIT_CONTRIB_SPATCHCACHE_SPATCH=default
+# export GIT_CONTRIB_SPATCHCACHE_DEPENDFORMAT=default
+# export GIT_CONTRIB_SPATCHCACHE_SETCMD=default
+# export GIT_CONTRIB_SPATCHCACHE_GETCMD=default
+
+set -e
+
+env_or_config () {
+ env="$1"
+ shift
+ if test "$env" = "default"
+ then
+ # Avoid expensive "git config" invocation
+ return
+ elif test -n "$env"
+ then
+ echo "$env"
+ else
+ git config $@ || :
+ fi
+}
+
+## Our own configuration & options
+debug=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_DEBUG" --bool "spatchCache.debug")
+if test "$debug" != "true"
+then
+ debug=
+fi
+if test -n "$debug"
+then
+ set -x
+fi
+
+trace=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_TRACE" --bool "spatchCache.trace")
+if test "$trace" != "true"
+then
+ trace=
+fi
+if test -n "$debug"
+then
+ # debug implies trace
+ trace=true
+fi
+
+cacheWhenStderr=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_CACHEWHENSTDERR" --bool "spatchCache.cacheWhenStderr")
+if test "$cacheWhenStderr" != "true"
+then
+ cacheWhenStderr=
+fi
+
+trace_it () {
+ if test -z "$trace"
+ then
+ return
+ fi
+ echo "$@" >&2
+}
+
+spatch=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_SPATCH" --path "spatchCache.spatch")
+if test -n "$spatch"
+then
+ if test -n "$debug"
+ then
+ trace_it "custom spatchCache.spatch='$spatch'"
+ fi
+else
+ spatch=spatch
+fi
+
+dependFormat='$dirname/.depend/${basename%.c}.o.d'
+dependFormatCfg=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_DEPENDFORMAT" "spatchCache.dependFormat")
+if test -n "$dependFormatCfg"
+then
+ dependFormat="$dependFormatCfg"
+fi
+
+set=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_SETCMD" "spatchCache.setCmd")
+get=$(env_or_config "$GIT_CONTRIB_SPATCHCACHE_GETCMD" "spatchCache.getCmd")
+
+## Parse spatch()-like command-line for caching info
+arg_sp=
+arg_file=
+args="$@"
+spatch_opts() {
+ while test $# != 0
+ do
+ arg_file="$1"
+ case "$1" in
+ --sp-file)
+ arg_sp="$2"
+ ;;
+ esac
+ shift
+ done
+}
+spatch_opts "$@"
+if ! test -f "$arg_file"
+then
+ arg_file=
+fi
+
+hash_for_cache() {
+ # Parameters that should affect the cache
+ echo "args=$args"
+ echo "config spatchCache.spatch=$spatch"
+ echo "config spatchCache.debug=$debug"
+ echo "config spatchCache.trace=$trace"
+ echo "config spatchCache.cacheWhenStderr=$cacheWhenStderr"
+ echo
+
+ # Our target file and its dependencies
+ git hash-object "$1" "$2" $(grep -E -o '^[^:]+:$' "$3" | tr -d ':')
+}
+
+# Sanity checks
+if ! test -f "$arg_sp" && ! test -f "$arg_file"
+then
+ echo $0: no idea how to cache "$@" >&2
+ exit 128
+fi
+
+# Main logic
+dirname=$(dirname "$arg_file")
+basename=$(basename "$arg_file")
+eval "dep=$dependFormat"
+
+if ! test -f "$dep"
+then
+ trace_it "$0: CANTCACHE have no '$dep' for '$arg_file'!"
+ exec "$spatch" "$@"
+fi
+
+if test -n "$debug"
+then
+ trace_it "$0: The full cache input for '$arg_sp' '$arg_file' '$dep'"
+ hash_for_cache "$arg_sp" "$arg_file" "$dep" >&2
+fi
+sum=$(hash_for_cache "$arg_sp" "$arg_file" "$dep" | git hash-object --stdin)
+
+trace_it "$0: processing '$arg_file' with '$arg_sp' rule, and got hash '$sum' for it + '$dep'"
+
+getret=
+if test -z "$get"
+then
+ if test $(redis-cli SISMEMBER spatch-cache "$sum") = 1
+ then
+ getret=0
+ else
+ getret=1
+ fi
+else
+ $set "$sum"
+ getret=$?
+fi
+
+if test "$getret" = 0
+then
+ trace_it "$0: HIT for '$arg_file' with '$arg_sp'"
+ exit 0
+else
+ trace_it "$0: MISS: for '$arg_file' with '$arg_sp'"
+fi
+
+out="$(mktemp)"
+err="$(mktemp)"
+
+set +e
+"$spatch" "$@" >"$out" 2>>"$err"
+ret=$?
+cat "$out"
+cat "$err" >&2
+set -e
+
+nocache=
+if test $ret != 0
+then
+ nocache="exited non-zero: $ret"
+elif test -s "$out"
+then
+ nocache="had patch output"
+elif test -z "$cacheWhenStderr" && test -s "$err"
+then
+ nocache="had stderr (use --very-quiet or spatchCache.cacheWhenStderr=true?)"
+fi
+
+if test -n "$nocache"
+then
+ trace_it "$0: NOCACHE ($nocache): for '$arg_file' with '$arg_sp'"
+ exit "$ret"
+fi
+
+trace_it "$0: SET: for '$arg_file' with '$arg_sp'"
+
+setret=
+if test -z "$set"
+then
+ if test $(redis-cli SADD spatch-cache "$sum") = 1
+ then
+ setret=0
+ else
+ setret=1
+ fi
+else
+ "$set" "$sum"
+ setret=$?
+fi
+
+if test "$setret" != 0
+then
+ echo "FAILED to set '$sum' in cache!" >&2
+ exit 128
+fi
+
+exit "$ret"
diff --git a/contrib/coccinelle/strbuf.cocci b/contrib/coccinelle/strbuf.cocci
new file mode 100644
index 0000000..5f06105
--- /dev/null
+++ b/contrib/coccinelle/strbuf.cocci
@@ -0,0 +1,62 @@
+@@
+expression E;
+constant fmt !~ "%";
+@@
+- strbuf_addf
++ strbuf_addstr
+ (E,
+(
+ fmt
+|
+ _(fmt)
+)
+ );
+
+@@
+expression E;
+struct strbuf SB;
+format F =~ "^s$";
+@@
+- strbuf_addf(E, "%@F@", SB.buf);
++ strbuf_addbuf(E, &SB);
+
+@@
+expression E;
+struct strbuf *SBP;
+format F =~ "^s$";
+@@
+- strbuf_addf(E, "%@F@", SBP->buf);
++ strbuf_addbuf(E, SBP);
+
+@@
+expression E;
+struct strbuf SB;
+@@
+- strbuf_addstr(E, SB.buf);
++ strbuf_addbuf(E, &SB);
+
+@@
+expression E;
+struct strbuf *SBP;
+@@
+- strbuf_addstr(E, SBP->buf);
++ strbuf_addbuf(E, SBP);
+
+@@
+expression E1, E2;
+format F =~ "^s$";
+@@
+- strbuf_addf(E1, "%@F@", E2);
++ strbuf_addstr(E1, E2);
+
+@@
+expression E1, E2, E3;
+@@
+- strbuf_addstr(E1, find_unique_abbrev(E2, E3));
++ strbuf_add_unique_abbrev(E1, E2, E3);
+
+@@
+expression E1, E2;
+@@
+- strbuf_addstr(E1, real_path(E2));
++ strbuf_add_real_path(E1, E2);
diff --git a/contrib/coccinelle/swap.cocci b/contrib/coccinelle/swap.cocci
new file mode 100644
index 0000000..522177a
--- /dev/null
+++ b/contrib/coccinelle/swap.cocci
@@ -0,0 +1,28 @@
+@@
+type T;
+identifier tmp;
+T a, b;
+@@
+- T tmp = a;
++ T tmp;
++ tmp = a;
+ a = b;
+ b = tmp;
+
+@ swap @
+type T;
+T tmp, a, b;
+@@
+- tmp = a;
+- a = b;
+- b = tmp;
++ SWAP(a, b);
+
+@ extends swap @
+identifier unused;
+@@
+ {
+ ...
+- T unused;
+ ... when != unused
+ }
diff --git a/contrib/coccinelle/tests/free.c b/contrib/coccinelle/tests/free.c
new file mode 100644
index 0000000..96d4abc
--- /dev/null
+++ b/contrib/coccinelle/tests/free.c
@@ -0,0 +1,11 @@
+int use_FREE_AND_NULL(int *v)
+{
+ free(*v);
+ *v = NULL;
+}
+
+int need_no_if(int *v)
+{
+ if (v)
+ free(v);
+}
diff --git a/contrib/coccinelle/tests/free.res b/contrib/coccinelle/tests/free.res
new file mode 100644
index 0000000..f90fd9f
--- /dev/null
+++ b/contrib/coccinelle/tests/free.res
@@ -0,0 +1,9 @@
+int use_FREE_AND_NULL(int *v)
+{
+ FREE_AND_NULL(*v);
+}
+
+int need_no_if(int *v)
+{
+ free(v);
+}
diff --git a/contrib/coccinelle/tests/unused.c b/contrib/coccinelle/tests/unused.c
new file mode 100644
index 0000000..8294d73
--- /dev/null
+++ b/contrib/coccinelle/tests/unused.c
@@ -0,0 +1,82 @@
+void test_strbuf(void)
+{
+ struct strbuf sb1 = STRBUF_INIT;
+ struct strbuf sb2 = STRBUF_INIT;
+ struct strbuf sb3 = STRBUF_INIT;
+ struct strbuf sb4 = STRBUF_INIT;
+ struct strbuf sb5;
+ struct strbuf sb6 = { 0 };
+ struct strbuf sb7 = STRBUF_INIT;
+ struct strbuf sb8 = STRBUF_INIT;
+ struct strbuf *sp1;
+ struct strbuf *sp2;
+ struct strbuf *sp3;
+ struct strbuf *sp4 = xmalloc(sizeof(struct strbuf));
+ struct strbuf *sp5 = xmalloc(sizeof(struct strbuf));
+ struct strbuf *sp6 = xmalloc(sizeof(struct strbuf));
+ struct strbuf *sp7;
+
+ strbuf_init(&sb5, 0);
+ strbuf_init(sp1, 0);
+ strbuf_init(sp2, 0);
+ strbuf_init(sp3, 0);
+ strbuf_init(sp4, 0);
+ strbuf_init(sp5, 0);
+ strbuf_init(sp6, 0);
+ strbuf_init(sp7, 0);
+ sp7 = xmalloc(sizeof(struct strbuf));
+
+ use_before(&sb3);
+ use_as_str("%s", sb7.buf);
+ use_as_str("%s", sp1->buf);
+ use_as_str("%s", sp6->buf);
+ pass_pp(&sp3);
+
+ strbuf_release(&sb1);
+ strbuf_reset(&sb2);
+ strbuf_release(&sb3);
+ strbuf_release(&sb4);
+ strbuf_release(&sb5);
+ strbuf_release(&sb6);
+ strbuf_release(&sb7);
+ strbuf_release(sp1);
+ strbuf_release(sp2);
+ strbuf_release(sp3);
+ strbuf_release(sp4);
+ strbuf_release(sp5);
+ strbuf_release(sp6);
+ strbuf_release(sp7);
+
+ use_after(&sb4);
+
+ if (when_strict())
+ return;
+ strbuf_release(&sb8);
+}
+
+void test_other(void)
+{
+ struct string_list l = STRING_LIST_INIT_DUP;
+ struct strbuf sb = STRBUF_INIT;
+
+ string_list_clear(&l, 0);
+ string_list_clear(&sb, 0);
+}
+
+void test_worktrees(void)
+{
+ struct worktree **w1 = get_worktrees();
+ struct worktree **w2 = get_worktrees();
+ struct worktree **w3;
+ struct worktree **w4;
+
+ w3 = get_worktrees();
+ w4 = get_worktrees();
+
+ use_it(w4);
+
+ free_worktrees(w1);
+ free_worktrees(w2);
+ free_worktrees(w3);
+ free_worktrees(w4);
+}
diff --git a/contrib/coccinelle/tests/unused.res b/contrib/coccinelle/tests/unused.res
new file mode 100644
index 0000000..6d3e745
--- /dev/null
+++ b/contrib/coccinelle/tests/unused.res
@@ -0,0 +1,45 @@
+void test_strbuf(void)
+{
+ struct strbuf sb3 = STRBUF_INIT;
+ struct strbuf sb4 = STRBUF_INIT;
+ struct strbuf sb7 = STRBUF_INIT;
+ struct strbuf *sp1;
+ struct strbuf *sp3;
+ struct strbuf *sp6 = xmalloc(sizeof(struct strbuf));
+ strbuf_init(sp1, 0);
+ strbuf_init(sp3, 0);
+ strbuf_init(sp6, 0);
+
+ use_before(&sb3);
+ use_as_str("%s", sb7.buf);
+ use_as_str("%s", sp1->buf);
+ use_as_str("%s", sp6->buf);
+ pass_pp(&sp3);
+
+ strbuf_release(&sb3);
+ strbuf_release(&sb4);
+ strbuf_release(&sb7);
+ strbuf_release(sp1);
+ strbuf_release(sp3);
+ strbuf_release(sp6);
+
+ use_after(&sb4);
+
+ if (when_strict())
+ return;
+}
+
+void test_other(void)
+{
+}
+
+void test_worktrees(void)
+{
+ struct worktree **w4;
+
+ w4 = get_worktrees();
+
+ use_it(w4);
+
+ free_worktrees(w4);
+}
diff --git a/contrib/coccinelle/the_repository.pending.cocci b/contrib/coccinelle/the_repository.pending.cocci
new file mode 100644
index 0000000..747d382
--- /dev/null
+++ b/contrib/coccinelle/the_repository.pending.cocci
@@ -0,0 +1,128 @@
+// This file is used for the ongoing refactoring of
+// bringing the index or repository struct in all of
+// our code base.
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- read_object_file(
++ repo_read_object_file(the_repository,
+ E, F, G)
+
+@@
+expression E;
+@@
+- has_object_file(
++ repo_has_object_file(the_repository,
+ E)
+
+@@
+expression E;
+@@
+- has_object_file_with_flags(
++ repo_has_object_file_with_flags(the_repository,
+ E)
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- parse_commit_internal(
++ repo_parse_commit_internal(the_repository,
+ E, F, G)
+
+@@
+expression E;
+expression F;
+@@
+- parse_commit_gently(
++ repo_parse_commit_gently(the_repository,
+ E, F)
+
+@@
+expression E;
+@@
+- parse_commit(
++ repo_parse_commit(the_repository,
+ E)
+
+@@
+expression E;
+expression F;
+@@
+- get_merge_bases(
++ repo_get_merge_bases(the_repository,
+ E, F);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- get_merge_bases_many(
++ repo_get_merge_bases_many(the_repository,
+ E, F, G);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- get_merge_bases_many_dirty(
++ repo_get_merge_bases_many_dirty(the_repository,
+ E, F, G);
+
+@@
+expression E;
+expression F;
+@@
+- in_merge_bases(
++ repo_in_merge_bases(the_repository,
+ E, F);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- in_merge_bases_many(
++ repo_in_merge_bases_many(the_repository,
+ E, F, G);
+
+@@
+expression E;
+expression F;
+@@
+- get_commit_buffer(
++ repo_get_commit_buffer(the_repository,
+ E, F);
+
+@@
+expression E;
+expression F;
+@@
+- unuse_commit_buffer(
++ repo_unuse_commit_buffer(the_repository,
+ E, F);
+
+@@
+expression E;
+expression F;
+expression G;
+@@
+- logmsg_reencode(
++ repo_logmsg_reencode(the_repository,
+ E, F, G);
+
+@@
+expression E;
+expression F;
+expression G;
+expression H;
+@@
+- format_commit_message(
++ repo_format_commit_message(the_repository,
+ E, F, G, H);
diff --git a/contrib/coccinelle/unused.cocci b/contrib/coccinelle/unused.cocci
new file mode 100644
index 0000000..d84046f
--- /dev/null
+++ b/contrib/coccinelle/unused.cocci
@@ -0,0 +1,43 @@
+// This rule finds sequences of "unused" declerations and uses of a
+// variable, where "unused" is defined to include only calling the
+// equivalent of alloc, init & free functions on the variable.
+@@
+type T;
+identifier I;
+// STRBUF_INIT, but also e.g. STRING_LIST_INIT_DUP (so no anchoring)
+constant INIT_MACRO =~ "_INIT";
+identifier MALLOC1 =~ "^x?[mc]alloc$";
+identifier INIT_ASSIGN1 =~ "^get_worktrees$";
+identifier INIT_CALL1 =~ "^[a-z_]*_init$";
+identifier REL1 =~ "^[a-z_]*_(release|reset|clear|free)$";
+identifier REL2 =~ "^(release|clear|free)_[a-z_]*$";
+@@
+
+(
+- T I;
+|
+- T I = { 0 };
+|
+- T I = INIT_MACRO;
+|
+- T I = MALLOC1(...);
+|
+- T I = INIT_ASSIGN1(...);
+)
+
+<... when != \( I \| &I \)
+(
+- \( INIT_CALL1 \)( \( I \| &I \), ...);
+|
+- I = \( INIT_ASSIGN1 \)(...);
+|
+- I = MALLOC1(...);
+)
+...>
+
+(
+- \( REL1 \| REL2 \)( \( I \| &I \), ...);
+|
+- \( REL1 \| REL2 \)( \( &I \| I \) );
+)
+ ... when != \( I \| &I \)
diff --git a/contrib/coccinelle/xcalloc.cocci b/contrib/coccinelle/xcalloc.cocci
new file mode 100644
index 0000000..c291011
--- /dev/null
+++ b/contrib/coccinelle/xcalloc.cocci
@@ -0,0 +1,10 @@
+@@
+type T;
+T *ptr;
+expression n;
+@@
+ xcalloc(
++ n,
+ \( sizeof(T) \| sizeof(*ptr) \)
+- , n
+ )
diff --git a/contrib/coccinelle/xopen.cocci b/contrib/coccinelle/xopen.cocci
new file mode 100644
index 0000000..b71db67
--- /dev/null
+++ b/contrib/coccinelle/xopen.cocci
@@ -0,0 +1,19 @@
+@@
+identifier fd;
+identifier die_fn =~ "^(die|die_errno)$";
+@@
+ int fd =
+- open
++ xopen
+ (...);
+- if ( \( fd < 0 \| fd == -1 \) ) { die_fn(...); }
+
+@@
+expression fd;
+identifier die_fn =~ "^(die|die_errno)$";
+@@
+ fd =
+- open
++ xopen
+ (...);
+- if ( \( fd < 0 \| fd == -1 \) ) { die_fn(...); }
diff --git a/contrib/coccinelle/xstrdup_or_null.cocci b/contrib/coccinelle/xstrdup_or_null.cocci
new file mode 100644
index 0000000..9c1d293
--- /dev/null
+++ b/contrib/coccinelle/xstrdup_or_null.cocci
@@ -0,0 +1,5 @@
+@@
+expression E;
+@@
+- xstrdup(absolute_path(E))
++ absolute_pathdup(E)