Merge branch 'ab/cocci-unused'

Add Coccinelle rules to detect the pattern of initializing and then
finalizing a structure without using it in between at all, which
happens after code restructuring and the compilers fail to
recognize as an unused variable.

* ab/cocci-unused:
  cocci: generalize "unused" rule to cover more than "strbuf"
  cocci: add and apply a rule to find "unused" strbufs
  cocci: have "coccicheck{,-pending}" depend on "coccicheck-test"
  cocci: add a "coccicheck-test" target and test *.cocci rules
  Makefile & .gitignore: ignore & clean "git.res", not "*.res"
  Makefile: remove mandatory "spatch" arguments from SPATCH_FLAGS
This commit is contained in:
Junio C Hamano 2022-07-18 13:31:56 -07:00
commit 7f8d098b1b
13 changed files with 219 additions and 16 deletions

2
.gitignore vendored
View File

@ -185,6 +185,7 @@
/git-worktree
/git-write-tree
/git-core-*/?*
/git.res
/gitweb/GITWEB-BUILD-OPTIONS
/gitweb/gitweb.cgi
/gitweb/static/gitweb.js
@ -225,7 +226,6 @@
*.hcc
*.obj
*.lib
*.res
*.sln
*.sp
*.suo

View File

@ -1292,7 +1292,7 @@ SANITIZE_ADDRESS =
# For the 'coccicheck' target; setting SPATCH_BATCH_SIZE higher will
# usually result in less CPU usage at the cost of higher peak memory.
# Setting it to 0 will feed all files in a single spatch invocation.
SPATCH_FLAGS = --all-includes --patch .
SPATCH_FLAGS = --all-includes
SPATCH_BATCH_SIZE = 1
include config.mak.uname
@ -3126,6 +3126,8 @@ check: $(GENERATED_H)
exit 1; \
fi
COCCI_TEST_RES = $(wildcard contrib/coccinelle/tests/*.res)
%.cocci.patch: %.cocci $(COCCI_SOURCES)
$(QUIET_SPATCH) \
if test $(SPATCH_BATCH_SIZE) = 0; then \
@ -3134,7 +3136,8 @@ check: $(GENERATED_H)
limit='-n $(SPATCH_BATCH_SIZE)'; \
fi; \
if ! echo $(COCCI_SOURCES) | xargs $$limit \
$(SPATCH) --sp-file $< $(SPATCH_FLAGS) \
$(SPATCH) $(SPATCH_FLAGS) \
--sp-file $< --patch . \
>$@+ 2>$@.log; \
then \
cat $@.log; \
@ -3145,9 +3148,27 @@ check: $(GENERATED_H)
then \
echo ' ' SPATCH result: $@; \
fi
COCCI_TEST_RES_GEN = $(addprefix .build/,$(COCCI_TEST_RES))
$(COCCI_TEST_RES_GEN): .build/%.res : %.c
$(COCCI_TEST_RES_GEN): .build/%.res : %.res
$(COCCI_TEST_RES_GEN): .build/contrib/coccinelle/tests/%.res : contrib/coccinelle/%.cocci
$(call mkdir_p_parent_template)
$(QUIET_SPATCH_T)$(SPATCH) $(SPATCH_FLAGS) \
--very-quiet --no-show-diff \
--sp-file $< -o $@ \
$(@:.build/%.res=%.c) && \
cmp $(@:.build/%=%) $@ || \
git -P diff --no-index $(@:.build/%=%) $@ 2>/dev/null; \
.PHONY: coccicheck-test
coccicheck-test: $(COCCI_TEST_RES_GEN)
coccicheck: coccicheck-test
coccicheck: $(addsuffix .patch,$(filter-out %.pending.cocci,$(wildcard contrib/coccinelle/*.cocci)))
# See contrib/coccinelle/README
coccicheck-pending: coccicheck-test
coccicheck-pending: $(addsuffix .patch,$(wildcard contrib/coccinelle/*.pending.cocci))
.PHONY: coccicheck coccicheck-pending
@ -3415,12 +3436,13 @@ profile-clean:
$(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
cocciclean:
$(RM) -r .build/contrib/coccinelle
$(RM) contrib/coccinelle/*.cocci.patch*
clean: profile-clean coverage-clean cocciclean
$(RM) -r .build
$(RM) po/git.pot po/git-core.pot
$(RM) *.res
$(RM) git.res
$(RM) $(OBJECTS)
$(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)
$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X

View File

@ -1109,7 +1109,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
struct fetch_head *fetch_head)
{
int url_len, i, rc = 0;
struct strbuf note = STRBUF_INIT, err = STRBUF_INIT;
struct strbuf note = STRBUF_INIT;
const char *what, *kind;
struct ref *rm;
char *url;
@ -1276,7 +1276,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
abort:
strbuf_release(&note);
strbuf_release(&err);
free(url);
return rc;
}

View File

@ -375,7 +375,6 @@ static void reset_hard(const struct object_id *oid, int verbose)
static void restore_state(const struct object_id *head,
const struct object_id *stash)
{
struct strbuf sb = STRBUF_INIT;
const char *args[] = { "stash", "apply", NULL, NULL };
if (is_null_oid(stash))
@ -391,7 +390,6 @@ static void restore_state(const struct object_id *head,
*/
run_command_v_opt(args, RUN_GIT_CMD);
strbuf_release(&sb);
refresh_cache(REFRESH_QUIET);
}
@ -502,7 +500,6 @@ static void merge_name(const char *remote, struct strbuf *msg)
{
struct commit *remote_head;
struct object_id branch_head;
struct strbuf buf = STRBUF_INIT;
struct strbuf bname = STRBUF_INIT;
struct merge_remote_desc *desc;
const char *ptr;
@ -590,7 +587,6 @@ static void merge_name(const char *remote, struct strbuf *msg)
oid_to_hex(&remote_head->object.oid), remote);
cleanup:
free(found_ref);
strbuf_release(&buf);
strbuf_release(&bname);
}

View File

@ -727,7 +727,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item;
struct string_list names = STRING_LIST_INIT_DUP;
struct string_list rollback = STRING_LIST_INIT_NODUP;
struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP;
struct string_list existing_kept_packs = STRING_LIST_INIT_DUP;
struct pack_geometry *geometry = NULL;
@ -1117,7 +1116,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
string_list_clear(&names, 0);
string_list_clear(&rollback, 0);
string_list_clear(&existing_nonkept_packs, 0);
string_list_clear(&existing_kept_packs, 0);
clear_pack_geometry(geometry);

View File

@ -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);
}

View File

@ -0,0 +1,9 @@
int use_FREE_AND_NULL(int *v)
{
FREE_AND_NULL(*v);
}
int need_no_if(int *v)
{
free(v);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 \)

View File

@ -687,7 +687,7 @@ static int cmd_diagnose(int argc, const char **argv)
int stdout_fd = -1, archiver_fd = -1;
time_t now = time(NULL);
struct tm tm;
struct strbuf path = STRBUF_INIT, buf = STRBUF_INIT;
struct strbuf buf = STRBUF_INIT;
int res = 0;
argc = parse_options(argc, argv, NULL, options,
@ -779,7 +779,6 @@ diagnose_cleanup:
free(argv_copy);
strvec_clear(&archiver_args);
strbuf_release(&zip_path);
strbuf_release(&path);
strbuf_release(&buf);
return res;

2
diff.c
View File

@ -1289,7 +1289,6 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
{
static const char *nneof = " No newline at end of file\n";
const char *context, *reset, *set, *set_sign, *meta, *fraginfo;
struct strbuf sb = STRBUF_INIT;
enum diff_symbol s = eds->s;
const char *line = eds->line;
@ -1521,7 +1520,6 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
default:
BUG("unknown diff symbol");
}
strbuf_release(&sb);
}
static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,

View File

@ -70,6 +70,7 @@ ifndef V
QUIET_HDR = @echo ' ' HDR $(<:hcc=h);
QUIET_RC = @echo ' ' RC $@;
QUIET_SPATCH = @echo ' ' SPATCH $<;
QUIET_SPATCH_T = @echo ' ' SPATCH TEST $(@:.build/%=%);
## Used in "Documentation/Makefile"
QUIET_ASCIIDOC = @echo ' ' ASCIIDOC $@;