From 2bdff9287d224b23f0e202fdcadf22756e54a8c9 Mon Sep 17 00:00:00 2001 From: Maschell Date: Sat, 4 Apr 2026 13:23:22 +0200 Subject: [PATCH] Improve type checks --- include/notifications/typechecks-gcc.h | 174 +++++++++------------ tests/parameter_checker/Makefile | 2 +- tests/parameter_checker/negative_runner.sh | 87 ++++++----- tests/parameter_checker/runner.sh | 99 ++++++------ 4 files changed, 173 insertions(+), 189 deletions(-) diff --git a/include/notifications/typechecks-gcc.h b/include/notifications/typechecks-gcc.h index e0a914b..faba4fe 100644 --- a/include/notifications/typechecks-gcc.h +++ b/include/notifications/typechecks-gcc.h @@ -3,13 +3,14 @@ #ifdef __cplusplus #include /* For std::nullptr_t */ +#include #else #include /* For NULL */ #endif -/* * Warning generators (Common to C and C++) - * These define static functions that trigger a compiler warning when called. - */ +/* ========================================================================= + * Warning generators (Common to C and C++) + * ========================================================================= */ #define _NM_WARNING(id, message) \ static void __attribute__((__warning__(message))) \ __attribute__((__unused__)) __attribute__((__noinline__)) \ @@ -29,127 +30,99 @@ _NM_WARNING(_nm_warn_bool, "NotificationModule_SetDefaultValue expects 'bool' (o } #endif - /* ========================================================================= * IMPLEMENTATION SELECTION * ========================================================================= */ #if defined(__cplusplus) - -/* ========================================== - * C++17 Implementation - * Only active if -std=c++17 or higher is used. - * ========================================== */ +/* --------------------------------------------------------- + * C++ IMPLEMENTATION + * --------------------------------------------------------- */ #if __cplusplus >= 201703L - namespace NM_Check { - /* NMColor Checker */ - inline bool check_NMColor(NMColor) { return true; } - inline bool check_NMColor(void *) { return false; } /* Sink for NULL */ - template - inline bool check_NMColor(T) { return false; } - /* Float Checker (accepts float/double) */ - inline bool check_float(float) { return true; } - inline bool check_float(double) { return true; } - inline bool check_float(void *) { return false; } /* Sink for NULL */ - template - inline bool check_float(T) { return false; } + template + using Decayed = std::decay_t; - /* Bool Checker (accepts bool/int) */ - inline bool check_bool(bool) { return true; } - inline bool check_bool(int) { return true; } - inline bool check_bool(void *) { return false; } /* Sink for NULL */ - template - inline bool check_bool(T) { return false; } + /* Strict matching: Must be the NMColor type or a raw integer (for hex literals) */ + template + constexpr bool is_NMColor = std::is_same_v> || std::is_integral_v>; - /* Callback Checker */ - inline bool check_callback(NotificationModuleNotificationFinishedCallback) { return true; } - /* Explicit void* (e.g. casting) */ - inline bool check_callback(void *) { return true; } - inline bool check_callback(int i) { return i == 0; } - inline bool check_callback(long i) { return i == 0; } - inline bool check_callback(std::nullptr_t) { return true; } - template - inline bool check_callback(T) { return false; } + /* Strict matching: Float or Double only (Rejects implicit ints/pointers) */ + template + constexpr bool is_float = std::is_same_v> || std::is_same_v>; - /* Context Checker */ - inline bool check_context(void *) { return true; } - inline bool check_context(int i) { return i == 0; } - inline bool check_context(long i) { return i == 0; } - inline bool check_context(std::nullptr_t) { return true; } - template - inline bool check_context(T) { return false; } + /* Strict matching: Bool or Int only (Rejects implicit pointers) */ + template + constexpr bool is_bool = std::is_same_v> || std::is_same_v>; + + /* Strict matching: Callback type, void*, nullptr, or 0 */ + template + constexpr bool is_callback = + std::is_same_v> || + std::is_same_v> || + std::is_same_v> || + std::is_integral_v>; + + /* Strict matching: void*, nullptr, or 0 */ + template + constexpr bool is_context = + std::is_same_v> || + std::is_same_v> || + std::is_integral_v>; + + /* Compile-time option router */ + template + inline void check_option() { + if constexpr (Option == NOTIFICATION_MODULE_DEFAULT_OPTION_BACKGROUND_COLOR || + Option == NOTIFICATION_MODULE_DEFAULT_OPTION_TEXT_COLOR) { + if constexpr (!is_NMColor) _nm_warn_NMColor(); + } else if constexpr (Option == NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT) { + if constexpr (!is_float) _nm_warn_float(); + } else if constexpr (Option == NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION) { + if constexpr (!is_callback) _nm_warn_callback(); + } else if constexpr (Option == NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION_CONTEXT) { + if constexpr (!is_context) _nm_warn_context(); + } else if constexpr (Option == NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN) { + if constexpr (!is_bool) _nm_warn_bool(); + } + } } // namespace NM_Check -/* Macros mapping to C++ namespace calls */ -#define _nm_is_NMColor(x) NM_Check::check_NMColor(x) -#define _nm_is_float(x) NM_Check::check_float(x) -#define _nm_is_bool(x) NM_Check::check_bool(x) -#define _nm_is_callback(x) NM_Check::check_callback(x) -#define _nm_is_context(x) NM_Check::check_context(x) - +/* C++17 Macro (Uses if constexpr) */ +#define NotificationModule_SetDefaultValue(type, option, value) \ + __extension__({ \ + NM_Check::check_option<__builtin_constant_p(option) ? (option) : -1, decltype(value)>(); \ + (NotificationModule_SetDefaultValue)(type, option, value); \ + }) #else -/* ========================================== - * Pre-C++17 Implementation - * Checks are DISABLED to avoid errors in older versions. - * ========================================== */ -#define _nm_is_NMColor(x) (1) -#define _nm_is_float(x) (1) -#define _nm_is_bool(x) (1) -#define _nm_is_callback(x) (1) -#define _nm_is_context(x) (1) +/* Fallback for old C++ (Checks disabled) */ +#define NotificationModule_SetDefaultValue(type, option, value) \ + (NotificationModule_SetDefaultValue)(type, option, value) #endif -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L - -/* ========================================== - * C11 Implementation (using _Generic) - * ========================================== */ - -#define _nm_is_NMColor(x) _Generic((x), \ - NMColor : 1, \ - default : 0) - -#define _nm_is_float(x) _Generic((x), \ - float : 1, \ - double : 1, \ - default : 0) - -#define _nm_is_bool(x) _Generic((x), \ - _Bool : 1, \ - int : 1, \ - default : 0) - -#define _nm_is_callback(x) _Generic((x), \ - NotificationModuleNotificationFinishedCallback : 1, \ - void * : 1, \ - default : 0) - -#define _nm_is_context(x) _Generic((x), \ - void * : 1, \ - default : 0) - #else - -/* ========================================== - * Legacy C Implementation - * Partial checking only. - * ========================================== */ - +/* --------------------------------------------------------- + * C IMPLEMENTATION + * --------------------------------------------------------- */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +/* C11 Implementation */ +#define _nm_is_NMColor(x) _Generic((x), NMColor : 1, default : 0) +#define _nm_is_float(x) _Generic((x), float : 1, double : 1, default : 0) +#define _nm_is_bool(x) _Generic((x), _Bool : 1, int : 1, default : 0) +#define _nm_is_callback(x) _Generic((x), NotificationModuleNotificationFinishedCallback : 1, void * : 1, default : 0) +#define _nm_is_context(x) _Generic((x), void * : 1, default : 0) +#else +/* Legacy C Implementation */ #define _nm_is_type(x, type) __builtin_types_compatible_p(__typeof__(x), type) - -/* Disable complex checks in old C */ #define _nm_is_NMColor(x) (1) #define _nm_is_callback(x) (1) #define _nm_is_context(x) (1) - -/* Scalars are safe to check */ #define _nm_is_float(x) (_nm_is_type(x, float) || _nm_is_type(x, double)) #define _nm_is_bool(x) (_nm_is_type(x, int) || _nm_is_type(x, _Bool)) +#endif /* C11 Check */ -#endif - +/* Unified C Macro (C-only) */ #define NotificationModule_SetDefaultValue(type, option, value) \ __extension__({ \ if (__builtin_constant_p(option)) { \ @@ -168,5 +141,6 @@ namespace NM_Check { } \ (NotificationModule_SetDefaultValue)(type, option, value); \ }) +#endif #endif /* NOTIFICATIONS_TYPECHECK_GCC_H */ \ No newline at end of file diff --git a/tests/parameter_checker/Makefile b/tests/parameter_checker/Makefile index 4c78162..466d061 100644 --- a/tests/parameter_checker/Makefile +++ b/tests/parameter_checker/Makefile @@ -47,7 +47,7 @@ DRC_SPLASH := #------------------------------------------------------------------------------- # options for code generation #------------------------------------------------------------------------------- -CFLAGS := -g -Wall -Werror -O2 -ffunction-sections \ +CFLAGS := -g -Wall -Werror -ffunction-sections \ $(MACHDEP) $(USER_CFLAGS) CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ diff --git a/tests/parameter_checker/negative_runner.sh b/tests/parameter_checker/negative_runner.sh index 7e0b2a1..88d0f68 100644 --- a/tests/parameter_checker/negative_runner.sh +++ b/tests/parameter_checker/negative_runner.sh @@ -6,7 +6,7 @@ RED='\033[0;31m' NC='\033[0m' # No Color echo "========================================" -echo "Starting Advanced Compilation Matrix" +echo "Starting Advanced 2D Compilation Matrix" echo "========================================" # Track overall status @@ -19,22 +19,24 @@ run_check() { TEST_NAME=$1 # e.g., TEST_FAIL_COLOR SRC_DIR=$2 # e.g., src_fail_cpp STD=$3 # e.g., gnu++17 - TYPE=$4 # CXX or C + OPT=$4 # e.g., -O0 + TYPE=$5 # CXX or C - echo -n "Checking $TEST_NAME [$STD]... " + # Updated echo to show both the Standard and the Optimization Level + echo -n "Checking $TEST_NAME [$STD | $OPT]... " # 1. POSITIVE VERIFICATION (Must Compile with -DMAKE_VALID) make clean --no-print-directory > /dev/null 2>&1 - - # Construct Flags + + # Construct Flags (Now including $OPT) if [ "$TYPE" == "CXX" ]; then - FLAGS="-std=$STD -D$TEST_NAME -DMAKE_VALID" + FLAGS="-std=$STD $OPT -D$TEST_NAME -DMAKE_VALID" OUT=$(make TEST_SRC=$SRC_DIR USER_CXXFLAGS="$FLAGS" --no-print-directory 2>&1) else - FLAGS="-std=$STD -D$TEST_NAME -DMAKE_VALID" + FLAGS="-std=$STD $OPT -D$TEST_NAME -DMAKE_VALID" OUT=$(make TEST_SRC=$SRC_DIR USER_CFLAGS="$FLAGS" --no-print-directory 2>&1) fi - + if [ $? -ne 0 ]; then echo -e "${RED}[ERROR]${NC}" echo " -> Valid code failed to build! Setup is broken." @@ -45,19 +47,23 @@ run_check() { # 2. NEGATIVE VERIFICATION (Must FAIL without -DMAKE_VALID) make clean --no-print-directory > /dev/null 2>&1 - + if [ "$TYPE" == "CXX" ]; then - FLAGS="-std=$STD -D$TEST_NAME" + FLAGS="-std=$STD $OPT -D$TEST_NAME" OUT=$(make TEST_SRC=$SRC_DIR USER_CXXFLAGS="$FLAGS" --no-print-directory 2>&1) else - FLAGS="-std=$STD -D$TEST_NAME" + FLAGS="-std=$STD $OPT -D$TEST_NAME" OUT=$(make TEST_SRC=$SRC_DIR USER_CFLAGS="$FLAGS" --no-print-directory 2>&1) fi if [ $? -eq 0 ]; then echo -e "${RED}[ERROR]${NC}" echo " -> Invalid code SUCCEEDED! Type check failed to catch error." - echo "make TEST_SRC=$SRC_DIR USER_CFLAGS="$FLAGS" --no-print-directory 2>&1" + if [ "$TYPE" == "CXX" ]; then + echo " make TEST_SRC=$SRC_DIR USER_CXXFLAGS=\"$FLAGS\" --no-print-directory 2>&1" + else + echo " make TEST_SRC=$SRC_DIR USER_CFLAGS=\"$FLAGS\" --no-print-directory 2>&1" + fi OVERALL_SUCCESS=false return fi @@ -65,42 +71,51 @@ run_check() { echo -e "${GREEN}[OK]${NC}" } +# --------------------------------------------------------- +# DEFINE MATRICES +# --------------------------------------------------------- +CPP_STANDARDS=("c++17" "gnu++17" "c++20" "gnu++20" "c++23" "gnu++23") +C_STANDARDS=("c11" "gnu11" "c17" "gnu17" "gnu18" "gnu2x" "c23" "gnu23") +C_SCALAR_CHECK_VERSIONS=("gnu99" "c99") + +# Define Optimization Matrix +OPT_LEVELS=("-O0" "-O1" "-O2" "-O3" "-Os" "-Ofast" "-Og") + + # --------------------------------------------------------- # C++ NEGATIVE TESTS # --------------------------------------------------------- -# Note: C++ Type checks are only active on C++17 and newer. -CPP_FULL_CHECK_VERSIONS=("gnu++17" "gnu++20" "gnu++23") - -for std in "${CPP_FULL_CHECK_VERSIONS[@]}"; do - run_check "TEST_FAIL_COLOR" "src_fail_cpp" "$std" "CXX" - run_check "TEST_FAIL_DURATION" "src_fail_cpp" "$std" "CXX" - run_check "TEST_FAIL_CALLBACK" "src_fail_cpp" "$std" "CXX" - run_check "TEST_FAIL_CONTEXT" "src_fail_cpp" "$std" "CXX" - run_check "TEST_FAIL_BOOL" "src_fail_cpp" "$std" "CXX" +for std in "${CPP_STANDARDS[@]}"; do + for opt in "${OPT_LEVELS[@]}"; do + run_check "TEST_FAIL_COLOR" "src_fail_cpp" "$std" "$opt" "CXX" + run_check "TEST_FAIL_DURATION" "src_fail_cpp" "$std" "$opt" "CXX" + run_check "TEST_FAIL_CALLBACK" "src_fail_cpp" "$std" "$opt" "CXX" + run_check "TEST_FAIL_CONTEXT" "src_fail_cpp" "$std" "$opt" "CXX" + run_check "TEST_FAIL_BOOL" "src_fail_cpp" "$std" "$opt" "CXX" + done done # --------------------------------------------------------- # C NEGATIVE TESTS # --------------------------------------------------------- -# C11 and newer have support for _Generic (Full Checks) -C_FULL_CHECK_VERSIONS=("gnu11" "gnu17" "gnu18" "gnu2x") -# C99 only supports built-in compatibility checks (Scalar only: float, bool) -C_SCALAR_CHECK_VERSIONS=("gnu99") - # 1. Full Checks (Color, Callback, Context) - C11+ Only -for std in "${C_FULL_CHECK_VERSIONS[@]}"; do - run_check "TEST_FAIL_COLOR" "src_fail_c" "$std" "C" - run_check "TEST_FAIL_CALLBACK" "src_fail_c" "$std" "C" - run_check "TEST_FAIL_CONTEXT" "src_fail_c" "$std" "C" - run_check "TEST_FAIL_DURATION" "src_fail_c" "$std" "C" - run_check "TEST_FAIL_BOOL" "src_fail_c" "$std" "C" +for std in "${C_STANDARDS[@]}"; do + for opt in "${OPT_LEVELS[@]}"; do + run_check "TEST_FAIL_COLOR" "src_fail_c" "$std" "$opt" "C" + run_check "TEST_FAIL_CALLBACK" "src_fail_c" "$std" "$opt" "C" + run_check "TEST_FAIL_CONTEXT" "src_fail_c" "$std" "$opt" "C" + run_check "TEST_FAIL_DURATION" "src_fail_c" "$std" "$opt" "C" + run_check "TEST_FAIL_BOOL" "src_fail_c" "$std" "$opt" "C" + done done # 2. Scalar Checks (Float, Bool) - C99+ for std in "${C_SCALAR_CHECK_VERSIONS[@]}"; do - run_check "TEST_FAIL_COLOR" "src_fail_c" "$std" "C" - run_check "TEST_FAIL_DURATION" "src_fail_c" "$std" "C" - run_check "TEST_FAIL_BOOL" "src_fail_c" "$std" "C" + for opt in "${OPT_LEVELS[@]}"; do + run_check "TEST_FAIL_COLOR" "src_fail_c" "$std" "$opt" "C" + run_check "TEST_FAIL_DURATION" "src_fail_c" "$std" "$opt" "C" + run_check "TEST_FAIL_BOOL" "src_fail_c" "$std" "$opt" "C" + done done # --------------------------------------------------------- @@ -108,7 +123,7 @@ done # --------------------------------------------------------- if [ "$OVERALL_SUCCESS" = true ]; then echo "========================================" - echo -e "${GREEN}All checks passed successfully!${NC}" + echo -e "${GREEN}All checks passed successfully across all optimizations!${NC}" echo "========================================" exit 0 else diff --git a/tests/parameter_checker/runner.sh b/tests/parameter_checker/runner.sh index e10c3fa..551aeaa 100644 --- a/tests/parameter_checker/runner.sh +++ b/tests/parameter_checker/runner.sh @@ -4,69 +4,64 @@ set -e echo "========================================" -echo "Starting Compilation Matrix" +echo "Starting 2D Compilation Matrix" echo "========================================" -# ------------------------------------------------------------------ -# 1. POSITIVE TESTS (Should Compile) -# ------------------------------------------------------------------ +# Optimization Levels Array +OPT_LEVELS=("-O0" "-O1" "-O2" "-O3" "-Os" "-Ofast" "-Og") # C++ Standards -CPP_STANDARDS=("gnu++11" "gnu++14" "gnu++17" "gnu++2a") - -for std in "${CPP_STANDARDS[@]}"; do - echo "[PASS-CHECK] C++ Source with -std=$std" - make clean --no-print-directory - make TEST_SRC=src_cpp USER_CXXFLAGS="-std=$std" --no-print-directory - echo " -> SUCCESS" -done +CPP_STANDARDS=("c++11" "gnu++11" "c++14" "gnu++14" "c++17" "gnu++17" "c++20" "gnu++20" "c++23" "gnu++23") # C Standards -C_STANDARDS=("gnu99" "gnu11" "gnu17") +C_STANDARDS=("c99" "gnu99" "c11" "gnu11" "c17" "gnu17" "c23" "gnu23") -for std in "${C_STANDARDS[@]}"; do - echo "[PASS-CHECK] C Source with -std=$std" - make clean --no-print-directory - make TEST_SRC=src_c USER_CFLAGS="-std=$std" --no-print-directory - echo " -> SUCCESS" +# ------------------------------------------------------------------ +# 1. POSITIVE TESTS (Should Compile without errors or warnings) +# ------------------------------------------------------------------ + +for std in "${CPP_STANDARDS[@]}"; do + for opt in "${OPT_LEVELS[@]}"; do + echo -n "[PASS-CHECK] C++ Source with -std=$std | $opt... " + make clean --no-print-directory > /dev/null 2>&1 + + set +e # Turn off exit-on-error temporarily + OUTPUT=$(make TEST_SRC=src_cpp USER_CXXFLAGS="-std=$std $opt" --no-print-directory 2>&1) + EXIT_CODE=$? + set -e # Turn it back on + + if [ $EXIT_CODE -ne 0 ]; then + echo -e "\n -> ERROR: Build failed!" + echo "================ COMPILER OUTPUT ================" + echo "$OUTPUT" + echo "=================================================" + exit 1 + else + echo "SUCCESS" + fi + done done -# ------------------------------------------------------------------ -# 2. NEGATIVE TESTS (Should FAIL to Compile) -# ------------------------------------------------------------------ -echo "========================================" -echo "Starting Negative Tests (Expect Errors)" -echo "========================================" +for std in "${C_STANDARDS[@]}"; do + for opt in "${OPT_LEVELS[@]}"; do + echo -n "[PASS-CHECK] C Source with -std=$std | $opt... " + make clean --no-print-directory > /dev/null 2>&1 -FAIL_CASES=( - "src_fail/fail_color" - "src_fail/fail_duration" - "src_fail/fail_callback" - "src_fail/fail_bool" -) + set +e # Turn off exit-on-error temporarily + OUTPUT=$(make TEST_SRC=src_c USER_CFLAGS="-std=$std $opt" --no-print-directory 2>&1) + EXIT_CODE=$? + set -e # Turn it back on -# Temporarily turn off 'set -e' so we can capture the failure -set +e - -for test_dir in "${FAIL_CASES[@]}"; do - echo "[FAIL-CHECK] Testing $test_dir (Should fail build)" - - make clean --no-print-directory > /dev/null 2>&1 - - # Run Make and capture output to prevent cluttering logs, - # unless you want to see the specific error. - OUTPUT=$(make TEST_SRC=$test_dir --no-print-directory 2>&1) - EXIT_CODE=$? - - if [ $EXIT_CODE -eq 0 ]; then - echo " -> ERROR: Build succeeded but SHOULD HAVE FAILED!" - echo " (Type checks failed to catch the invalid parameter)" - exit 1 - else - echo " -> SUCCESS: Build failed as expected." - # Optional: Check if the output contains the specific warning message? - # echo "$OUTPUT" | grep "NotificationModule_SetDefaultValue expects" - fi + if [ $EXIT_CODE -ne 0 ]; then + echo -e "\n -> ERROR: Build failed!" + echo "================ COMPILER OUTPUT ================" + echo "$OUTPUT" + echo "=================================================" + exit 1 + else + echo "SUCCESS" + fi + done done # Re-enable exit on error