From 9acc5282af5382d31f5ad7947dc2887b289b56d5 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 20 Apr 2017 21:50:09 +0200 Subject: [PATCH 1/3] dist: tools: introduce coccinelle static testing --- dist/tools/ci/build_and_test.sh | 1 + dist/tools/coccinelle/check.sh | 78 +++++++++++++ dist/tools/coccinelle/force/badzero.cocci | 51 +++++++++ dist/tools/coccinelle/force/continue.cocci | 17 +++ dist/tools/coccinelle/force/static.cocci | 25 +++++ dist/tools/coccinelle/include/riot-standard.h | 1 + dist/tools/coccinelle/warn/notnull.cocci | 103 ++++++++++++++++++ 7 files changed, 276 insertions(+) create mode 100755 dist/tools/coccinelle/check.sh create mode 100644 dist/tools/coccinelle/force/badzero.cocci create mode 100644 dist/tools/coccinelle/force/continue.cocci create mode 100644 dist/tools/coccinelle/force/static.cocci create mode 100644 dist/tools/coccinelle/include/riot-standard.h create mode 100644 dist/tools/coccinelle/warn/notnull.cocci diff --git a/dist/tools/ci/build_and_test.sh b/dist/tools/ci/build_and_test.sh index b4b83a02bf..a79ee2d5a0 100755 --- a/dist/tools/ci/build_and_test.sh +++ b/dist/tools/ci/build_and_test.sh @@ -89,6 +89,7 @@ then run ./dist/tools/externc/check.sh run ./dist/tools/cppcheck/check.sh run ./dist/tools/pr_check/pr_check.sh ${CI_BASE_BRANCH} + run ./dist/tools/coccinelle/check.sh exit $RESULT fi diff --git a/dist/tools/coccinelle/check.sh b/dist/tools/coccinelle/check.sh new file mode 100755 index 0000000000..0afe8a5983 --- /dev/null +++ b/dist/tools/coccinelle/check.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +# Copyright 2017 Kaspar Schleiser +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +: ${RIOTBASE:=$(pwd)} + +. ${RIOTBASE}/dist/tools/ci/changed_files.sh + +EXIT_CODE=0 + +filter() { + if [ $COCCINELLE_QUIET -eq 0 ]; then + cat + else + grep '^---' | cut -f 2 -d ' ' + fi +} + +indent() { + sed 's/^/ /g' +} + +coccinelle_checkone() { + OUT="$(spatch --very-quiet \ + --macro-file-builtins ${RIOTBASE}/dist/tools/coccinelle/include/riot-standard.h \ + --sp-file $patch ${FILES} | filter)" + + if [ -n "$OUT" ]; then + if [ $COCCINELLE_QUIET -eq 1 ]; then + echo "$patch:" + echo "$OUT" | indent + if [ COCCINELLE_WARNONLY -eq 0 ]; then + EXIT_CODE=1 + fi + else + echo "$OUT" + fi + fi +} + +coccinelle_checkall() { + local dir="$1" + local warn="${2:-0}" + + [ -d "$dir" ] || { + echo "$0: coccinelle_checkall(): $dir doesn't exist!" + exit 1 + } + + for patch in $dir/*; do + coccinelle_checkone $patch + done +} + +: ${FILES:=$(FILEREGEX='\.c$' changed_files)} + +if [ -z "${FILES}" ]; then + exit +fi + +: ${COCCINELLE_QUIET:=0} + +if [ -z "$*" ]; then + coccinelle_checkall ${RIOTBASE}/dist/tools/coccinelle/force + + COCCINELLE_WARNONLY=1 \ + coccinelle_checkall ${RIOTBASE}/dist/tools/coccinelle/warn +else + for patch in "$@"; do + coccinelle_checkone "$patch" + done +fi + +exit $EXIT_CODE diff --git a/dist/tools/coccinelle/force/badzero.cocci b/dist/tools/coccinelle/force/badzero.cocci new file mode 100644 index 0000000000..430206c67d --- /dev/null +++ b/dist/tools/coccinelle/force/badzero.cocci @@ -0,0 +1,51 @@ +// A pointer should not be compared to zero +// +// Confidence: High +// Copyright: (C) Gilles Muller, Julia Lawall, EMN, INRIA, DIKU. GPLv2. +// URL: http://coccinelle.lip6.fr/rules/badzero.html +// Options: + +@ disable is_zero,isnt_zero @ +expression *E; +expression E1,f; +@@ + +E = f(...) +<... +( +- E == 0 ++ !E +| +- E != 0 ++ E +| +- 0 == E ++ !E +| +- 0 != E ++ E +) +...> +?E = E1 + +@ disable is_zero,isnt_zero @ +expression *E; +@@ + +( + E == +- 0 ++ NULL +| + E != +- 0 ++ NULL +| +- 0 ++ NULL + == E +| +- 0 ++ NULL + != E +) diff --git a/dist/tools/coccinelle/force/continue.cocci b/dist/tools/coccinelle/force/continue.cocci new file mode 100644 index 0000000000..3489ececdd --- /dev/null +++ b/dist/tools/coccinelle/force/continue.cocci @@ -0,0 +1,17 @@ +// Continue at the end of a for loop has no purpose +// +// Confidence: Moderate +// Copyright: (C) Gilles Muller, Julia Lawall, EMN, INRIA, DIKU. GPLv2. +// URL: http://coccinelle.lip6.fr/rules/continue.html +// Options: + +@@ +@@ + +for (...;...;...) { + ... + if (...) { + ... +- continue; + } +} diff --git a/dist/tools/coccinelle/force/static.cocci b/dist/tools/coccinelle/force/static.cocci new file mode 100644 index 0000000000..0e5ebd8fa6 --- /dev/null +++ b/dist/tools/coccinelle/force/static.cocci @@ -0,0 +1,25 @@ +// remove unused static property of function-local static variable +// (from Julia Lawall, see https://lists.riot-os.org/pipermail/devel/2017-May/005133.html) + +@bad exists@ +position p; +identifier x; +type T; +@@ + +static T x@p; +... +x = <+...x...+> + +@@ +identifier x; +expression e; +type T; +position p != bad.p; +@@ + +-static + T x@p; + ... when != x + when strict +?x = e; diff --git a/dist/tools/coccinelle/include/riot-standard.h b/dist/tools/coccinelle/include/riot-standard.h new file mode 100644 index 0000000000..421509eb5c --- /dev/null +++ b/dist/tools/coccinelle/include/riot-standard.h @@ -0,0 +1 @@ +#define LL_SEARCH_SCALAR(pkt, tcp, type, GNRC_NETTYPE_TCP) tcp = XXX diff --git a/dist/tools/coccinelle/warn/notnull.cocci b/dist/tools/coccinelle/warn/notnull.cocci new file mode 100644 index 0000000000..b56255d967 --- /dev/null +++ b/dist/tools/coccinelle/warn/notnull.cocci @@ -0,0 +1,103 @@ +// this detects NULL tests that can only be reached when the value is known +// not to be NULL +// +// Confidence: High +// Copyright: (C) Gilles Muller, Julia Lawall, EMN, INRIA, DIKU. GPLv2. +// URL: http://coccinelle.lip6.fr/rules/notnull.html +// Options: + +@r exists@ +local idexpression x; +expression E; +position p1,p2; +@@ + +if (x@p1 == NULL || ...) { ... when forall + return ...; } +... when != \(x=E\|x--\|x++\|--x\|++x\|x-=E\|x+=E\|x|=E\|x&=E\) + when != &x +( +x@p2 == NULL +| +x@p2 != NULL +) + +// another path to the test that is not through p1? + +@s exists@ +local idexpression r.x; +position r.p1,r.p2; +@@ + +... when != x@p1 +( +x@p2 == NULL +| +x@p2 != NULL +) + +// another path to the test from p1? + +@t exists@ +local idexpression x; +position r.p1,r.p2; +@@ + +if (x@p1 == NULL || ...) { ... x@p2 ... when any + return ...; } + +// another path to the test containing an assignment? + +@u exists@ +local idexpression x; +expression E; +position r.p1,r.p2; +@@ + +if (x@p1 == NULL || ...) { ... when forall + return ...; } + ... + \(x=E\|x--\|x++\|--x\|++x\|x-=E\|x+=E\|x|=E\|x&=E\|&x\) + ... when != x@p1 + when any +( +x@p2 == NULL +| +x@p2 != NULL +) + +@fix depends on !s && !t && !u@ +position r.p2; +expression x,E; +statement S1,S2; +@@ + +( +- if ((x@p2 != NULL) || ...) + S1 +| +- if ((x@p2 != NULL) || ...) + S1 +- else S2 +| +- (x@p2 != NULL) && E ++ E +| +- (x@p2 == NULL) || E ++ E +| +- if ((x@p2 == NULL) && ...) S1 +| +- if ((x@p2 == NULL) && ...) S1 else + S2 +| +- BUG_ON(x@p2 == NULL); +) + +@script:python depends on !s && !t && !u && !fix@ +p1 << r.p1; +p2 << r.p2; +@@ + +cocci.print_main("",p1) +cocci.print_secs("retest",p2) From 98c359d2312db1812a252c4b18a389f6bebeb4f4 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 11 May 2017 13:58:05 +0200 Subject: [PATCH 2/3] dist/tools/ci/changed_files.sh: add coccinelle includes to EXCLUDE --- dist/tools/ci/changed_files.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/tools/ci/changed_files.sh b/dist/tools/ci/changed_files.sh index fd42941f5a..df255b2802 100644 --- a/dist/tools/ci/changed_files.sh +++ b/dist/tools/ci/changed_files.sh @@ -8,7 +8,7 @@ changed_files() { : ${FILEREGEX:='\.([CcHh]|[ch]pp)$'} - : ${EXCLUDE:='^(.+/include/vendor/)'} + : ${EXCLUDE:='^(.+/include/vendor/|dist/tools/coccinelle/include)'} : ${DIFFFILTER:='ACMR'} DIFFFILTER="--diff-filter=${DIFFFILTER}" From d603367c088871c42d592637849569199f8a366d Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 11 May 2017 14:12:35 +0200 Subject: [PATCH 3/3] dist/tools/ci/print_toolchain_versions.sh: add coccinelle --- dist/tools/ci/print_toolchain_versions.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dist/tools/ci/print_toolchain_versions.sh b/dist/tools/ci/print_toolchain_versions.sh index 9dbd2aa941..05b4a8a299 100755 --- a/dist/tools/ci/print_toolchain_versions.sh +++ b/dist/tools/ci/print_toolchain_versions.sh @@ -58,6 +58,17 @@ cppcheck_version() { printf "%s" "$ver" } +spatch_version() { + local cmd="spatch" + if command -v "$cmd" 2>&1 >/dev/null; then + ver=$("$cmd" --version | head -n 1) + else + ver="missing" + fi + + printf "%s" "$ver" +} + printf "%s\n" "Installed toolchain versions" printf "%s\n" "----------------------------" VER=$(gcc --version | head -n 1) @@ -79,4 +90,5 @@ for p in avr; do printf "%20s: %s\n" "$p-libc" "$(avr_libc_version "$p")" done printf "%20s: %s\n" "cppcheck" "$(cppcheck_version)" +printf "%20s: %s\n" "coccinelle" "$(spatch_version)" exit 0