From 5bd05ec8f0e6cb990fee907a0eacf023b097c369 Mon Sep 17 00:00:00 2001 From: Andrei Gramakov Date: Mon, 1 Dec 2025 17:03:07 +0100 Subject: [PATCH 1/6] Add tidy file --- .clang-tidy | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 .clang-tidy diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..fe3b0cb --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,200 @@ +# ************************************************************************* +# +# clang-tidy configuration for microlog +# Copyright (c) 2025 Andrei Gramakov. All rights reserved. +# +# This configuration reinforces current code practices while maintaining +# flexibility for embedded systems and cross-platform compatibility. +# +# ************************************************************************* + +# Inherit configuration from parent directories +InheritParentConfig: false + +# Use LLVM style as baseline (matching .clang-format) +FormatStyle: file + +# Enable checks that reinforce current code practices +Checks: > + -*, + bugprone-*, + -bugprone-easily-swappable-parameters, + -bugprone-assignment-in-if-condition, + cert-*, + -cert-err33-c, + -cert-dcl37-c, + -cert-dcl51-cpp, + clang-analyzer-*, + -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, + concurrency-*, + cppcoreguidelines-*, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-init-variables, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-union-access, + -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-bounds-constant-array-index, + hicpp-multiway-paths-covered, + hicpp-no-assembler, + hicpp-signed-bitwise, + misc-*, + -misc-unused-parameters, + -misc-non-private-member-variables-in-classes, + modernize-*, + -modernize-use-trailing-return-type, + -modernize-macro-to-enum, + -modernize-avoid-c-arrays, + performance-*, + portability-*, + readability-*, + -readability-magic-numbers, + -readability-function-cognitive-complexity, + -readability-identifier-length, + -readability-isolate-declaration, + -readability-avoid-const-params-in-decls, + -readability-non-const-parameter, + -readability-uppercase-literal-suffix, + -readability-else-after-return, + +# Check options +CheckOptions: + # Naming conventions - enforce snake_case for functions/variables + - key: readability-identifier-naming.FunctionCase + value: lower_case + - key: readability-identifier-naming.VariableCase + value: lower_case + - key: readability-identifier-naming.ParameterCase + value: lower_case + - key: readability-identifier-naming.LocalVariableCase + value: lower_case + - key: readability-identifier-naming.GlobalVariableCase + value: lower_case + + # Allow internal types with _t suffix + - key: readability-identifier-naming.TypedefCase + value: lower_case + - key: readability-identifier-naming.TypedefSuffix + value: '' + - key: readability-identifier-naming.StructCase + value: lower_case + - key: readability-identifier-naming.UnionCase + value: lower_case + + # Enforce SCREAMING_SNAKE_CASE for macros + - key: readability-identifier-naming.MacroDefinitionCase + value: UPPER_CASE + - key: readability-identifier-naming.EnumConstantCase + value: UPPER_CASE + + # Enforce UPPER_CASE for enum types (ulog_level, ulog_status) + - key: readability-identifier-naming.EnumCase + value: lower_case + + # Constants should be in lower_case (following project style) + - key: readability-identifier-naming.ConstantCase + value: lower_case + - key: readability-identifier-naming.GlobalConstantCase + value: lower_case + + # Static function naming + - key: readability-identifier-naming.StaticVariableCase + value: lower_case + + # Allow some flexibility for embedded patterns + - key: readability-function-size.LineThreshold + value: 150 + - key: readability-function-size.StatementThreshold + value: 150 + - key: readability-function-size.BranchThreshold + value: 30 + - key: readability-function-size.ParameterThreshold + value: 10 + - key: readability-function-size.NestingThreshold + value: 6 + + # Braces are required for safety + - key: readability-braces-around-statements.ShortStatementLines + value: 0 + + # Line length matches .clang-format + - key: readability-line-length.LineLength + value: 80 + + # Allow some implicit conversions for C compatibility + - key: readability-implicit-bool-conversion.AllowIntegerConditions + value: true + - key: readability-implicit-bool-conversion.AllowPointerConditions + value: true + + # Configure memory management checks + - key: cppcoreguidelines-no-malloc.Allocations + value: 'malloc,calloc,realloc' + - key: cppcoreguidelines-no-malloc.Deallocations + value: 'free' + - key: cppcoreguidelines-no-malloc.Reallocations + value: 'realloc' + + # Allow some narrowing for embedded systems + - key: cppcoreguidelines-narrowing-conversions.WarnOnIntegerNarrowingConversion + value: false + + # Avoid C-style casts but be lenient with platform code + - key: cppcoreguidelines-pro-type-cstyle-cast.StrictMode + value: false + + # Configure bugprone checks + - key: bugprone-assert-side-effect.AssertMacros + value: 'assert' + - key: bugprone-suspicious-string-compare.WarnOnImplicitComparison + value: true + - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison + value: true + + # Performance settings + - key: performance-move-const-arg.CheckTriviallyCopyableMove + value: false + - key: performance-unnecessary-value-param.AllowedTypes + value: '' + + # Modernize settings (be conservative for C99 compatibility) + - key: modernize-use-nullptr.NullMacros + value: 'NULL' + - key: modernize-use-default-member-init.UseAssignment + value: true + - key: modernize-use-default-member-init.IgnoreMacros + value: true + + # Readability settings + - key: readability-simplify-boolean-expr.ChainedConditionalReturn + value: false + - key: readability-simplify-boolean-expr.ChainedConditionalAssignment + value: false + + # Comment style + - key: readability-redundant-declaration.IgnoreMacros + value: true + +# Files to exclude from analysis +HeaderFilterRegex: '(include|src|extensions)/.*\.(h|c)$' + +# Exclude third-party code and build directories +ExcludeHeaderFilterRegex: '(tests/unit/doctest|example/build|build)/.*' + +# Warning as errors for critical issues +WarningsAsErrors: > + bugprone-use-after-move, + bugprone-infinite-loop, + bugprone-assert-side-effect, + cert-err34-c, + cert-msc50-cpp, + cert-msc51-cpp, + clang-analyzer-core.DivideZero, + clang-analyzer-core.NullDereference, + clang-analyzer-deadcode.DeadStores, + clang-analyzer-unix.Malloc, + concurrency-mt-unsafe + +# System headers to ignore +SystemHeaders: false From 37d913a47c2f43700bf1280962353519cb2b4dac Mon Sep 17 00:00:00 2001 From: Andrei Gramakov Date: Mon, 1 Dec 2025 17:12:19 +0100 Subject: [PATCH 2/6] Fix tidy issues in the src --- src/ulog.c | 56 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/ulog.c b/src/ulog.c index db1cf1d..89f50dd 100644 --- a/src/ulog.c +++ b/src/ulog.c @@ -17,6 +17,8 @@ // ************************************************************************* #include "ulog.h" +#include +#include #include #include @@ -216,18 +218,20 @@ static inline bool is_str_empty(const char *str) { (`warn_not_enabled`, depends on: - ) ============================================================================ */ #if ULOG_HAS_WARN_NOT_ENABLED +// NOLINTBEGIN // Macro to log a warning when a feature is not enabled // Usage: warn_not_enabled("ULOG_BUILD_TIME") // Output: // WARN src/main.c:42: 'ulog_configure_time' ignored: ULOG_BUILD_TIME disabled #define warn_not_enabled(feature) \ - warn_non_enabled_full(__func__, feature, __FILE__, __LINE__) + warn_non_enabled_full(__func__, feature, __FILE__, __LINE__) // NOLINT #define warn_non_enabled_full(func, feature, file, line) \ ulog_log(ULOG_LEVEL_WARN, file, line, NULL, \ "'%s' called with %s disabled", func, feature) +// NOLINTEND #endif // ULOG_HAS_WARN_NOT_ENABLED /* ============================================================================ Core Feature: Print @@ -473,7 +477,7 @@ ulog_status ulog_color_config(bool enabled) { // Disabled Private // ================ -#define color_config_is_enabled() (ULOG_HAS_COLOR) +#define color_config_is_enabled() (ULOG_HAS_COLOR) // NOLINT #endif // ULOG_HAS_DYNAMIC_CONFIG @@ -520,8 +524,8 @@ static void color_print_end(print_target *tgt) { // Disabled Private // ================ -#define color_print_start(tgt, ev) (void)(tgt), (void)(ev) -#define color_print_end(tgt) (void)(tgt) +#define color_print_start(tgt, ev) (void)(tgt), (void)(ev) // NOLINT +#define color_print_end(tgt) (void)(tgt) // NOLINT #endif // ULOG_HAS_COLOR @@ -573,8 +577,8 @@ ulog_status ulog_prefix_config(bool enabled) { // Disabled Private // ================ -#define prefix_config_is_enabled() (ULOG_HAS_PREFIX) -#endif // ULOG_HAS_DYNAMIC_CONFIG +#define prefix_config_is_enabled() (ULOG_HAS_PREFIX) // NOLINT +#endif // ULOG_HAS_DYNAMIC_CONFIG /* ============================================================================ Optional Feature: Prefix @@ -638,9 +642,9 @@ ulog_status ulog_prefix_set_fn(ulog_prefix_fn function) { // Disabled Private // ================ -#define prefix_print(tgt) (void)(tgt) -#define prefix_update(ev) (void)(ev) -#endif // ULOG_HAS_PREFIX +#define prefix_print(tgt) (void)(tgt) // NOLINT +#define prefix_update(ev) (void)(ev) // NOLINT +#endif // ULOG_HAS_PREFIX /* ============================================================================ Optional Feature: Dynamic Configuration - Time @@ -690,8 +694,8 @@ ulog_status ulog_time_config(bool enabled) { // Disabled Private // ================ -#define time_config_is_enabled() (ULOG_HAS_TIME) -#endif // ULOG_HAS_DYNAMIC_CONFIG +#define time_config_is_enabled() (ULOG_HAS_TIME) // NOLINT +#endif // ULOG_HAS_DYNAMIC_CONFIG /* ============================================================================ Optional Feature: Time @@ -754,10 +758,10 @@ static void time_print_full(print_target *tgt, ulog_event *ev, // Disabled Private // ================ -#define time_print_short(tgt, ev, append_space) (void)(0) -#define time_print_full(tgt, ev, append_space) (void)(0) -#define time_fill_current_time(ev) (void)(ev) -#endif // ULOG_HAS_TIME +#define time_print_short(tgt, ev, append_space) (void)(0) // NOLINT +#define time_print_full(tgt, ev, append_space) (void)(0) // NOLINT +#define time_fill_current_time(ev) (void)(ev) // NOLINT +#endif // ULOG_HAS_TIME /* ============================================================================ Core Feature: Levels @@ -902,7 +906,7 @@ ulog_status ulog_level_config(ulog_level_config_style style) { // Disabled Private // ================ -#define level_config_is_short(void) (ULOG_HAS_LEVEL_SHORT) +#define level_config_is_short(void) (ULOG_HAS_LEVEL_SHORT) // NOLINT #endif // ULOG_HAS_DYNAMIC_CONFIG /* ============================================================================ @@ -1157,8 +1161,8 @@ ulog_status ulog_topic_config(bool enabled) { // Disabled Private // ================ -#define topic_config_is_enabled() (ULOG_HAS_TOPICS) -#endif // ULOG_HAS_DYNAMIC_CONFIG +#define topic_config_is_enabled() (ULOG_HAS_TOPICS) // NOLINT +#endif // ULOG_HAS_DYNAMIC_CONFIG /* ============================================================================ Optional Feature: Topics @@ -1170,12 +1174,13 @@ ulog_status ulog_topic_config(bool enabled) { // Private // ================ #ifndef ULOG_BUILD_TOPICS_STATIC_NUM - /* If static count not provided, default to 0 */ - #define ULOG_BUILD_TOPICS_STATIC_NUM 0 +/* If static count not provided, default to 0 */ +#define ULOG_BUILD_TOPICS_STATIC_NUM 0 #endif /* Dynamic if mode equals DYNAMIC */ -#define TOPIC_IS_DYNAMIC (ULOG_BUILD_TOPICS_MODE == ULOG_BUILD_TOPICS_MODE_DYNAMIC) +#define TOPIC_IS_DYNAMIC \ + (ULOG_BUILD_TOPICS_MODE == ULOG_BUILD_TOPICS_MODE_DYNAMIC) #define TOPIC_STATIC_NUM ULOG_BUILD_TOPICS_STATIC_NUM #define TOPIC_LEVEL_DEFAULT ULOG_LEVEL_TRACE @@ -1370,15 +1375,16 @@ ulog_status ulog_topic_remove(const char *topic_name) { } #endif // ULOG_HAS_WARN_NOT_ENABLED +// NOLINTBEGIN // Disabled Private // ================ - #define topic_print(tgt, ev) (void)(tgt), (void)(ev) #define topic_process(topic, level, is_log_allowed, topic_id, output) \ (void)(topic), (void)(level), (void)(is_log_allowed), (void)(topic_id), \ (void)(output) +// NOLINTEND #endif // ULOG_HAS_TOPICS /* ============================================================================ @@ -1659,7 +1665,7 @@ ulog_status ulog_source_location_config(bool enabled) { // Disabled Private // ================ -#define src_loc_config_is_enabled() (ULOG_HAS_SOURCE_LOCATION) +#define src_loc_config_is_enabled() (ULOG_HAS_SOURCE_LOCATION) // NOLINT #endif // ULOG_HAS_DYNAMIC_CONFIG /* ============================================================================ @@ -1716,8 +1722,8 @@ static void log_print_event(print_target *tgt, ulog_event *ev, bool full_time, } #endif - full_time ? time_print_full(tgt, ev, append_space) - : time_print_short(tgt, ev, append_space); + full_time ? time_print_full(tgt, ev, append_space) // NOLINT + : time_print_short(tgt, ev, append_space); // NOLINT prefix_print(tgt); level_print(tgt, ev); From 5be66196a893497941a4d327c151a05a394b9afd Mon Sep 17 00:00:00 2001 From: Andrei Gramakov Date: Mon, 1 Dec 2025 20:37:22 +0100 Subject: [PATCH 3/6] Fix more issues --- .clang-tidy | 25 +++++++--------- extensions/ulog_generic_interface.h | 2 +- extensions/ulog_lock_cmsis.c | 2 ++ extensions/ulog_lock_freertos.c | 2 ++ extensions/ulog_lock_pthread.c | 3 ++ extensions/ulog_lock_threadx.c | 2 ++ extensions/ulog_lock_win.c | 3 ++ extensions/ulog_syslog.c | 1 + src/ulog.c | 45 ++++++++++++++--------------- 9 files changed, 46 insertions(+), 39 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index fe3b0cb..9349ab5 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -48,6 +48,7 @@ Checks: > -modernize-avoid-c-arrays, performance-*, portability-*, + -portability-avoid-pragma-once, readability-*, -readability-magic-numbers, -readability-function-cognitive-complexity, @@ -76,21 +77,17 @@ CheckOptions: - key: readability-identifier-naming.TypedefCase value: lower_case - key: readability-identifier-naming.TypedefSuffix - value: '' + value: "" - key: readability-identifier-naming.StructCase value: lower_case - key: readability-identifier-naming.UnionCase value: lower_case - # Enforce SCREAMING_SNAKE_CASE for macros - - key: readability-identifier-naming.MacroDefinitionCase - value: UPPER_CASE - - key: readability-identifier-naming.EnumConstantCase - value: UPPER_CASE - # Enforce UPPER_CASE for enum types (ulog_level, ulog_status) - key: readability-identifier-naming.EnumCase value: lower_case + - key: readability-identifier-naming.EnumConstantCase + value: UPPER_CASE # Constants should be in lower_case (following project style) - key: readability-identifier-naming.ConstantCase @@ -130,11 +127,11 @@ CheckOptions: # Configure memory management checks - key: cppcoreguidelines-no-malloc.Allocations - value: 'malloc,calloc,realloc' + value: "malloc,calloc,realloc" - key: cppcoreguidelines-no-malloc.Deallocations - value: 'free' + value: "free" - key: cppcoreguidelines-no-malloc.Reallocations - value: 'realloc' + value: "realloc" # Allow some narrowing for embedded systems - key: cppcoreguidelines-narrowing-conversions.WarnOnIntegerNarrowingConversion @@ -146,7 +143,7 @@ CheckOptions: # Configure bugprone checks - key: bugprone-assert-side-effect.AssertMacros - value: 'assert' + value: "assert" - key: bugprone-suspicious-string-compare.WarnOnImplicitComparison value: true - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison @@ -156,11 +153,11 @@ CheckOptions: - key: performance-move-const-arg.CheckTriviallyCopyableMove value: false - key: performance-unnecessary-value-param.AllowedTypes - value: '' + value: "" # Modernize settings (be conservative for C99 compatibility) - key: modernize-use-nullptr.NullMacros - value: 'NULL' + value: "NULL" - key: modernize-use-default-member-init.UseAssignment value: true - key: modernize-use-default-member-init.IgnoreMacros @@ -180,7 +177,7 @@ CheckOptions: HeaderFilterRegex: '(include|src|extensions)/.*\.(h|c)$' # Exclude third-party code and build directories -ExcludeHeaderFilterRegex: '(tests/unit/doctest|example/build|build)/.*' +ExcludeHeaderFilterRegex: "(tests/unit/doctest|example/build|build)/.*" # Warning as errors for critical issues WarningsAsErrors: > diff --git a/extensions/ulog_generic_interface.h b/extensions/ulog_generic_interface.h index 42b9760..000e784 100644 --- a/extensions/ulog_generic_interface.h +++ b/extensions/ulog_generic_interface.h @@ -39,7 +39,7 @@ typedef enum { /// @brief Log a message at the specified level. /// @param level Log level (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL). /// @param ... printf-style format string and arguments. -#define log_message(level, ...) ulog(level, __VA_ARGS__) +#define log_message(level, ...) ulog(level, __VA_ARGS__) /// @brief Log a message with a topic at the specified level. /// @param level Log level (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL). diff --git a/extensions/ulog_lock_cmsis.c b/extensions/ulog_lock_cmsis.c index d0f11eb..6cee8c9 100644 --- a/extensions/ulog_lock_cmsis.c +++ b/extensions/ulog_lock_cmsis.c @@ -3,7 +3,9 @@ // ************************************************************************* #include "ulog_lock_cmsis.h" +#include #include "cmsis_os2.h" +#include "ulog.h" /** @brief Internal CMSIS-RTOS2 mutex adapter. */ static ulog_status cmsis_lock_fn(bool lock, void *arg) { diff --git a/extensions/ulog_lock_freertos.c b/extensions/ulog_lock_freertos.c index dc11025..e7338e0 100644 --- a/extensions/ulog_lock_freertos.c +++ b/extensions/ulog_lock_freertos.c @@ -4,8 +4,10 @@ // ************************************************************************* #include "ulog_lock_freertos.h" +#include #include "FreeRTOS.h" #include "semphr.h" +#include "ulog.h" /** @brief Internal FreeRTOS mutex adapter. */ static ulog_status freertos_lock_fn(bool lock, void *arg) { diff --git a/extensions/ulog_lock_pthread.c b/extensions/ulog_lock_pthread.c index 765e81f..ba76838 100644 --- a/extensions/ulog_lock_pthread.c +++ b/extensions/ulog_lock_pthread.c @@ -9,6 +9,9 @@ // ************************************************************************* #include "ulog_lock_pthread.h" +#include +#include +#include "ulog.h" /** * @brief Internal adapter; wraps pthread mutex operations in ulog_lock_fn diff --git a/extensions/ulog_lock_threadx.c b/extensions/ulog_lock_threadx.c index 7fbf41c..6cd36e5 100644 --- a/extensions/ulog_lock_threadx.c +++ b/extensions/ulog_lock_threadx.c @@ -2,6 +2,8 @@ // microlog extension: ThreadX mutex lock helper (implementation) // ************************************************************************* #include "ulog_lock_threadx.h" +#include +#include "ulog.h" /** @brief Internal ThreadX mutex adapter. */ static ulog_status threadx_lock_fn(bool lock, void *arg) { diff --git a/extensions/ulog_lock_win.c b/extensions/ulog_lock_win.c index 645d4b3..f7f9eb8 100644 --- a/extensions/ulog_lock_win.c +++ b/extensions/ulog_lock_win.c @@ -3,6 +3,9 @@ // ************************************************************************* #include "ulog_lock_win.h" +#include +#include +#include "ulog.h" // Internal lock function ---------------------------------------------------- /** @brief Internal lock adapter for Windows Critical Section. */ diff --git a/extensions/ulog_syslog.c b/extensions/ulog_syslog.c index d405463..18d24a1 100644 --- a/extensions/ulog_syslog.c +++ b/extensions/ulog_syslog.c @@ -2,6 +2,7 @@ // microlog extension: Syslog Levels (implementation) // ************************************************************************* +#include "ulog.h" #include "ulog_syslog.h" static ulog_level_descriptor syslog_levels = { diff --git a/src/ulog.c b/src/ulog.c index 89f50dd..7b86485 100644 --- a/src/ulog.c +++ b/src/ulog.c @@ -218,25 +218,24 @@ static inline bool is_str_empty(const char *str) { (`warn_not_enabled`, depends on: - ) ============================================================================ */ #if ULOG_HAS_WARN_NOT_ENABLED -// NOLINTBEGIN // Macro to log a warning when a feature is not enabled // Usage: warn_not_enabled("ULOG_BUILD_TIME") // Output: // WARN src/main.c:42: 'ulog_configure_time' ignored: ULOG_BUILD_TIME disabled #define warn_not_enabled(feature) \ - warn_non_enabled_full(__func__, feature, __FILE__, __LINE__) // NOLINT + warn_non_enabled_full(__func__, feature, __FILE__, __LINE__) #define warn_non_enabled_full(func, feature, file, line) \ ulog_log(ULOG_LEVEL_WARN, file, line, NULL, \ "'%s' called with %s disabled", func, feature) -// NOLINTEND #endif // ULOG_HAS_WARN_NOT_ENABLED /* ============================================================================ Core Feature: Print (`print_*`, depends on: - ) -============================================================================ */ +============================================================================ +*/ // Private // ================ @@ -477,7 +476,7 @@ ulog_status ulog_color_config(bool enabled) { // Disabled Private // ================ -#define color_config_is_enabled() (ULOG_HAS_COLOR) // NOLINT +#define color_config_is_enabled() (ULOG_HAS_COLOR) #endif // ULOG_HAS_DYNAMIC_CONFIG @@ -524,8 +523,8 @@ static void color_print_end(print_target *tgt) { // Disabled Private // ================ -#define color_print_start(tgt, ev) (void)(tgt), (void)(ev) // NOLINT -#define color_print_end(tgt) (void)(tgt) // NOLINT +#define color_print_start(tgt, ev) (void)(tgt), (void)(ev) +#define color_print_end(tgt) (void)(tgt) #endif // ULOG_HAS_COLOR @@ -577,8 +576,8 @@ ulog_status ulog_prefix_config(bool enabled) { // Disabled Private // ================ -#define prefix_config_is_enabled() (ULOG_HAS_PREFIX) // NOLINT -#endif // ULOG_HAS_DYNAMIC_CONFIG +#define prefix_config_is_enabled() (ULOG_HAS_PREFIX) +#endif // ULOG_HAS_DYNAMIC_CONFIG /* ============================================================================ Optional Feature: Prefix @@ -642,9 +641,9 @@ ulog_status ulog_prefix_set_fn(ulog_prefix_fn function) { // Disabled Private // ================ -#define prefix_print(tgt) (void)(tgt) // NOLINT -#define prefix_update(ev) (void)(ev) // NOLINT -#endif // ULOG_HAS_PREFIX +#define prefix_print(tgt) (void)(tgt) +#define prefix_update(ev) (void)(ev) +#endif // ULOG_HAS_PREFIX /* ============================================================================ Optional Feature: Dynamic Configuration - Time @@ -694,8 +693,8 @@ ulog_status ulog_time_config(bool enabled) { // Disabled Private // ================ -#define time_config_is_enabled() (ULOG_HAS_TIME) // NOLINT -#endif // ULOG_HAS_DYNAMIC_CONFIG +#define time_config_is_enabled() (ULOG_HAS_TIME) +#endif // ULOG_HAS_DYNAMIC_CONFIG /* ============================================================================ Optional Feature: Time @@ -758,10 +757,10 @@ static void time_print_full(print_target *tgt, ulog_event *ev, // Disabled Private // ================ -#define time_print_short(tgt, ev, append_space) (void)(0) // NOLINT -#define time_print_full(tgt, ev, append_space) (void)(0) // NOLINT -#define time_fill_current_time(ev) (void)(ev) // NOLINT -#endif // ULOG_HAS_TIME +#define time_print_short(tgt, ev, append_space) (void)(0) +#define time_print_full(tgt, ev, append_space) (void)(0) +#define time_fill_current_time(ev) (void)(ev) +#endif // ULOG_HAS_TIME /* ============================================================================ Core Feature: Levels @@ -906,7 +905,7 @@ ulog_status ulog_level_config(ulog_level_config_style style) { // Disabled Private // ================ -#define level_config_is_short(void) (ULOG_HAS_LEVEL_SHORT) // NOLINT +#define level_config_is_short(void) (ULOG_HAS_LEVEL_SHORT) #endif // ULOG_HAS_DYNAMIC_CONFIG /* ============================================================================ @@ -1161,8 +1160,8 @@ ulog_status ulog_topic_config(bool enabled) { // Disabled Private // ================ -#define topic_config_is_enabled() (ULOG_HAS_TOPICS) // NOLINT -#endif // ULOG_HAS_DYNAMIC_CONFIG +#define topic_config_is_enabled() (ULOG_HAS_TOPICS) +#endif // ULOG_HAS_DYNAMIC_CONFIG /* ============================================================================ Optional Feature: Topics @@ -1375,7 +1374,6 @@ ulog_status ulog_topic_remove(const char *topic_name) { } #endif // ULOG_HAS_WARN_NOT_ENABLED -// NOLINTBEGIN // Disabled Private // ================ @@ -1384,7 +1382,6 @@ ulog_status ulog_topic_remove(const char *topic_name) { (void)(topic), (void)(level), (void)(is_log_allowed), (void)(topic_id), \ (void)(output) -// NOLINTEND #endif // ULOG_HAS_TOPICS /* ============================================================================ @@ -1665,7 +1662,7 @@ ulog_status ulog_source_location_config(bool enabled) { // Disabled Private // ================ -#define src_loc_config_is_enabled() (ULOG_HAS_SOURCE_LOCATION) // NOLINT +#define src_loc_config_is_enabled() (ULOG_HAS_SOURCE_LOCATION) #endif // ULOG_HAS_DYNAMIC_CONFIG /* ============================================================================ From 0969c899014e397f1bfd4817243f54f0c9242271 Mon Sep 17 00:00:00 2001 From: Andrei Gramakov Date: Mon, 1 Dec 2025 20:37:41 +0100 Subject: [PATCH 4/6] Version --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 66ce77b..9fe9ff9 100644 --- a/version +++ b/version @@ -1 +1 @@ -7.0.0 +7.0.1 From fbd0d7c17f0787ed0964d8647add4acdea86db58 Mon Sep 17 00:00:00 2001 From: Andrei Gramakov Date: Mon, 1 Dec 2025 20:43:07 +0100 Subject: [PATCH 5/6] more fixes --- extensions/ulog_generic_interface.h | 8 +++++--- src/ulog.c | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/extensions/ulog_generic_interface.h b/extensions/ulog_generic_interface.h index 000e784..7c363ab 100644 --- a/extensions/ulog_generic_interface.h +++ b/extensions/ulog_generic_interface.h @@ -37,12 +37,14 @@ typedef enum { } log_level; /// @brief Log a message at the specified level. -/// @param level Log level (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL). +/// @param level Log level (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, +/// LOG_FATAL). /// @param ... printf-style format string and arguments. -#define log_message(level, ...) ulog(level, __VA_ARGS__) +#define log_message(level, ...) ulog(level, __VA_ARGS__) /// @brief Log a message with a topic at the specified level. -/// @param level Log level (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL). +/// @param level Log level (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, +/// LOG_FATAL). /// @param topic A string topic (e.g. "NET", "UI") to categorize the message. /// @param ... printf-style format string and arguments. #define log_topic(level, topic, ...) ulog_t(level, topic, __VA_ARGS__) diff --git a/src/ulog.c b/src/ulog.c index 7b86485..22b758d 100644 --- a/src/ulog.c +++ b/src/ulog.c @@ -18,6 +18,7 @@ #include "ulog.h" #include +#include #include #include #include From 2701825f3b50be8913dcd53c1bd06cb2688ca1e3 Mon Sep 17 00:00:00 2001 From: Andrei Gramakov Date: Mon, 1 Dec 2025 20:54:55 +0100 Subject: [PATCH 6/6] add clang tidy --- .github/workflows/clang-tidy.yml | 46 ++++++++ .github/workflows/workflow-pr.yml | 4 + scripts/check-clang-tidy.ps1 | 185 ++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 .github/workflows/clang-tidy.yml create mode 100644 scripts/check-clang-tidy.ps1 diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml new file mode 100644 index 0000000..88c9742 --- /dev/null +++ b/.github/workflows/clang-tidy.yml @@ -0,0 +1,46 @@ +name: Clang-Tidy + +on: + workflow_call: + pull_request: + branches: + - main + - release/* + paths: + - '**.c' + - '**.h' + - '**.cpp' + - '**.hpp' + - '.clang-tidy' + - 'scripts/check-clang-tidy.ps1' + push: + branches: + - main + paths: + - '**.c' + - '**.h' + - '**.cpp' + - '**.hpp' + - '.clang-tidy' + +jobs: + clang-tidy: + name: Run Clang-Tidy + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for all branches and tags + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y clang-tidy cmake ninja-build + + - name: Run clang-tidy check + run: | + pwsh scripts/check-clang-tidy.ps1 -BaseBranch origin/main + env: + BASE_BRANCH: origin/main diff --git a/.github/workflows/workflow-pr.yml b/.github/workflows/workflow-pr.yml index 94daab9..a2cbae7 100644 --- a/.github/workflows/workflow-pr.yml +++ b/.github/workflows/workflow-pr.yml @@ -13,3 +13,7 @@ jobs: name: Verify Version uses: ./.github/workflows/verify-version.yml + clang-tidy: + name: Clang-Tidy Check + uses: ./.github/workflows/clang-tidy.yml + diff --git a/scripts/check-clang-tidy.ps1 b/scripts/check-clang-tidy.ps1 new file mode 100644 index 0000000..27ee40e --- /dev/null +++ b/scripts/check-clang-tidy.ps1 @@ -0,0 +1,185 @@ +#!/usr/bin/env pwsh + +# ************************************************************************* +# +# Copyright (c) 2025 Andrei Gramakov. All rights reserved. +# +# This file is licensed under the terms of the MIT license. For a copy, see: +# https://opensource.org/licenses/MIT +# +# ************************************************************************* + +param( + [switch]$Fix, + [string]$BaseBranch = "main", + [switch]$All, + [switch]$Help +) + +if ($Help) { + Write-Host @" +Usage: check-clang-tidy.ps1 [OPTIONS] + +Options: + -Fix Apply fixes automatically + -BaseBranch Base branch to compare against (default: main) + -All Check all files instead of just changed files + -Help Show this help message + +Environment variables: + CLANG_TIDY Path to clang-tidy executable (default: clang-tidy) + BUILD_DIR Build directory for compile_commands.json (default: build/tidy) +"@ + exit 0 +} + +$ErrorActionPreference = "Stop" +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$ProjectRoot = Resolve-Path "$ScriptDir/.." +Push-Location $ProjectRoot + +try { + Write-Host "=== Clang-Tidy Check ===" -ForegroundColor Green + Write-Host "Project root: $ProjectRoot" + Write-Host "Base branch: $BaseBranch" + + # Check if clang-tidy is available + $ClangTidy = if ($env:CLANG_TIDY) { $env:CLANG_TIDY } else { "clang-tidy" } + + try { + $version = & $ClangTidy --version 2>&1 | Select-Object -First 1 + Write-Host "Using clang-tidy: $version" + } catch { + Write-Host "Error: clang-tidy not found. Please install clang-tidy." -ForegroundColor Red + Write-Host "On Ubuntu/Debian: sudo apt-get install clang-tidy" + Write-Host "On macOS: brew install llvm" + Write-Host "On Windows: Install LLVM from https://llvm.org/" + exit 1 + } + + # Get list of files to check + $Files = @() + + if ($All) { + Write-Host "Checking all C/C++ files" -ForegroundColor Yellow + $Files = Get-ChildItem -Path src,include,extensions -Include *.c,*.h,*.cpp,*.hpp -Recurse -ErrorAction SilentlyContinue | + ForEach-Object { $_.FullName -replace [regex]::Escape($ProjectRoot + [IO.Path]::DirectorySeparatorChar), "" } + } else { + # Check if base branch exists + $branchExists = git rev-parse --verify $BaseBranch 2>$null + + if ($LASTEXITCODE -eq 0) { + Write-Host "Checking files changed compared to $BaseBranch" -ForegroundColor Yellow + + # Get committed changes on current branch + $changedFiles = git diff --name-only "$BaseBranch...HEAD" 2>$null | + Where-Object { $_ -match '\.(c|h|cpp|hpp)$' } + + # Also include uncommitted changes + $uncommittedFiles = git diff --name-only HEAD 2>$null | + Where-Object { $_ -match '\.(c|h|cpp|hpp)$' } + + # Combine and get unique files + $Files = ($changedFiles + $uncommittedFiles) | Select-Object -Unique + } else { + Write-Host "Warning: Base branch $BaseBranch not found. Checking all files." -ForegroundColor Yellow + $Files = Get-ChildItem -Path src,include,extensions -Include *.c,*.h,*.cpp,*.hpp -Recurse -ErrorAction SilentlyContinue | + ForEach-Object { $_.FullName -replace [regex]::Escape($ProjectRoot + [IO.Path]::DirectorySeparatorChar), "" } + } + } + + # Filter to only existing files + $Files = $Files | Where-Object { Test-Path $_ } + + if ($Files.Count -eq 0) { + Write-Host "No C/C++ files to check." -ForegroundColor Green + exit 0 + } + + Write-Host "Files to check:" -ForegroundColor Yellow + $Files | ForEach-Object { Write-Host " - $_" } + + # Setup build directory + $BuildDir = if ($env:BUILD_DIR) { $env:BUILD_DIR } else { "build/tidy" } + $CompileCommands = Join-Path $BuildDir "compile_commands.json" + + # Generate compile_commands.json if it doesn't exist + if (-not (Test-Path $CompileCommands)) { + Write-Host "Generating compile_commands.json..." -ForegroundColor Yellow + + if (Get-Command cmake -ErrorAction SilentlyContinue) { + New-Item -ItemType Directory -Force -Path $BuildDir | Out-Null + cmake -B $BuildDir -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DULOG_BUILD_TESTS=OFF 2>&1 | Out-Null + + if ($LASTEXITCODE -ne 0) { + Write-Host "Warning: Could not generate compile_commands.json with CMake" -ForegroundColor Yellow + } + } + } + + # Run clang-tidy on each file + $Errors = 0 + $Warnings = 0 + $TotalFiles = 0 + + foreach ($file in $Files) { + Write-Host "" + Write-Host "Checking: $file" -ForegroundColor Yellow + $TotalFiles++ + + # Build the clang-tidy command arguments + $tidyArgs = @($file) + + # Add compile commands if available + if (Test-Path $CompileCommands) { + $tidyArgs += "-p=$BuildDir" + } + + # Add fix mode if requested + if ($Fix) { + $tidyArgs += "-fix" + } + + # Run clang-tidy and capture output + $output = & $ClangTidy $tidyArgs 2>&1 | Out-String + + # Check for errors and warnings + if ($output -match "error:") { + $Errors++ + Write-Host "✗ Found errors" -ForegroundColor Red + Write-Host $output + } elseif ($output -match "warning:") { + $Warnings++ + Write-Host "⚠ Found warnings" -ForegroundColor Yellow + Write-Host $output + } else { + Write-Host "✓ No issues found" -ForegroundColor Green + } + } + + # Print summary + Write-Host "" + Write-Host "=== Summary ===" -ForegroundColor Green + Write-Host "Files checked: $TotalFiles" + Write-Host "Files with warnings: $Warnings" -ForegroundColor Yellow + Write-Host "Files with errors: $Errors" -ForegroundColor Red + + # Exit with error code if there were errors + if ($Errors -gt 0) { + Write-Host "Clang-tidy check failed with errors." -ForegroundColor Red + exit 1 + } elseif ($Warnings -gt 0) { + Write-Host "Clang-tidy check completed with warnings." -ForegroundColor Yellow + exit 0 + } else { + Write-Host "Clang-tidy check passed successfully!" -ForegroundColor Green + exit 0 + } + +} catch { + Write-Host "An error occurred: $_" -ForegroundColor Red + Pop-Location + exit 1 +} finally { + Pop-Location +}