From dd07767d6a57731be2e43c29984183dff0c0aead Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 22 Oct 2024 22:58:36 +0100 Subject: [PATCH 1/3] Rule 2.5: Consider macros accessed before definition --- .../rules/RULE-2-5/UnusedMacroDeclaration.ql | 11 +++++++ c/misra/test/rules/RULE-2-5/test.c | 33 ++++++++++++++++++- change_notes/2024-10-22-rule-2-5.md | 2 ++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 change_notes/2024-10-22-rule-2-5.md diff --git a/c/misra/src/rules/RULE-2-5/UnusedMacroDeclaration.ql b/c/misra/src/rules/RULE-2-5/UnusedMacroDeclaration.ql index b7ea9f64de..5e70755eea 100644 --- a/c/misra/src/rules/RULE-2-5/UnusedMacroDeclaration.ql +++ b/c/misra/src/rules/RULE-2-5/UnusedMacroDeclaration.ql @@ -19,7 +19,18 @@ import codingstandards.c.misra from Macro m where not isExcluded(m, DeadCodePackage::unusedMacroDeclarationQuery()) and + // We consider a macro "used" if there is a macro access not exists(MacroAccess ma | ma.getMacro() = m) and + // Or if there exists a check whether the macro is defined which the extractor + // hasn't been able to tie to a macro (usually because this use came before + // the macro was defined e.g. a header guard) + not exists(PreprocessorBranchDirective bd | + // Covers the #ifdef and #ifndef cases + bd.getHead() = m.getName() + or + // Covers the use of defined() to check if a macro is defined + m.getName() = bd.getHead().regexpCapture(".*defined\\((.*)\\).*", 1) + ) and // We consider a macro "used" if the name is undef-ed at some point in the same file, or a file // that includes the file defining the macro. This will over approximate use in the case of a // macro which is defined, then undefined, then re-defined but not used. diff --git a/c/misra/test/rules/RULE-2-5/test.c b/c/misra/test/rules/RULE-2-5/test.c index f37acb1509..755b783eab 100644 --- a/c/misra/test/rules/RULE-2-5/test.c +++ b/c/misra/test/rules/RULE-2-5/test.c @@ -13,4 +13,35 @@ void test() { MACRO2; HEADER_MACRO2; -} \ No newline at end of file +} + +#define CHECKED_MACRO_1 // COMPLIANT - used in branch +#define CHECKED_MACRO_2 // COMPLIANT - used in branch +#define CHECKED_MACRO_3 // COMPLIANT - used in branch + +#ifdef CHECKED_MACRO_1 +#endif + +#ifndef CHECKED_MACRO_2 +#endif + +#if defined(CHECKED_MACRO_3) +#endif + +// In the case above, the extractor will identify macro accesses with each use +// of the macro. In the case above, the extractor does not tie them together, +// but the standard considers this acceptable usage. Notably, this type of +// pattern occurs for header guards. + +#ifdef CHECKED_MACRO_BEFORE_1 +#endif + +#ifndef CHECKED_MACRO_BEFORE_2 +#endif + +#if defined(CHECKED_MACRO_BEFORE_3) +#endif + +#define CHECKED_MACRO_BEFORE_1 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_2 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_3 // COMPLIANT - used in branch \ No newline at end of file diff --git a/change_notes/2024-10-22-rule-2-5.md b/change_notes/2024-10-22-rule-2-5.md new file mode 100644 index 0000000000..6de3f0be11 --- /dev/null +++ b/change_notes/2024-10-22-rule-2-5.md @@ -0,0 +1,2 @@ + - `RULE-2-5` - `UnusedMacroDeclaration.ql`: + - Exclude false positives where a macro was used before definition, for example a header guard. \ No newline at end of file From 5b61358f7f3dc1fb525b279b19ed098bfa56b1c7 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 23 Oct 2024 14:38:02 +0100 Subject: [PATCH 2/3] Improve regex to handle spaces --- .../src/rules/RULE-2-5/UnusedMacroDeclaration.ql | 2 +- c/misra/test/rules/RULE-2-5/test.c | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/c/misra/src/rules/RULE-2-5/UnusedMacroDeclaration.ql b/c/misra/src/rules/RULE-2-5/UnusedMacroDeclaration.ql index 5e70755eea..2b5a8e8c1d 100644 --- a/c/misra/src/rules/RULE-2-5/UnusedMacroDeclaration.ql +++ b/c/misra/src/rules/RULE-2-5/UnusedMacroDeclaration.ql @@ -29,7 +29,7 @@ where bd.getHead() = m.getName() or // Covers the use of defined() to check if a macro is defined - m.getName() = bd.getHead().regexpCapture(".*defined\\((.*)\\).*", 1) + m.getName() = bd.getHead().regexpCapture(".*defined *\\(? *([^\\s()]+) *\\)?\\.*", 1) ) and // We consider a macro "used" if the name is undef-ed at some point in the same file, or a file // that includes the file defining the macro. This will over approximate use in the case of a diff --git a/c/misra/test/rules/RULE-2-5/test.c b/c/misra/test/rules/RULE-2-5/test.c index 755b783eab..e220b3d444 100644 --- a/c/misra/test/rules/RULE-2-5/test.c +++ b/c/misra/test/rules/RULE-2-5/test.c @@ -42,6 +42,18 @@ void test() { #if defined(CHECKED_MACRO_BEFORE_3) #endif +#if defined(CHECKED_MACRO_BEFORE_4) +#endif + +#if defined(CHECKED_MACRO_BEFORE_5) +#endif + +#if defined(CHECKED_MACRO_BEFORE_6) +#endif + #define CHECKED_MACRO_BEFORE_1 // COMPLIANT - used in branch #define CHECKED_MACRO_BEFORE_2 // COMPLIANT - used in branch -#define CHECKED_MACRO_BEFORE_3 // COMPLIANT - used in branch \ No newline at end of file +#define CHECKED_MACRO_BEFORE_3 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_4 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_5 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_6 // COMPLIANT - used in branch \ No newline at end of file From cbcf0374aa9ac115e25b428a70717cdd705497c1 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 23 Oct 2024 14:43:45 +0100 Subject: [PATCH 3/3] Add test case for spaces, ensure no clang-format --- c/misra/test/rules/RULE-2-5/test.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/c/misra/test/rules/RULE-2-5/test.c b/c/misra/test/rules/RULE-2-5/test.c index e220b3d444..15930f68d1 100644 --- a/c/misra/test/rules/RULE-2-5/test.c +++ b/c/misra/test/rules/RULE-2-5/test.c @@ -42,18 +42,26 @@ void test() { #if defined(CHECKED_MACRO_BEFORE_3) #endif -#if defined(CHECKED_MACRO_BEFORE_4) +// clang-format off + +#if defined (CHECKED_MACRO_BEFORE_4) +#endif + +#if defined( CHECKED_MACRO_BEFORE_5 ) #endif -#if defined(CHECKED_MACRO_BEFORE_5) +#if defined ( CHECKED_MACRO_BEFORE_6 ) #endif -#if defined(CHECKED_MACRO_BEFORE_6) +#if defined CHECKED_MACRO_BEFORE_7 #endif +// clang-format on + #define CHECKED_MACRO_BEFORE_1 // COMPLIANT - used in branch #define CHECKED_MACRO_BEFORE_2 // COMPLIANT - used in branch #define CHECKED_MACRO_BEFORE_3 // COMPLIANT - used in branch #define CHECKED_MACRO_BEFORE_4 // COMPLIANT - used in branch #define CHECKED_MACRO_BEFORE_5 // COMPLIANT - used in branch -#define CHECKED_MACRO_BEFORE_6 // COMPLIANT - used in branch \ No newline at end of file +#define CHECKED_MACRO_BEFORE_6 // COMPLIANT - used in branch +#define CHECKED_MACRO_BEFORE_7 // COMPLIANT - used in branch \ No newline at end of file