Improve type checks

This commit is contained in:
Maschell 2026-04-04 13:23:22 +02:00
parent 86b08aad46
commit 2bdff9287d
4 changed files with 173 additions and 189 deletions

View File

@ -3,13 +3,14 @@
#ifdef __cplusplus
#include <cstddef> /* For std::nullptr_t */
#include <type_traits>
#else
#include <stddef.h> /* 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<typename T>
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<typename T>
inline bool check_float(T) { return false; }
template<typename Actual>
using Decayed = std::decay_t<Actual>;
/* 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<typename T>
inline bool check_bool(T) { return false; }
/* Strict matching: Must be the NMColor type or a raw integer (for hex literals) */
template<typename Actual>
constexpr bool is_NMColor = std::is_same_v<NMColor, Decayed<Actual>> || std::is_integral_v<Decayed<Actual>>;
/* 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<typename T>
inline bool check_callback(T) { return false; }
/* Strict matching: Float or Double only (Rejects implicit ints/pointers) */
template<typename Actual>
constexpr bool is_float = std::is_same_v<float, Decayed<Actual>> || std::is_same_v<double, Decayed<Actual>>;
/* 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<typename T>
inline bool check_context(T) { return false; }
/* Strict matching: Bool or Int only (Rejects implicit pointers) */
template<typename Actual>
constexpr bool is_bool = std::is_same_v<bool, Decayed<Actual>> || std::is_same_v<int, Decayed<Actual>>;
/* Strict matching: Callback type, void*, nullptr, or 0 */
template<typename Actual>
constexpr bool is_callback =
std::is_same_v<NotificationModuleNotificationFinishedCallback, Decayed<Actual>> ||
std::is_same_v<void *, Decayed<Actual>> ||
std::is_same_v<std::nullptr_t, Decayed<Actual>> ||
std::is_integral_v<Decayed<Actual>>;
/* Strict matching: void*, nullptr, or 0 */
template<typename Actual>
constexpr bool is_context =
std::is_same_v<void *, Decayed<Actual>> ||
std::is_same_v<std::nullptr_t, Decayed<Actual>> ||
std::is_integral_v<Decayed<Actual>>;
/* Compile-time option router */
template<int Option, typename Actual>
inline void check_option() {
if constexpr (Option == NOTIFICATION_MODULE_DEFAULT_OPTION_BACKGROUND_COLOR ||
Option == NOTIFICATION_MODULE_DEFAULT_OPTION_TEXT_COLOR) {
if constexpr (!is_NMColor<Actual>) _nm_warn_NMColor();
} else if constexpr (Option == NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT) {
if constexpr (!is_float<Actual>) _nm_warn_float();
} else if constexpr (Option == NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION) {
if constexpr (!is_callback<Actual>) _nm_warn_callback();
} else if constexpr (Option == NOTIFICATION_MODULE_DEFAULT_OPTION_FINISH_FUNCTION_CONTEXT) {
if constexpr (!is_context<Actual>) _nm_warn_context();
} else if constexpr (Option == NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN) {
if constexpr (!is_bool<Actual>) _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 */

View File

@ -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__

View File

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

View File

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