diff --git a/.github/fedora-42.Dockerfile b/.github/fedora-42.Dockerfile index 9cf14cef5..9dc80405d 100644 --- a/.github/fedora-42.Dockerfile +++ b/.github/fedora-42.Dockerfile @@ -25,6 +25,7 @@ RUN dnf --disablerepo=* --enablerepo=fedora,updates --nodocs --setopt=install_we libcmocka-devel \ libgit2-devel \ libtool \ + pipx \ procps-ng \ python3-breathe \ python3-dateutil \ @@ -36,3 +37,6 @@ RUN dnf --disablerepo=* --enablerepo=fedora,updates --nodocs --setopt=install_we sed \ xxd && \ dnf clean all -y + +RUN pipx install --global ast-grep-cli && \ + ast-grep --version diff --git a/.github/fedora-43.Dockerfile b/.github/fedora-43.Dockerfile index 3879410ec..dc45a44bd 100644 --- a/.github/fedora-43.Dockerfile +++ b/.github/fedora-43.Dockerfile @@ -25,6 +25,7 @@ RUN dnf --disablerepo=* --enablerepo=fedora,updates --nodocs --setopt=install_we libcmocka-devel \ libgit2-devel \ libtool \ + pipx \ procps-ng \ python3-breathe \ python3-dateutil \ @@ -36,3 +37,6 @@ RUN dnf --disablerepo=* --enablerepo=fedora,updates --nodocs --setopt=install_we sed \ xxd && \ dnf clean all -y + +RUN pipx install --global ast-grep-cli && \ + ast-grep --version diff --git a/.github/fedora-44.Dockerfile b/.github/fedora-44.Dockerfile index eab316185..23aab745b 100644 --- a/.github/fedora-44.Dockerfile +++ b/.github/fedora-44.Dockerfile @@ -26,6 +26,7 @@ RUN dnf --disablerepo=* --enablerepo=fedora,updates --nodocs --setopt=install_we libgit2-devel \ libpfm-devel \ libtool \ + pipx \ procps-ng \ python3-breathe \ python3-dateutil \ @@ -37,3 +38,6 @@ RUN dnf --disablerepo=* --enablerepo=fedora,updates --nodocs --setopt=install_we sed \ xxd && \ dnf clean all -y + +RUN pipx install --global ast-grep-cli && \ + ast-grep --version diff --git a/.github/fedora-rawhide.Dockerfile b/.github/fedora-rawhide.Dockerfile index 4bccb992d..9a3eb7322 100644 --- a/.github/fedora-rawhide.Dockerfile +++ b/.github/fedora-rawhide.Dockerfile @@ -28,6 +28,7 @@ RUN dnf --disablerepo=* --enablerepo=fedora --nodocs --setopt=install_weak_deps= libgit2-devel \ libpfm-devel \ libtool \ + pipx \ procps-ng \ python3-breathe \ python3-dateutil \ @@ -39,3 +40,6 @@ RUN dnf --disablerepo=* --enablerepo=fedora --nodocs --setopt=install_weak_deps= sed \ xxd && \ dnf clean all -y + +RUN pipx install --global ast-grep-cli && \ + ast-grep --version diff --git a/.github/ubuntu-24.04.Dockerfile b/.github/ubuntu-24.04.Dockerfile index d69937c05..6b91c6044 100644 --- a/.github/ubuntu-24.04.Dockerfile +++ b/.github/ubuntu-24.04.Dockerfile @@ -39,4 +39,5 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ xxd && \ rm -rf /var/lib/apt/lists/* -RUN pip install --break-system-packages linuxdoc +RUN pip install --break-system-packages linuxdoc ast-grep-cli && \ + ast-grep --version diff --git a/.github/ubuntu-25.04.Dockerfile b/.github/ubuntu-25.04.Dockerfile index 5656f66a5..3e5676d98 100644 --- a/.github/ubuntu-25.04.Dockerfile +++ b/.github/ubuntu-25.04.Dockerfile @@ -39,4 +39,5 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ xxd && \ rm -rf /var/lib/apt/lists/* -RUN pip install --break-system-packages linuxdoc +RUN pip install --break-system-packages linuxdoc ast-grep-cli && \ + ast-grep --version diff --git a/.github/ubuntu-25.10.Dockerfile b/.github/ubuntu-25.10.Dockerfile index 1f7e7da9d..6e079853d 100644 --- a/.github/ubuntu-25.10.Dockerfile +++ b/.github/ubuntu-25.10.Dockerfile @@ -39,4 +39,5 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ xxd && \ rm -rf /var/lib/apt/lists/* -RUN pip install --break-system-packages linuxdoc +RUN pip install --break-system-packages linuxdoc ast-grep-cli && \ + ast-grep --version diff --git a/tests/check/CMakeLists.txt b/tests/check/CMakeLists.txt index 63bee1e3c..82deab759 100644 --- a/tests/check/CMakeLists.txt +++ b/tests/check/CMakeLists.txt @@ -57,6 +57,31 @@ set_tests_properties("check.checkstyle" PROPERTIES LABELS "check" ) +find_program(SG_BIN NAMES ast-grep) + +if(SG_BIN) + set(SG_CONFIG ${CMAKE_SOURCE_DIR}/tests/check/ast-grep/sgconfig.yml) + + add_test(NAME "check.astgrep.test" + COMMAND ${SG_BIN} test --config ${SG_CONFIG} + ) + + add_test(NAME "check.astgrep" + COMMAND ${SG_BIN} scan --config ${SG_CONFIG} + ${CMAKE_SOURCE_DIR}/src/libbpfilter + ${CMAKE_SOURCE_DIR}/src/bfcli + --off=assert-pointer-params + --off=doxygen-prefer-backticks + --off=doxygen-public-functions + --off=no-direct-free + --off=single-line-comment-style + ) + + set_tests_properties("check.astgrep.test" "check.astgrep" PROPERTIES + LABELS "check" + ) +endif() + add_custom_target(fixstyle COMMAND ${CLANG_FORMAT_BIN} diff --git a/tests/check/ast-grep/rule-tests/.gitkeep b/tests/check/ast-grep/rule-tests/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/assert-pointer-params-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/assert-pointer-params-snapshot.yml new file mode 100644 index 000000000..00be886ad --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/assert-pointer-params-snapshot.yml @@ -0,0 +1,62 @@ +id: assert-pointer-params +snapshots: + ? | + int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) + { + return 0; + } + : labels: + - source: |- + int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) + { + return 0; + } + style: primary + start: 0 + end: 85 + - source: '*chain' + style: secondary + start: 38 + end: 44 + - source: struct bf_chain *chain + style: secondary + start: 22 + end: 44 + - source: (struct bf_chain *chain, struct bf_rule *rule) + style: secondary + start: 21 + end: 67 + - source: bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) + style: secondary + start: 4 + end: 67 + ? | + void bf_rule_free(struct bf_rule **rule) + { + return; + } + : labels: + - source: |- + void bf_rule_free(struct bf_rule **rule) + { + return; + } + style: primary + start: 0 + end: 56 + - source: '**rule' + style: secondary + start: 33 + end: 39 + - source: struct bf_rule **rule + style: secondary + start: 18 + end: 39 + - source: (struct bf_rule **rule) + style: secondary + start: 17 + end: 40 + - source: bf_rule_free(struct bf_rule **rule) + style: secondary + start: 5 + end: 40 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/bf-prefix-public-functions-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/bf-prefix-public-functions-snapshot.yml new file mode 100644 index 000000000..4897c7976 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/bf-prefix-public-functions-snapshot.yml @@ -0,0 +1,30 @@ +id: bf-prefix-public-functions +snapshots: + ? | + int my_func(void) + { + return 0; + } + : labels: + - source: |- + int my_func(void) + { + return 0; + } + style: primary + start: 0 + end: 35 + ? | + void process_packet(struct pkt *p) + { + return; + } + : labels: + - source: |- + void process_packet(struct pkt *p) + { + return; + } + style: primary + start: 0 + end: 50 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/double-pointer-output-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/double-pointer-output-snapshot.yml new file mode 100644 index 000000000..b68842849 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/double-pointer-output-snapshot.yml @@ -0,0 +1,24 @@ +id: double-pointer-output +snapshots: + ? | + int bf_chain_new(struct bf_chain *chain) + { + return 0; + } + : labels: + - source: |- + int bf_chain_new(struct bf_chain *chain) + { + return 0; + } + style: primary + start: 0 + end: 58 + - source: bf_chain_new + style: secondary + start: 4 + end: 16 + - source: bf_chain_new(struct bf_chain *chain) + style: secondary + start: 4 + end: 40 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/doxygen-prefer-backticks-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/doxygen-prefer-backticks-snapshot.yml new file mode 100644 index 000000000..1dfe5f9ef --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/doxygen-prefer-backticks-snapshot.yml @@ -0,0 +1,36 @@ +id: doxygen-prefer-backticks +snapshots: + ? | + /** + * @brief Free a map object. + * + * @param map @ref bf_map object to free. + */ + void bf_map_free(struct bf_map **map); + : labels: + - source: |- + /** + * @brief Free a map object. + * + * @param map @ref bf_map object to free. + */ + style: primary + start: 0 + end: 81 + ? | + /** + * @brief Manage BPF object references. + * + * @ref bf_handle is used to manage references. + */ + int bf_handle_new(void); + : labels: + - source: |- + /** + * @brief Manage BPF object references. + * + * @ref bf_handle is used to manage references. + */ + style: primary + start: 0 + end: 98 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/doxygen-public-functions-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/doxygen-public-functions-snapshot.yml new file mode 100644 index 000000000..77f7d3dc4 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/doxygen-public-functions-snapshot.yml @@ -0,0 +1,17 @@ +id: doxygen-public-functions +snapshots: + ? | + // This is not a Doxygen comment + int bf_set_add(struct bf_set *set, int value); + : labels: + - source: int bf_set_add(struct bf_set *set, int value); + style: primary + start: 33 + end: 79 + ? | + int bf_rule_free(void); + : labels: + - source: int bf_rule_free(void); + style: primary + start: 0 + end: 23 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/enum-bf-prefix-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/enum-bf-prefix-snapshot.yml new file mode 100644 index 000000000..47c08f05d --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/enum-bf-prefix-snapshot.yml @@ -0,0 +1,42 @@ +id: enum-bf-prefix +snapshots: + ? | + enum filter_type + { + FILTER_ACCEPT, + _FILTER_MAX, + }; + : labels: + - source: |- + enum filter_type + { + FILTER_ACCEPT, + _FILTER_MAX, + } + style: primary + start: 0 + end: 56 + - source: filter_type + style: secondary + start: 5 + end: 16 + ? | + enum hook + { + HOOK_XDP, + _HOOK_MAX, + }; + : labels: + - source: |- + enum hook + { + HOOK_XDP, + _HOOK_MAX, + } + style: primary + start: 0 + end: 42 + - source: hook + style: secondary + start: 5 + end: 9 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/enum-sentinel-format-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/enum-sentinel-format-snapshot.yml new file mode 100644 index 000000000..0fe209133 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/enum-sentinel-format-snapshot.yml @@ -0,0 +1,58 @@ +id: enum-sentinel-format +snapshots: + ? | + enum bf_chain_type + { + BF_CHAIN_TYPE_FILTER, + BF_CHAIN_TYPE_NAT, + }; + : labels: + - source: |- + enum bf_chain_type + { + BF_CHAIN_TYPE_FILTER, + BF_CHAIN_TYPE_NAT, + } + style: primary + start: 0 + end: 71 + - source: bf_chain_type + style: secondary + start: 5 + end: 18 + - source: |- + { + BF_CHAIN_TYPE_FILTER, + BF_CHAIN_TYPE_NAT, + } + style: secondary + start: 19 + end: 71 + ? | + enum bf_hook + { + BF_HOOK_XDP, + BF_HOOK_TC_INGRESS, + }; + : labels: + - source: |- + enum bf_hook + { + BF_HOOK_XDP, + BF_HOOK_TC_INGRESS, + } + style: primary + start: 0 + end: 57 + - source: bf_hook + style: secondary + start: 5 + end: 12 + - source: |- + { + BF_HOOK_XDP, + BF_HOOK_TC_INGRESS, + } + style: secondary + start: 13 + end: 57 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/fd-init-minus-one-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/fd-init-minus-one-snapshot.yml new file mode 100644 index 000000000..5bb6ec8a5 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/fd-init-minus-one-snapshot.yml @@ -0,0 +1,22 @@ +id: fd-init-minus-one +snapshots: + ? | + void foo(void) + { + _cleanup_close_ int fd = 0; + } + : labels: + - source: _cleanup_close_ int fd = 0; + style: primary + start: 21 + end: 48 + ? | + void foo(void) + { + _cleanup_close_ int fd; + } + : labels: + - source: _cleanup_close_ int fd; + style: primary + start: 21 + end: 44 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/free-function-pattern-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/free-function-pattern-snapshot.yml new file mode 100644 index 000000000..1f5d40d89 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/free-function-pattern-snapshot.yml @@ -0,0 +1,46 @@ +id: free-function-pattern +snapshots: + ? | + int bf_chain_free(struct bf_chain **chain) + { + return 0; + } + : labels: + - source: |- + int bf_chain_free(struct bf_chain **chain) + { + return 0; + } + style: primary + start: 0 + end: 60 + - source: bf_chain_free + style: secondary + start: 4 + end: 17 + - source: bf_chain_free(struct bf_chain **chain) + style: secondary + start: 4 + end: 42 + ? | + void bf_chain_free(struct bf_chain *chain) + { + return; + } + : labels: + - source: |- + void bf_chain_free(struct bf_chain *chain) + { + return; + } + style: primary + start: 0 + end: 58 + - source: bf_chain_free + style: secondary + start: 5 + end: 18 + - source: bf_chain_free(struct bf_chain *chain) + style: secondary + start: 5 + end: 42 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/negative-errno-returns-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/negative-errno-returns-snapshot.yml new file mode 100644 index 000000000..2c3236126 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/negative-errno-returns-snapshot.yml @@ -0,0 +1,58 @@ +id: negative-errno-returns +snapshots: + ? | + int bf_arg_parse(const char *s) + { + return E2BIG; + } + : labels: + - source: return E2BIG; + style: primary + start: 38 + end: 51 + - source: E2BIG + style: secondary + start: 45 + end: 50 + ? | + int bf_chain_new(struct bf_chain **chain) + { + return ENOMEM; + } + : labels: + - source: return ENOMEM; + style: primary + start: 48 + end: 62 + - source: ENOMEM + style: secondary + start: 55 + end: 61 + ? | + int bf_io_read(int fd) + { + return EIO; + } + : labels: + - source: return EIO; + style: primary + start: 29 + end: 40 + - source: EIO + style: secondary + start: 36 + end: 39 + ? | + int bf_rule_load(struct bf_rule *rule) + { + return EINVAL; + } + : labels: + - source: return EINVAL; + style: primary + start: 45 + end: 59 + - source: EINVAL + style: secondary + start: 52 + end: 58 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/no-direct-free-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/no-direct-free-snapshot.yml new file mode 100644 index 000000000..b1644a8a3 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/no-direct-free-snapshot.yml @@ -0,0 +1,16 @@ +id: no-direct-free +snapshots: + ? | + void process(char *buf) + { + free(buf); + } + : labels: + - source: free(buf) + style: primary + start: 30 + end: 39 + - source: free + style: secondary + start: 30 + end: 34 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/no-fprintf-stderr-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/no-fprintf-stderr-snapshot.yml new file mode 100644 index 000000000..98fea0d7a --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/no-fprintf-stderr-snapshot.yml @@ -0,0 +1,22 @@ +id: no-fprintf-stderr +snapshots: + ? | + void bf_handle(const char *fmt, va_list args) + { + vfprintf(stderr, fmt, args); + } + : labels: + - source: vfprintf(stderr, fmt, args) + style: primary + start: 52 + end: 79 + ? | + void bf_handle(void) + { + fprintf(stderr, "error: %s", msg); + } + : labels: + - source: 'fprintf(stderr, "error: %s", msg)' + style: primary + start: 27 + end: 60 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/no-ifndef-guards-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/no-ifndef-guards-snapshot.yml new file mode 100644 index 000000000..afd590428 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/no-ifndef-guards-snapshot.yml @@ -0,0 +1,24 @@ +id: no-ifndef-guards +snapshots: + ? | + #ifndef BF_CHAIN_H + #define BF_CHAIN_H + struct bf_chain; + #endif + : labels: + - source: |- + #ifndef BF_CHAIN_H + #define BF_CHAIN_H + struct bf_chain; + #endif + style: primary + start: 0 + end: 61 + - source: | + #ifndef BF_CHAIN_H + #define BF_CHAIN_H + struct bf_chain; + #endif + style: secondary + start: 0 + end: 62 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/single-line-comment-style-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/single-line-comment-style-snapshot.yml new file mode 100644 index 000000000..3126567bd --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/single-line-comment-style-snapshot.yml @@ -0,0 +1,10 @@ +id: single-line-comment-style +snapshots: + ? | + /* This should be a // comment */ + int x = 0; + : labels: + - source: /* This should be a // comment */ + style: primary + start: 0 + end: 33 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/spdx-license-header-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/spdx-license-header-snapshot.yml new file mode 100644 index 000000000..a86e6d7ef --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/spdx-license-header-snapshot.yml @@ -0,0 +1,12 @@ +id: spdx-license-header +snapshots: + ? | + #include + int main(void) { return 0; } + : labels: + - source: | + #include + int main(void) { return 0; } + style: primary + start: 0 + end: 48 diff --git a/tests/check/ast-grep/rule-tests/__snapshots__/struct-bf-prefix-snapshot.yml b/tests/check/ast-grep/rule-tests/__snapshots__/struct-bf-prefix-snapshot.yml new file mode 100644 index 000000000..8e3ce76af --- /dev/null +++ b/tests/check/ast-grep/rule-tests/__snapshots__/struct-bf-prefix-snapshot.yml @@ -0,0 +1,52 @@ +id: struct-bf-prefix +snapshots: + ? | + struct chain + { + int count; + }; + : labels: + - source: |- + struct chain + { + int count; + } + style: primary + start: 0 + end: 31 + - source: |- + { + int count; + } + style: secondary + start: 13 + end: 31 + - source: chain + style: secondary + start: 7 + end: 12 + ? | + struct filter_rule + { + int id; + }; + : labels: + - source: |- + struct filter_rule + { + int id; + } + style: primary + start: 0 + end: 34 + - source: |- + { + int id; + } + style: secondary + start: 19 + end: 34 + - source: filter_rule + style: secondary + start: 7 + end: 18 diff --git a/tests/check/ast-grep/rule-tests/assert-pointer-params-test.yml b/tests/check/ast-grep/rule-tests/assert-pointer-params-test.yml new file mode 100644 index 000000000..e60dba742 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/assert-pointer-params-test.yml @@ -0,0 +1,61 @@ +id: assert-pointer-params +valid: + - | + int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) + { + assert(chain); + assert(rule); + return 0; + } + - | + int bf_set_size(int count) + { + return count; + } + - | + static int _helper(struct bf_chain *chain) + { + return 0; + } + - | + int main(int argc, const char **argv) + { + return 0; + } + - | + int bf_hook_opts_set(struct bf_hookopts *opts, const char *name) + { + assert(opts); + assert(name); + opts->name = name; + return 0; + } + - | + void closep(int *fd) + { + return; + } + - | + void yyerror(struct bfc_ruleset *ruleset, const char *fmt, ...) + { + return; + } +invalid: + - | + int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) + { + return 0; + } + - | + void bf_rule_free(struct bf_rule **rule) + { + return; + } +# Known limitation: the rule flags only functions with zero assert() calls. +# A function with multiple pointer parameters but only some asserted will +# pass silently. The following case is a known-pass (documented limitation): +# int bf_chain_add_rule(struct bf_chain *chain, struct bf_rule *rule) +# { +# assert(chain); +# return 0; +# } diff --git a/tests/check/ast-grep/rule-tests/bf-prefix-public-functions-test.yml b/tests/check/ast-grep/rule-tests/bf-prefix-public-functions-test.yml new file mode 100644 index 000000000..85b399a1c --- /dev/null +++ b/tests/check/ast-grep/rule-tests/bf-prefix-public-functions-test.yml @@ -0,0 +1,58 @@ +id: bf-prefix-public-functions +valid: + - | + int bf_chain_new(struct bf_chain **chain) + { + return 0; + } + - | + int bfc_parse(const char *input) + { + return 0; + } + - | + int main(int argc, const char **argv) + { + return 0; + } + - | + void closep(int *fd) + { + return; + } + - | + static inline void bf_freep(void *ptr) + { + free(*(void **)ptr); + } + - | + static int helper(int x) + { + return x + 1; + } + - | + void yyerror(struct bfc_ruleset *ruleset, const char *fmt, ...) + { + return; + } + - | + void _bf_ctx_free(struct bf_ctx **ctx) + { + return; + } + - | + int _bfc_opts_get_cmd(int object, int action) + { + return 0; + } +invalid: + - | + int my_func(void) + { + return 0; + } + - | + void process_packet(struct pkt *p) + { + return; + } diff --git a/tests/check/ast-grep/rule-tests/double-pointer-output-test.yml b/tests/check/ast-grep/rule-tests/double-pointer-output-test.yml new file mode 100644 index 000000000..4a14004d3 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/double-pointer-output-test.yml @@ -0,0 +1,18 @@ +id: double-pointer-output +valid: + - | + int bf_chain_new(struct bf_chain **chain) + { + return 0; + } + - | + int bf_set_new(struct bf_set **set, const char *name) + { + return 0; + } +invalid: + - | + int bf_chain_new(struct bf_chain *chain) + { + return 0; + } diff --git a/tests/check/ast-grep/rule-tests/doxygen-prefer-backticks-test.yml b/tests/check/ast-grep/rule-tests/doxygen-prefer-backticks-test.yml new file mode 100644 index 000000000..53f417af8 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/doxygen-prefer-backticks-test.yml @@ -0,0 +1,34 @@ +id: doxygen-prefer-backticks +valid: + - | + /** + * @brief Create a new chain. + * + * @param chain On success, points to the new `bf_chain`. + * @return 0 on success. + */ + int bf_chain_new(struct bf_chain **chain); + - | + // @param is fine in regular comments + int x = 0; + - | + /** + * @brief Return value description. + * @return A `bf_btf` structure on success. + */ + int bf_foo(void); +invalid: + - | + /** + * @brief Manage BPF object references. + * + * @ref bf_handle is used to manage references. + */ + int bf_handle_new(void); + - | + /** + * @brief Free a map object. + * + * @param map @ref bf_map object to free. + */ + void bf_map_free(struct bf_map **map); diff --git a/tests/check/ast-grep/rule-tests/doxygen-public-functions-test.yml b/tests/check/ast-grep/rule-tests/doxygen-public-functions-test.yml new file mode 100644 index 000000000..74efa5173 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/doxygen-public-functions-test.yml @@ -0,0 +1,27 @@ +id: doxygen-public-functions +valid: + - | + /** + * @brief Create a new chain from the given parameters. + * + * @param chain On success, points to the new chain. + * @return 0 on success, negative errno on failure. + */ + int bf_chain_new(struct bf_chain **chain, const char *name); + - | + static inline int _bf_helper(int x); + - | + /** + * @brief Free a chain object. + * + * @param chain Pointer to the chain pointer. Set to NULL on return. + */ + void bf_chain_free(struct bf_chain **chain); + - | + int x = 42; +invalid: + - | + int bf_rule_free(void); + - | + // This is not a Doxygen comment + int bf_set_add(struct bf_set *set, int value); diff --git a/tests/check/ast-grep/rule-tests/enum-bf-prefix-test.yml b/tests/check/ast-grep/rule-tests/enum-bf-prefix-test.yml new file mode 100644 index 000000000..488d8bcba --- /dev/null +++ b/tests/check/ast-grep/rule-tests/enum-bf-prefix-test.yml @@ -0,0 +1,32 @@ +id: enum-bf-prefix +valid: + - | + enum bf_hook + { + BF_HOOK_XDP, + _BF_HOOK_MAX, + }; + - | + enum bfc_action + { + BFC_ACTION_ACCEPT, + _BFC_ACTION_MAX, + }; + - | + enum + { + _BFC_DEPRECATED_FOO = 100, + }; +invalid: + - | + enum hook + { + HOOK_XDP, + _HOOK_MAX, + }; + - | + enum filter_type + { + FILTER_ACCEPT, + _FILTER_MAX, + }; diff --git a/tests/check/ast-grep/rule-tests/enum-sentinel-format-test.yml b/tests/check/ast-grep/rule-tests/enum-sentinel-format-test.yml new file mode 100644 index 000000000..7a54748d3 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/enum-sentinel-format-test.yml @@ -0,0 +1,47 @@ +id: enum-sentinel-format +valid: + - | + enum bf_hook + { + BF_HOOK_XDP, + BF_HOOK_TC_INGRESS, + _BF_HOOK_MAX, + }; + - | + enum bf_log + { + BF_LOG_DBG, + BF_LOG_INFO, + _BF_LOG_MAX, + }; + - | + enum bf_color + { + BF_COLOR_RESET = 0, + BF_COLOR_RED = 1 << 3, + }; + - | + enum bf_bpf_cmd + { + BF_BPF_PROG_LOAD = 5, + BF_BPF_MAP_CREATE = 0, + }; + - | + enum bf_nf_inet_hooks + { + BF_NF_INET_PRE_ROUTING = 0, + BF_NF_INET_LOCAL_IN = 1, + }; +invalid: + - | + enum bf_hook + { + BF_HOOK_XDP, + BF_HOOK_TC_INGRESS, + }; + - | + enum bf_chain_type + { + BF_CHAIN_TYPE_FILTER, + BF_CHAIN_TYPE_NAT, + }; diff --git a/tests/check/ast-grep/rule-tests/fd-init-minus-one-test.yml b/tests/check/ast-grep/rule-tests/fd-init-minus-one-test.yml new file mode 100644 index 000000000..f203a18b8 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/fd-init-minus-one-test.yml @@ -0,0 +1,24 @@ +id: fd-init-minus-one +valid: + - | + void foo(void) + { + _cleanup_close_ int fd = -1; + } + - | + void foo(void) + { + _cleanup_close_ int mnt_fd = -1; + _cleanup_close_ int bpffs_fd = -1; + } +invalid: + - | + void foo(void) + { + _cleanup_close_ int fd = 0; + } + - | + void foo(void) + { + _cleanup_close_ int fd; + } diff --git a/tests/check/ast-grep/rule-tests/free-function-pattern-test.yml b/tests/check/ast-grep/rule-tests/free-function-pattern-test.yml new file mode 100644 index 000000000..916d16f2c --- /dev/null +++ b/tests/check/ast-grep/rule-tests/free-function-pattern-test.yml @@ -0,0 +1,18 @@ +id: free-function-pattern +valid: + - | + void bf_chain_free(struct bf_chain **chain) + { + return; + } +invalid: + - | + int bf_chain_free(struct bf_chain **chain) + { + return 0; + } + - | + void bf_chain_free(struct bf_chain *chain) + { + return; + } diff --git a/tests/check/ast-grep/rule-tests/negative-errno-returns-test.yml b/tests/check/ast-grep/rule-tests/negative-errno-returns-test.yml new file mode 100644 index 000000000..46c3a6441 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/negative-errno-returns-test.yml @@ -0,0 +1,50 @@ +id: negative-errno-returns +valid: + - | + int bf_chain_new(struct bf_chain **chain) + { + return -ENOMEM; + } + - | + int bf_rule_load(struct bf_rule *rule) + { + return bf_err_r(-EINVAL, "invalid rule"); + } + - | + int bf_ctx_setup(struct bf_ctx *ctx) + { + return 0; + } + - | + int bf_set_add(struct bf_set *set, const void *elem) + { + return r; + } + - | + int bf_hook_parse(struct bf_hook *hook) + { + if (!hook) + return -EINVAL; + return -ENOMEM; + } +invalid: + - | + int bf_chain_new(struct bf_chain **chain) + { + return ENOMEM; + } + - | + int bf_rule_load(struct bf_rule *rule) + { + return EINVAL; + } + - | + int bf_io_read(int fd) + { + return EIO; + } + - | + int bf_arg_parse(const char *s) + { + return E2BIG; + } diff --git a/tests/check/ast-grep/rule-tests/no-direct-free-test.yml b/tests/check/ast-grep/rule-tests/no-direct-free-test.yml new file mode 100644 index 000000000..01da9d149 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/no-direct-free-test.yml @@ -0,0 +1,36 @@ +id: no-direct-free +valid: + - | + void caller(struct bf_chain **chain) + { + BF_FREEP(chain); + } + - | + static inline void bf_freep(void *ptr) + { + free(*(void **)ptr); + *(void **)ptr = NULL; + } + - | + void bf_list_free(bf_list **list) + { + free(*list); + *list = NULL; + } + - | + static void _bf_swich_option_free(struct bf_swich_option **option) + { + free(*option); + *option = NULL; + } + - | + void bf_swich_cleanup(struct bf_swich *swich) + { + free(swich->default_opt); + } +invalid: + - | + void process(char *buf) + { + free(buf); + } diff --git a/tests/check/ast-grep/rule-tests/no-fprintf-stderr-test.yml b/tests/check/ast-grep/rule-tests/no-fprintf-stderr-test.yml new file mode 100644 index 000000000..96fbd51c0 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/no-fprintf-stderr-test.yml @@ -0,0 +1,33 @@ +id: no-fprintf-stderr +valid: + - | + void bf_handle(void) + { + bf_err("something went wrong"); + } + - | + void bf_process(const char *s) + { + bf_warn("unexpected value: %s", s); + } + - | + static void _bf_print_insn(void *private_data, const char *fmt, ...) + { + fprintf(stderr, fmt); + } + - | + void bf_logger_emit(const char *fmt, va_list args) + { + vfprintf(stderr, fmt, args); + } +invalid: + - | + void bf_handle(void) + { + fprintf(stderr, "error: %s", msg); + } + - | + void bf_handle(const char *fmt, va_list args) + { + vfprintf(stderr, fmt, args); + } diff --git a/tests/check/ast-grep/rule-tests/no-ifndef-guards-test.yml b/tests/check/ast-grep/rule-tests/no-ifndef-guards-test.yml new file mode 100644 index 000000000..aaf43401b --- /dev/null +++ b/tests/check/ast-grep/rule-tests/no-ifndef-guards-test.yml @@ -0,0 +1,21 @@ +id: no-ifndef-guards +valid: + - | + #pragma once + struct bf_chain; + int bf_chain_new(struct bf_chain **chain); + - | + #pragma once + #ifdef __cplusplus + extern "C" { + #endif + int bf_init(void); + #ifdef __cplusplus + } + #endif +invalid: + - | + #ifndef BF_CHAIN_H + #define BF_CHAIN_H + struct bf_chain; + #endif diff --git a/tests/check/ast-grep/rule-tests/single-line-comment-style-test.yml b/tests/check/ast-grep/rule-tests/single-line-comment-style-test.yml new file mode 100644 index 000000000..854cba990 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/single-line-comment-style-test.yml @@ -0,0 +1,23 @@ +id: single-line-comment-style +valid: + - | + // This is a correct single-line comment + int x = 0; + - | + /* SPDX-License-Identifier: GPL-2.0-only */ + int x = 0; + - | + /* + * This is a correct multi-line comment + * spanning multiple lines. + */ + int x = 0; + - | + /** + * @brief Doxygen comment is fine. + */ + int bf_foo(void); +invalid: + - | + /* This should be a // comment */ + int x = 0; diff --git a/tests/check/ast-grep/rule-tests/spdx-license-header-test.yml b/tests/check/ast-grep/rule-tests/spdx-license-header-test.yml new file mode 100644 index 000000000..d9f0a1c51 --- /dev/null +++ b/tests/check/ast-grep/rule-tests/spdx-license-header-test.yml @@ -0,0 +1,14 @@ +id: spdx-license-header +valid: + - | + /* SPDX-License-Identifier: GPL-2.0-only */ + #include + int main(void) { return 0; } + - | + // SPDX-License-Identifier: GPL-2.0-only + #include + void foo(void) {} +invalid: + - | + #include + int main(void) { return 0; } diff --git a/tests/check/ast-grep/rule-tests/struct-bf-prefix-test.yml b/tests/check/ast-grep/rule-tests/struct-bf-prefix-test.yml new file mode 100644 index 000000000..413c057ac --- /dev/null +++ b/tests/check/ast-grep/rule-tests/struct-bf-prefix-test.yml @@ -0,0 +1,23 @@ +id: struct-bf-prefix +valid: + - | + struct bf_chain + { + int count; + }; + - | + struct bfc_opts + { + int argc; + }; +invalid: + - | + struct chain + { + int count; + }; + - | + struct filter_rule + { + int id; + }; diff --git a/tests/check/ast-grep/rules/.gitkeep b/tests/check/ast-grep/rules/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tests/check/ast-grep/rules/assert-pointer-params.yml b/tests/check/ast-grep/rules/assert-pointer-params.yml new file mode 100644 index 000000000..1df70e4a8 --- /dev/null +++ b/tests/check/ast-grep/rules/assert-pointer-params.yml @@ -0,0 +1,37 @@ +id: assert-pointer-params +language: c +severity: error +message: Functions with pointer parameters must assert() them at entry +note: >- + bpfilter uses assert() for pointer preconditions at function entry. + Limitation: this rule flags functions with zero assert() calls; it cannot + verify that EACH pointer parameter is asserted individually. A function + with three pointer parameters and a single assert() will pass silently. +rule: + all: + - matches: is-public-function + - has: + kind: function_declarator + has: + kind: parameter_list + has: + kind: parameter_declaration + has: + kind: pointer_declarator + stopBy: end + - not: + has: + kind: compound_statement + has: + kind: call_expression + has: + kind: identifier + regex: "^assert$" + stopBy: end + stopBy: end + - not: + has: + kind: function_declarator + has: + kind: identifier + regex: "^(main|closep|yyerror)$" diff --git a/tests/check/ast-grep/rules/bf-prefix-public-functions.yml b/tests/check/ast-grep/rules/bf-prefix-public-functions.yml new file mode 100644 index 000000000..d966183b3 --- /dev/null +++ b/tests/check/ast-grep/rules/bf-prefix-public-functions.yml @@ -0,0 +1,20 @@ +id: bf-prefix-public-functions +language: c +severity: error +message: >- + Non-static function does not follow naming convention. + Public library functions must use bf_ prefix, CLI functions bfc_ prefix. + Internal/static functions use leading underscore (_bf_, _bfc_). +note: >- + Rename this function with the appropriate prefix: bf_ for libbpfilter, + bfc_ for CLI utilities, or make it static if it is internal. +rule: + all: + - matches: is-public-function + - not: + has: + kind: function_declarator + has: + kind: identifier + regex: "^(_?bf_|_?bfc_|main|closep|yy\\w+)" + stopBy: end diff --git a/tests/check/ast-grep/rules/double-pointer-output.yml b/tests/check/ast-grep/rules/double-pointer-output.yml new file mode 100644 index 000000000..3d9c62727 --- /dev/null +++ b/tests/check/ast-grep/rules/double-pointer-output.yml @@ -0,0 +1,28 @@ +id: double-pointer-output +language: c +severity: error +message: >- + Constructor bf_*_new() / bfc_*_new() missing double-pointer output parameter. + Constructors must take a TYPE ** parameter to return the allocated object. +note: >- + Change the output parameter to a double pointer (TYPE **) so the caller + receives ownership via TAKE_PTR() and cleanup attributes work correctly. +rule: + kind: function_definition + has: + kind: function_declarator + has: + kind: identifier + regex: "^(bf|bfc)_\\w+_new(_from_\\w+)?$" + not: + has: + kind: function_declarator + has: + kind: parameter_list + has: + kind: parameter_declaration + has: + kind: pointer_declarator + has: + kind: pointer_declarator + stopBy: end diff --git a/tests/check/ast-grep/rules/doxygen-prefer-backticks.yml b/tests/check/ast-grep/rules/doxygen-prefer-backticks.yml new file mode 100644 index 000000000..bfc4c50a0 --- /dev/null +++ b/tests/check/ast-grep/rules/doxygen-prefer-backticks.yml @@ -0,0 +1,11 @@ +id: doxygen-prefer-backticks +language: c +severity: error +message: >- + Prefer `name` over @p name, @ref name, and @c name in Doxygen comments. +note: >- + Replace @p name, @ref name, and @c name with `name` for readability. + This migration is gradual; many existing comments still use the old forms. +rule: + kind: comment + regex: "@(p|ref|c)\\s" diff --git a/tests/check/ast-grep/rules/doxygen-public-functions.yml b/tests/check/ast-grep/rules/doxygen-public-functions.yml new file mode 100644 index 000000000..8c2cee6bd --- /dev/null +++ b/tests/check/ast-grep/rules/doxygen-public-functions.yml @@ -0,0 +1,20 @@ +id: doxygen-public-functions +language: c +severity: error +files: + - "**/*.h" +message: >- + Public function declaration missing @brief tag. + Public functions should be preceded by a /** @brief ... */ comment. +note: >- + Add a Doxygen comment with an explicit @brief tag before this declaration. + Exception: trivial getters/setters may skip documentation per style.rst. + This rule flags functions whose preceding comment lacks an explicit + @brief tag; implicit-brief Doxygen is also detected as missing. +rule: + all: + - matches: is-public-declaration + - not: + follows: + kind: comment + regex: "@brief" diff --git a/tests/check/ast-grep/rules/enum-bf-prefix.yml b/tests/check/ast-grep/rules/enum-bf-prefix.yml new file mode 100644 index 000000000..f8b57e8d8 --- /dev/null +++ b/tests/check/ast-grep/rules/enum-bf-prefix.yml @@ -0,0 +1,16 @@ +id: enum-bf-prefix +language: c +severity: error +message: >- + Enum tag does not follow naming convention. + Library enums must use bf_ prefix, CLI enums use bfc_ prefix. +note: >- + Rename the enum to bf_ (libbpfilter) or bfc_ (bfcli). + Anonymous enums are allowed (the rule only fires on named enums). +rule: + kind: enum_specifier + has: + field: name + kind: type_identifier + not: + regex: "^(bf_|bfc_)" diff --git a/tests/check/ast-grep/rules/enum-sentinel-format.yml b/tests/check/ast-grep/rules/enum-sentinel-format.yml new file mode 100644 index 000000000..4e9e91b93 --- /dev/null +++ b/tests/check/ast-grep/rules/enum-sentinel-format.yml @@ -0,0 +1,37 @@ +id: enum-sentinel-format +language: c +severity: error +message: >- + Enum missing _MAX sentinel value. + All enums should have a _BF_*_MAX or _BFC_*_MAX sentinel for bounds checking. +note: >- + Add a sentinel like _BF__MAX as the last enumerator. + Anonymous enums are allowed (the rule only fires on named enums). + Exceptions: bitmask enums (bf_color, bf_style), kernel ABI mirror enums + (bf_nf_inet_hooks, bf_bpf_cmd, bf_bpf_prog_type, bf_bpf_attach_type, + bf_bpf_map_type), and negative-indexed sentinel enums (bf_counter_type) + do not require sentinels. +rule: + kind: enum_specifier + all: + - has: + field: name + kind: type_identifier + - has: + kind: enumerator_list + - not: + has: + kind: enumerator_list + has: + kind: enumerator + has: + kind: identifier + regex: "^_B(F|FC)_.*_MAX$" + # Excluded enums: bitmask enums (bf_color, bf_style), kernel ABI mirrors + # that must match an external numbering (bf_nf_inet_hooks, bf_bpf_*), and + # negative-indexed sentinel enums (bf_counter_type). Add new entries only + # when an enum legitimately cannot have a _BF_*_MAX trailing sentinel. + - not: + has: + kind: type_identifier + regex: "^(bf_color|bf_style|bf_nf_inet_hooks|bf_bpf_cmd|bf_bpf_prog_type|bf_bpf_attach_type|bf_bpf_map_type|bf_counter_type)$" diff --git a/tests/check/ast-grep/rules/fd-init-minus-one.yml b/tests/check/ast-grep/rules/fd-init-minus-one.yml new file mode 100644 index 000000000..8bccb80b6 --- /dev/null +++ b/tests/check/ast-grep/rules/fd-init-minus-one.yml @@ -0,0 +1,14 @@ +id: fd-init-minus-one +language: c +severity: error +message: >- + Variable with _cleanup_close_ not initialized to -1. + File descriptors using cleanup attributes must be initialized to -1 + (the neutral/invalid value) to prevent closing fd 0 (stdin) on error paths. +note: >- + Change the declaration to: _cleanup_close_ int fd = -1; +rule: + kind: declaration + regex: "_cleanup_close_" + not: + regex: "=\\s*-\\s*1\\s*;" diff --git a/tests/check/ast-grep/rules/free-function-pattern.yml b/tests/check/ast-grep/rules/free-function-pattern.yml new file mode 100644 index 000000000..7b45a5fbd --- /dev/null +++ b/tests/check/ast-grep/rules/free-function-pattern.yml @@ -0,0 +1,37 @@ +id: free-function-pattern +language: c +severity: error +message: >- + Free function _?bf_*_free() / _?bfc_*_free() violates conventions. + Free functions must return void and take a double-pointer parameter (TYPE **). +note: >- + Ensure the function returns void, takes TYPE ** as parameter, + is a no-op if *ptr is NULL, and sets *ptr to NULL after freeing. + Limitation: this rule only checks the function signature. + The "no-op if *ptr is NULL" and "set *ptr to NULL" requirements must be + verified by review or by runtime tests. +rule: + all: + - kind: function_definition + - has: + kind: function_declarator + has: + kind: identifier + regex: "^_?(bf|bfc)_\\w+_free$" + - any: + - not: + has: + kind: primitive_type + regex: "^void$" + - not: + has: + kind: function_declarator + has: + kind: parameter_list + has: + kind: parameter_declaration + has: + kind: pointer_declarator + has: + kind: pointer_declarator + stopBy: end diff --git a/tests/check/ast-grep/rules/negative-errno-returns.yml b/tests/check/ast-grep/rules/negative-errno-returns.yml new file mode 100644 index 000000000..796085cc2 --- /dev/null +++ b/tests/check/ast-grep/rules/negative-errno-returns.yml @@ -0,0 +1,12 @@ +id: negative-errno-returns +language: c +severity: error +message: Error returns must be negative (e.g. -ENOMEM, not ENOMEM) +note: >- + bpfilter uses negative errno convention: return -ENOMEM, not ENOMEM. + See doc/developers/style.rst. +rule: + kind: return_statement + has: + kind: identifier + regex: "^E[A-Z0-9]{2,}$" diff --git a/tests/check/ast-grep/rules/no-direct-free.yml b/tests/check/ast-grep/rules/no-direct-free.yml new file mode 100644 index 000000000..95b816559 --- /dev/null +++ b/tests/check/ast-grep/rules/no-direct-free.yml @@ -0,0 +1,27 @@ +id: no-direct-free +language: c +severity: error +message: >- + Direct call to free() detected. Use BF_FREEP() or a typed bf_*_free() + cleanup function instead to prevent double-free via cleanup attributes. +note: >- + Replace free(ptr) with the appropriate cleanup helper. + Use BF_FREEP(&ptr) for generic allocations (type-safe macro form), + or bf_X_free(&obj) for typed objects. Declare locals with the + _cleanup_free_ attribute (which binds bf_freep) where possible. + Exempt callers: bf_freep, _?bf_*_free, _?bfc_*_free, _?bf_*_cleanup, + _?bfc_*_cleanup. +rule: + kind: call_expression + has: + kind: identifier + regex: "^free$" + not: + inside: + kind: function_definition + stopBy: end + has: + kind: function_declarator + has: + kind: identifier + regex: "^(bf_freep|_?bf_\\w+_(free|cleanup)|_?bfc_\\w+_(free|cleanup))$" diff --git a/tests/check/ast-grep/rules/no-fprintf-stderr.yml b/tests/check/ast-grep/rules/no-fprintf-stderr.yml new file mode 100644 index 000000000..159bc226d --- /dev/null +++ b/tests/check/ast-grep/rules/no-fprintf-stderr.yml @@ -0,0 +1,20 @@ +id: no-fprintf-stderr +language: c +severity: error +message: Use logging macros (bf_err, bf_warn, etc.) instead of fprintf(stderr, ...) or vfprintf(stderr, ...) +note: >- + bpfilter has structured logging via bf_err(), bf_warn(), bf_info(), bf_dbg(). + Direct fprintf(stderr) / vfprintf(stderr) bypasses log levels and formatting. +rule: + any: + - pattern: fprintf(stderr, $$$) + - pattern: vfprintf(stderr, $$$) + not: + inside: + kind: function_definition + has: + kind: function_declarator + has: + kind: identifier + regex: "^(_bf_print_|bf_logger_).*" + stopBy: end diff --git a/tests/check/ast-grep/rules/no-ifndef-guards.yml b/tests/check/ast-grep/rules/no-ifndef-guards.yml new file mode 100644 index 000000000..b0208ad9c --- /dev/null +++ b/tests/check/ast-grep/rules/no-ifndef-guards.yml @@ -0,0 +1,17 @@ +id: no-ifndef-guards +language: c +severity: error +files: + - "**/*.h" +message: Use #pragma once instead of #ifndef/#define include guards +note: >- + bpfilter uses #pragma once exclusively for header guards. +rule: + kind: preproc_ifdef + regex: "^#ifndef\\b" + not: + has: + kind: identifier + regex: "^__" + inside: + kind: translation_unit diff --git a/tests/check/ast-grep/rules/single-line-comment-style.yml b/tests/check/ast-grep/rules/single-line-comment-style.yml new file mode 100644 index 000000000..d6448fcca --- /dev/null +++ b/tests/check/ast-grep/rules/single-line-comment-style.yml @@ -0,0 +1,17 @@ +id: single-line-comment-style +language: c +severity: error +message: >- + Single-line comments should use // style, not /* */ style. +note: >- + Replace /* comment */ with // comment. Reserve /* */ for multi-line comments. +rule: + all: + - kind: comment + - regex: "^/\\*[^*!]" + - not: + regex: "\\n" + - not: + regex: "SPDX" + - not: + regex: "NOLINT" diff --git a/tests/check/ast-grep/rules/spdx-license-header.yml b/tests/check/ast-grep/rules/spdx-license-header.yml new file mode 100644 index 000000000..203df7fef --- /dev/null +++ b/tests/check/ast-grep/rules/spdx-license-header.yml @@ -0,0 +1,18 @@ +id: spdx-license-header +language: c +severity: error +message: File must contain an SPDX license header +note: >- + Every source file must contain an SPDX license identifier comment, + conventionally at the top of the file. Both styles are accepted: + /* SPDX-License-Identifier: GPL-2.0-only */ and + // SPDX-License-Identifier: GPL-2.0-only. + Limitation: this rule matches any SPDX comment anywhere in the file, + it does not verify that the comment is the first line. +rule: + kind: translation_unit + not: + has: + kind: comment + regex: "SPDX-License-Identifier" + stopBy: end diff --git a/tests/check/ast-grep/rules/struct-bf-prefix.yml b/tests/check/ast-grep/rules/struct-bf-prefix.yml new file mode 100644 index 000000000..8afd81930 --- /dev/null +++ b/tests/check/ast-grep/rules/struct-bf-prefix.yml @@ -0,0 +1,21 @@ +id: struct-bf-prefix +language: c +severity: error +message: >- + Struct tag does not follow naming convention. + Library structs must use bf_ prefix, CLI structs use bfc_ prefix. +note: >- + Rename the struct to bf_ (libbpfilter) or bfc_ (bfcli). + Only struct definitions (with a body) are checked -- references to + external types like `struct stat` or `struct nlmsghdr` are ignored. +rule: + all: + - kind: struct_specifier + - has: + field: body + kind: field_declaration_list + - has: + field: name + kind: type_identifier + not: + regex: "^(bf_|bfc_)" diff --git a/tests/check/ast-grep/sgconfig.yml b/tests/check/ast-grep/sgconfig.yml new file mode 100644 index 000000000..e416dae38 --- /dev/null +++ b/tests/check/ast-grep/sgconfig.yml @@ -0,0 +1,6 @@ +ruleDirs: + - rules +testConfigs: + - testDir: rule-tests +utilDirs: + - utils diff --git a/tests/check/ast-grep/utils/is-public-declaration.yml b/tests/check/ast-grep/utils/is-public-declaration.yml new file mode 100644 index 000000000..24598c0a8 --- /dev/null +++ b/tests/check/ast-grep/utils/is-public-declaration.yml @@ -0,0 +1,10 @@ +id: is-public-declaration +language: c +rule: + kind: declaration + has: + kind: function_declarator + stopBy: end + not: + has: + kind: storage_class_specifier diff --git a/tests/check/ast-grep/utils/is-public-function.yml b/tests/check/ast-grep/utils/is-public-function.yml new file mode 100644 index 000000000..06054724f --- /dev/null +++ b/tests/check/ast-grep/utils/is-public-function.yml @@ -0,0 +1,7 @@ +id: is-public-function +language: c +rule: + kind: function_definition + not: + has: + kind: storage_class_specifier