From 00f84f18ea79ece133fee018c3075ed2baf6e055 Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Fri, 15 Jul 2022 11:54:02 -0400 Subject: [PATCH] Add Preprocessor 4 package --- .vscode/tasks.json | 4 +- c/common/src/codingstandards/c/Keywords.qll | 95 +++++++++++++++++++ ...ssingDirectiveWithinMacroArgument.expected | 3 + ...eprocessingDirectiveWithinMacroArgument.ql | 2 + .../test.c | 14 +++ .../MacroDefinedWithTheSameNameAsKeyword.ql | 26 +++++ ...tionLikeMacroArgsContainHashTokenCQuery.ql | 23 +++++ ...ndefUsedOnReservedIdentifierOrMacroName.ql | 37 ++++++++ ...roDefinedWithTheSameNameAsKeyword.expected | 2 + ...MacroDefinedWithTheSameNameAsKeyword.qlref | 1 + c/misra/test/rules/RULE-20-4/test.c | 4 + ...ikeMacroArgsContainHashTokenCQuery.testref | 1 + ...edOnReservedIdentifierOrMacroName.expected | 4 + ...fUsedOnReservedIdentifierOrMacroName.qlref | 1 + c/misra/test/rules/RULE-21-1/test.c | 7 ++ .../FunctionLikeMacroArgsContainHashToken.ql | 34 +------ ...tionLikeMacroArgsContainHashToken.expected | 3 - ...unctionLikeMacroArgsContainHashToken.qlref | 1 - ...ctionLikeMacroArgsContainHashToken.testref | 1 + .../cpp/exclusions/c/Preprocessor4.qll | 58 +++++++++++ .../cpp/exclusions/c/RuleMetadata.qll | 3 + ...processingDirectiveWithinMacroArgument.qll | 44 +++++++++ ...ssingDirectiveWithinMacroArgument.expected | 3 + ...eprocessingDirectiveWithinMacroArgument.ql | 2 + .../test.cpp | 0 rule_packages/c/Preprocessor4.json | 67 +++++++++++++ rule_packages/cpp/Macros.json | 1 + rules.csv | 8 +- 28 files changed, 411 insertions(+), 38 deletions(-) create mode 100644 c/common/src/codingstandards/c/Keywords.qll create mode 100644 c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.expected create mode 100644 c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql create mode 100644 c/common/test/rules/preprocessingdirectivewithinmacroargument/test.c create mode 100644 c/misra/src/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.ql create mode 100644 c/misra/src/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.ql create mode 100644 c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql create mode 100644 c/misra/test/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.expected create mode 100644 c/misra/test/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.qlref create mode 100644 c/misra/test/rules/RULE-20-4/test.c create mode 100644 c/misra/test/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.testref create mode 100644 c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.expected create mode 100644 c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.qlref create mode 100644 c/misra/test/rules/RULE-21-1/test.c delete mode 100644 cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.expected delete mode 100644 cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.qlref create mode 100644 cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.testref create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll create mode 100644 cpp/common/src/codingstandards/cpp/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.qll create mode 100644 cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.expected create mode 100644 cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql rename cpp/{autosar/test/rules/M16-0-5 => common/test/rules/preprocessingdirectivewithinmacroargument}/test.cpp (100%) create mode 100644 rule_packages/c/Preprocessor4.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9de0bd3ae7..e510268498 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -221,9 +221,11 @@ "Pointers", "Preprocessor1", "Preprocessor2", + "Preprocessor3", + "Preprocessor4", "IntegerConversion", "Expressions", - "DeadCode" + "DeadCode", "VirtualFunctions" ] }, diff --git a/c/common/src/codingstandards/c/Keywords.qll b/c/common/src/codingstandards/c/Keywords.qll new file mode 100644 index 0000000000..076569a730 --- /dev/null +++ b/c/common/src/codingstandards/c/Keywords.qll @@ -0,0 +1,95 @@ +import cpp + +/** Module to reason about keywords in standards C90, C99 and C11. */ +module Keywords { + /** Holds if `s` is a keyword. */ + predicate isKeyword(string s) { + s = "auto" + or + s = "break" + or + s = "case" + or + s = "char" + or + s = "const" + or + s = "continue" + or + s = "default" + or + s = "do" + or + s = "double" + or + s = "else" + or + s = "enum" + or + s = "extern" + or + s = "float" + or + s = "for" + or + s = "goto" + or + s = "if" + or + s = "inline" + or + s = "int" + or + s = "long" + or + s = "register" + or + s = "restrict" + or + s = "return" + or + s = "short" + or + s = "signed" + or + s = "sizeof" + or + s = "static" + or + s = "struct" + or + s = "switch" + or + s = "typedef" + or + s = "union" + or + s = "unsigned" + or + s = "void" + or + s = "volatile" + or + s = "while" + or + s = "_Alignas" + or + s = "_Alignof" + or + s = "_Atomic" + or + s = "_Bool" + or + s = "_Complex" + or + s = "_Generic" + or + s = "_Imaginary" + or + s = "_Noreturn" + or + s = "_Static_assert" + or + s = "_Thread_local" + } +} diff --git a/c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.expected b/c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.expected new file mode 100644 index 0000000000..deb612db6f --- /dev/null +++ b/c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.expected @@ -0,0 +1,3 @@ +| test.c:5:3:11:3 | MACROFUNCTION(X) | Invocation of macro MACROFUNCTION includes a token "#else" that could be confused for an argument preprocessor directive. | +| test.c:5:3:11:3 | MACROFUNCTION(X) | Invocation of macro MACROFUNCTION includes a token "#endif" that could be confused for an argument preprocessor directive. | +| test.c:5:3:11:3 | MACROFUNCTION(X) | Invocation of macro MACROFUNCTION includes a token "#if NOTDEFINEDMACRO" that could be confused for an argument preprocessor directive. | diff --git a/c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql b/c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql new file mode 100644 index 0000000000..37ff4945f4 --- /dev/null +++ b/c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.preprocessingdirectivewithinmacroargument.PreprocessingDirectiveWithinMacroArgument diff --git a/c/common/test/rules/preprocessingdirectivewithinmacroargument/test.c b/c/common/test/rules/preprocessingdirectivewithinmacroargument/test.c new file mode 100644 index 0000000000..35aa934ff2 --- /dev/null +++ b/c/common/test/rules/preprocessingdirectivewithinmacroargument/test.c @@ -0,0 +1,14 @@ +#include +#define MACROFUNCTION(X) strlen(X) + +void f() { + MACROFUNCTION( +#if NOTDEFINEDMACRO // NON_COMPLIANT + "longstringtest!test!" +#else // NON_COMPLIANT + "shortstring" +#endif // NON_COMPLIANT + ); + + MACROFUNCTION("alright"); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/src/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.ql b/c/misra/src/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.ql new file mode 100644 index 0000000000..5ec14f4df1 --- /dev/null +++ b/c/misra/src/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.ql @@ -0,0 +1,26 @@ +/** + * @id c/misra/macro-defined-with-the-same-name-as-keyword + * @name RULE-20-4: A macro shall not be defined with the same name as a keyword + * @description Redefinition of keywords is confusing and in the case where the standard library is + * included where that keyword is defined, the redefinition will result in undefined + * behaviour. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-20-4 + * correctness + * readability + * maintainability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Keywords + +from Macro m, string name +where + not isExcluded(m, Preprocessor4Package::macroDefinedWithTheSameNameAsKeywordQuery()) and + m.hasName(name) and + Keywords::isKeyword(name) +select m, "Redefinition of keyword '" + name + "'." diff --git a/c/misra/src/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.ql b/c/misra/src/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.ql new file mode 100644 index 0000000000..3006b9ab15 --- /dev/null +++ b/c/misra/src/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/function-like-macro-args-contain-hash-token-c-query + * @name RULE-20-6: Tokens that look like a preprocessing directive shall not occur within a macro argument + * @description Arguments to a function-like macro shall not contain tokens that look like + * pre-processing directives or else behaviour after macro expansion is unpredictable. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-20-6 + * readability + * correctness + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.preprocessingdirectivewithinmacroargument.PreprocessingDirectiveWithinMacroArgument + +class FunctionLikeMacroArgsContainHashTokenCQueryQuery extends PreprocessingDirectiveWithinMacroArgumentSharedQuery { + FunctionLikeMacroArgsContainHashTokenCQueryQuery() { + this = Preprocessor4Package::functionLikeMacroArgsContainHashTokenCQueryQuery() + } +} diff --git a/c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql b/c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql new file mode 100644 index 0000000000..b37b5cb92e --- /dev/null +++ b/c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql @@ -0,0 +1,37 @@ +/** + * @id c/misra/define-and-undef-used-on-reserved-identifier-or-macro-name + * @name RULE-21-1: #define and #undef shall not be used on a reserved identifier or reserved macro name + * @description The use of #define and #undef on reserved identifiers or macro names can result in + * undefined behaviour. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-21-1 + * correctness + * readability + * maintainability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Naming + +from PreprocessorDirective p, string name +where + not isExcluded(p, Preprocessor4Package::defineAndUndefUsedOnReservedIdentifierOrMacroNameQuery()) and + ( + p.(Macro).hasName(name) + or + p.(PreprocessorUndef).getName() = name + ) and + ( + Naming::Cpp14::hasStandardLibraryMacroName(name) + or + Naming::Cpp14::hasStandardLibraryObjectName(name) + or + name.regexpMatch("_.*") + or + name = "defined" + ) +select p, "Reserved identifier '" + name + "' has been undefined or redefined." diff --git a/c/misra/test/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.expected b/c/misra/test/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.expected new file mode 100644 index 0000000000..de92be24bf --- /dev/null +++ b/c/misra/test/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.expected @@ -0,0 +1,2 @@ +| test.c:1:1:1:16 | #define int long | Redefinition of keyword 'int'. | +| test.c:2:1:2:30 | #define while (E) for (; (E);) | Redefinition of keyword 'while'. | diff --git a/c/misra/test/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.qlref b/c/misra/test/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.qlref new file mode 100644 index 0000000000..c43fbf1edc --- /dev/null +++ b/c/misra/test/rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.qlref @@ -0,0 +1 @@ +rules/RULE-20-4/MacroDefinedWithTheSameNameAsKeyword.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-20-4/test.c b/c/misra/test/rules/RULE-20-4/test.c new file mode 100644 index 0000000000..1638e665e4 --- /dev/null +++ b/c/misra/test/rules/RULE-20-4/test.c @@ -0,0 +1,4 @@ +#define int long // NON_COMPLIANT +#define while (E) for (; (E);) // NON_COMPLIANT +#define test(E) for (; (E);) // COMPLIANT +#define _Decimal128 long // COMPLIANT introduced in C23 \ No newline at end of file diff --git a/c/misra/test/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.testref b/c/misra/test/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.testref new file mode 100644 index 0000000000..4647ffbdb7 --- /dev/null +++ b/c/misra/test/rules/RULE-20-6/FunctionLikeMacroArgsContainHashTokenCQuery.testref @@ -0,0 +1 @@ +c/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.expected b/c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.expected new file mode 100644 index 0000000000..299626d6fc --- /dev/null +++ b/c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.expected @@ -0,0 +1,4 @@ +| test.c:1:1:1:17 | #define _NOT_OKAY | Reserved identifier '_NOT_OKAY' has been undefined or redefined. | +| test.c:2:1:2:16 | #undef _NOT_OKAY | Reserved identifier '_NOT_OKAY' has been undefined or redefined. | +| test.c:4:1:4:15 | #define defined | Reserved identifier 'defined' has been undefined or redefined. | +| test.c:5:1:5:13 | #define errno | Reserved identifier 'errno' has been undefined or redefined. | diff --git a/c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.qlref b/c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.qlref new file mode 100644 index 0000000000..853c3f031c --- /dev/null +++ b/c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.qlref @@ -0,0 +1 @@ +rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-1/test.c b/c/misra/test/rules/RULE-21-1/test.c new file mode 100644 index 0000000000..380679d84a --- /dev/null +++ b/c/misra/test/rules/RULE-21-1/test.c @@ -0,0 +1,7 @@ +#define _NOT_OKAY // NON_COMPLIANT +#undef _NOT_OKAY // NON_COMPLIANT + +#define defined // NON_COMPLIANT +#define errno // NON_COMPLIANT + +#define NDEBUG 1 // COMPLIANT \ No newline at end of file diff --git a/cpp/autosar/src/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.ql b/cpp/autosar/src/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.ql index 980f822e46..105ba04144 100644 --- a/cpp/autosar/src/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.ql +++ b/cpp/autosar/src/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.ql @@ -16,34 +16,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.preprocessingdirectivewithinmacroargument.PreprocessingDirectiveWithinMacroArgument -pragma[noinline] -predicate isMacroInvocationLocation(MacroInvocation mi, File f, int startChar, int endChar) { - mi.getActualLocation().charLoc(f, startChar, endChar) +class FunctionLikeMacroArgsContainHashTokenCQueryQuery extends PreprocessingDirectiveWithinMacroArgumentSharedQuery { + FunctionLikeMacroArgsContainHashTokenCQueryQuery() { + this = MacrosPackage::functionLikeMacroArgsContainHashTokenQuery() + } } - -pragma[noinline] -predicate isPreprocDirectiveLocation(PreprocessorDirective pd, File f, int startChar) { - pd.getLocation().charLoc(f, startChar, _) -} - -from MacroInvocation m, PreprocessorDirective p -where - not isExcluded(m, MacrosPackage::functionLikeMacroArgsContainHashTokenQuery()) and - // There is not sufficient information in the database for nested macro invocations, because - // the location of nested macros and preprocessor directives are all set to the location of the - // outermost macro invocation - not exists(m.getParentInvocation()) and - exists(File f, int startChar, int endChar | - isMacroInvocationLocation(m, f, startChar, endChar) and - exists(int lStart | isPreprocDirectiveLocation(p, f, lStart) | - // If the start location of the preprocessor directive is after the start of the macro - // invocation, and before the end, it must be within the macro invocation - // Note: it's critical to use startChar < lStart, not startChar <= lStart, because the - // latter will include preprocessor directives which occur in nested macro invocations - startChar < lStart and lStart < endChar - ) - ) -select m, - "Invocation of macro " + m.getMacroName() + - " includes a $@ that could be confused for an argument", p, "preprocessor directive" diff --git a/cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.expected b/cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.expected deleted file mode 100644 index 52082a8662..0000000000 --- a/cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.cpp:5:3:11:3 | MACROFUNCTION(X) | Invocation of macro MACROFUNCTION includes a $@ that could be confused for an argument | test.cpp:6:1:6:19 | #if NOTDEFINEDMACRO | preprocessor directive | -| test.cpp:5:3:11:3 | MACROFUNCTION(X) | Invocation of macro MACROFUNCTION includes a $@ that could be confused for an argument | test.cpp:8:1:8:5 | #else | preprocessor directive | -| test.cpp:5:3:11:3 | MACROFUNCTION(X) | Invocation of macro MACROFUNCTION includes a $@ that could be confused for an argument | test.cpp:10:1:10:6 | #endif | preprocessor directive | diff --git a/cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.qlref b/cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.qlref deleted file mode 100644 index a6c23607d2..0000000000 --- a/cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.testref b/cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.testref new file mode 100644 index 0000000000..1e15c636ee --- /dev/null +++ b/cpp/autosar/test/rules/M16-0-5/FunctionLikeMacroArgsContainHashToken.testref @@ -0,0 +1 @@ +cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll new file mode 100644 index 0000000000..94ffc1dc8d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll @@ -0,0 +1,58 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Preprocessor4Query = + TMacroDefinedWithTheSameNameAsKeywordQuery() or + TFunctionLikeMacroArgsContainHashTokenCQueryQuery() or + TDefineAndUndefUsedOnReservedIdentifierOrMacroNameQuery() + +predicate isPreprocessor4QueryMetadata(Query query, string queryId, string ruleId) { + query = + // `Query` instance for the `macroDefinedWithTheSameNameAsKeyword` query + Preprocessor4Package::macroDefinedWithTheSameNameAsKeywordQuery() and + queryId = + // `@id` for the `macroDefinedWithTheSameNameAsKeyword` query + "c/misra/macro-defined-with-the-same-name-as-keyword" and + ruleId = "RULE-20-4" + or + query = + // `Query` instance for the `functionLikeMacroArgsContainHashTokenCQuery` query + Preprocessor4Package::functionLikeMacroArgsContainHashTokenCQueryQuery() and + queryId = + // `@id` for the `functionLikeMacroArgsContainHashTokenCQuery` query + "c/misra/function-like-macro-args-contain-hash-token-c-query" and + ruleId = "RULE-20-6" + or + query = + // `Query` instance for the `defineAndUndefUsedOnReservedIdentifierOrMacroName` query + Preprocessor4Package::defineAndUndefUsedOnReservedIdentifierOrMacroNameQuery() and + queryId = + // `@id` for the `defineAndUndefUsedOnReservedIdentifierOrMacroName` query + "c/misra/define-and-undef-used-on-reserved-identifier-or-macro-name" and + ruleId = "RULE-21-1" +} + +module Preprocessor4Package { + Query macroDefinedWithTheSameNameAsKeywordQuery() { + //autogenerate `Query` type + result = + // `Query` type for `macroDefinedWithTheSameNameAsKeyword` query + TQueryC(TPreprocessor4PackageQuery(TMacroDefinedWithTheSameNameAsKeywordQuery())) + } + + Query functionLikeMacroArgsContainHashTokenCQueryQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionLikeMacroArgsContainHashTokenCQuery` query + TQueryC(TPreprocessor4PackageQuery(TFunctionLikeMacroArgsContainHashTokenCQueryQuery())) + } + + Query defineAndUndefUsedOnReservedIdentifierOrMacroNameQuery() { + //autogenerate `Query` type + result = + // `Query` type for `defineAndUndefUsedOnReservedIdentifierOrMacroName` query + TQueryC(TPreprocessor4PackageQuery(TDefineAndUndefUsedOnReservedIdentifierOrMacroNameQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index 321a86d7bd..6959619ecb 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -11,6 +11,7 @@ import Pointers1 import Preprocessor1 import Preprocessor2 import Preprocessor3 +import Preprocessor4 import SideEffects1 import SideEffects2 import Strings1 @@ -29,6 +30,7 @@ newtype TCQuery = TPreprocessor1PackageQuery(Preprocessor1Query q) or TPreprocessor2PackageQuery(Preprocessor2Query q) or TPreprocessor3PackageQuery(Preprocessor3Query q) or + TPreprocessor4PackageQuery(Preprocessor4Query q) or TSideEffects1PackageQuery(SideEffects1Query q) or TSideEffects2PackageQuery(SideEffects2Query q) or TStrings1PackageQuery(Strings1Query q) or @@ -47,6 +49,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId) { isPreprocessor1QueryMetadata(query, queryId, ruleId) or isPreprocessor2QueryMetadata(query, queryId, ruleId) or isPreprocessor3QueryMetadata(query, queryId, ruleId) or + isPreprocessor4QueryMetadata(query, queryId, ruleId) or isSideEffects1QueryMetadata(query, queryId, ruleId) or isSideEffects2QueryMetadata(query, queryId, ruleId) or isStrings1QueryMetadata(query, queryId, ruleId) or diff --git a/cpp/common/src/codingstandards/cpp/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.qll b/cpp/common/src/codingstandards/cpp/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.qll new file mode 100644 index 0000000000..c3ee2a4a1e --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.qll @@ -0,0 +1,44 @@ +/** + * Provides a library which includes a `problems` predicate for reporting.... + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class PreprocessingDirectiveWithinMacroArgumentSharedQuery extends Query { } + +Query getQuery() { result instanceof PreprocessingDirectiveWithinMacroArgumentSharedQuery } + +pragma[noinline] +predicate isMacroInvocationLocation(MacroInvocation mi, File f, int startChar, int endChar) { + mi.getActualLocation().charLoc(f, startChar, endChar) +} + +pragma[noinline] +predicate isPreprocDirectiveLocation(PreprocessorDirective pd, File f, int startChar) { + pd.getLocation().charLoc(f, startChar, _) +} + +query predicate problems(MacroInvocation m, string message) { + not isExcluded(m, getQuery()) and + exists(PreprocessorDirective p | + // There is not sufficient information in the database for nested macro invocations, because + // the location of nested macros and preprocessor directives are all set to the location of the + // outermost macro invocation + not exists(m.getParentInvocation()) and + exists(File f, int startChar, int endChar | + isMacroInvocationLocation(m, f, startChar, endChar) and + exists(int lStart | isPreprocDirectiveLocation(p, f, lStart) | + // If the start location of the preprocessor directive is after the start of the macro + // invocation, and before the end, it must be within the macro invocation + // Note: it's critical to use startChar < lStart, not startChar <= lStart, because the + // latter will include preprocessor directives which occur in nested macro invocations + startChar < lStart and lStart < endChar + ) + ) and + message = + "Invocation of macro " + m.getMacroName() + " includes a token \"" + p + + "\" that could be confused for an argument preprocessor directive." + ) +} diff --git a/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.expected b/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.expected new file mode 100644 index 0000000000..3673437e35 --- /dev/null +++ b/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.expected @@ -0,0 +1,3 @@ +| test.cpp:5:3:11:3 | MACROFUNCTION(X) | Invocation of macro MACROFUNCTION includes a token "#else" that could be confused for an argument preprocessor directive. | +| test.cpp:5:3:11:3 | MACROFUNCTION(X) | Invocation of macro MACROFUNCTION includes a token "#endif" that could be confused for an argument preprocessor directive. | +| test.cpp:5:3:11:3 | MACROFUNCTION(X) | Invocation of macro MACROFUNCTION includes a token "#if NOTDEFINEDMACRO" that could be confused for an argument preprocessor directive. | diff --git a/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql b/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql new file mode 100644 index 0000000000..37ff4945f4 --- /dev/null +++ b/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.preprocessingdirectivewithinmacroargument.PreprocessingDirectiveWithinMacroArgument diff --git a/cpp/autosar/test/rules/M16-0-5/test.cpp b/cpp/common/test/rules/preprocessingdirectivewithinmacroargument/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M16-0-5/test.cpp rename to cpp/common/test/rules/preprocessingdirectivewithinmacroargument/test.cpp diff --git a/rule_packages/c/Preprocessor4.json b/rule_packages/c/Preprocessor4.json new file mode 100644 index 0000000000..404909c479 --- /dev/null +++ b/rule_packages/c/Preprocessor4.json @@ -0,0 +1,67 @@ +{ + "MISRA-C-2012": { + "RULE-20-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Redefinition of keywords is confusing and in the case where the standard library is included where that keyword is defined, the redefinition will result in undefined behaviour.", + "kind": "problem", + "name": "A macro shall not be defined with the same name as a keyword", + "precision": "very-high", + "severity": "warning", + "short_name": "MacroDefinedWithTheSameNameAsKeyword", + "tags": [ + "correctness", + "readability", + "maintainability" + ] + } + ], + "title": "A macro shall not be defined with the same name as a keyword" + }, + "RULE-20-6": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Arguments to a function-like macro shall not contain tokens that look like pre-processing directives or else behaviour after macro expansion is unpredictable.", + "kind": "problem", + "name": "Tokens that look like a preprocessing directive shall not occur within a macro argument", + "precision": "very-high", + "severity": "warning", + "short_name": "FunctionLikeMacroArgsContainHashTokenCQuery", + "shared_implementation_short_name": "PreprocessingDirectiveWithinMacroArgument", + "tags": [ + "readability", + "correctness" + ] + } + ], + "title": "Tokens that look like a preprocessing directive shall not occur within a macro argument" + }, + "RULE-21-1": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The use of #define and #undef on reserved identifiers or macro names can result in undefined behaviour.", + "kind": "problem", + "name": "#define and #undef shall not be used on a reserved identifier or reserved macro name", + "precision": "high", + "severity": "warning", + "short_name": "DefineAndUndefUsedOnReservedIdentifierOrMacroName", + "tags": [ + "correctness", + "readability", + "maintainability" + ] + } + ], + "title": "#define and #undef shall not be used on a reserved identifier or reserved macro name" + } + } +} \ No newline at end of file diff --git a/rule_packages/cpp/Macros.json b/rule_packages/cpp/Macros.json index f6f53449a0..923c0d6988 100644 --- a/rule_packages/cpp/Macros.json +++ b/rule_packages/cpp/Macros.json @@ -163,6 +163,7 @@ "precision": "very-high", "severity": "warning", "short_name": "FunctionLikeMacroArgsContainHashToken", + "shared_implementation_short_name": "PreprocessingDirectiveWithinMacroArgument", "tags": [ "readability", "correctness" diff --git a/rules.csv b/rules.csv index 891cbefd8d..8fc6b709b8 100755 --- a/rules.csv +++ b/rules.csv @@ -585,7 +585,7 @@ c,CERT-C,POS51-C,OutOfScope,Rule,,,Avoid deadlock with POSIX threads by locking c,CERT-C,POS52-C,OutOfScope,Rule,,,Do not perform operations that can block while holding a POSIX lock,,,, c,CERT-C,POS53-C,OutOfScope,Rule,,,Do not use more than one mutex for concurrent waiting operations on a condition variable,,,, c,CERT-C,POS54-C,OutOfScope,Rule,,,Detect and handle POSIX library errors,,,, -c,CERT-C,PRE30-C,Yes,Rule,,,Do not create a universal character name through concatenation,,Preprocessor,Medium, +c,CERT-C,PRE30-C,No,Rule,,,Do not create a universal character name through concatenation,,,Medium, c,CERT-C,PRE31-C,Yes,Rule,,,Avoid side effects in arguments to unsafe macros,RULE-13-2,SideEffects,Medium, c,CERT-C,PRE32-C,Yes,Rule,,,Do not use preprocessor directives in invocations of function-like macros,,Preprocessor,Hard, c,CERT-C,SIG30-C,Yes,Rule,,,Call only asynchronous-safe functions within signal handlers,,Contracts,Medium, @@ -732,9 +732,9 @@ c,MISRA-C-2012,RULE-19-2,Yes,Advisory,,,The union keyword should not be used,A9- c,MISRA-C-2012,RULE-20-1,Yes,Advisory,,,#include directives should only be preceded by preprocessor directives or comments,M16-0-1,Preprocessor1,Import, c,MISRA-C-2012,RULE-20-2,Yes,Required,,,"The ', "" or \ characters and the /* or // character sequences shall not occur in a header file name",A16-2-1,Preprocessor1,Import, c,MISRA-C-2012,RULE-20-3,No,Required,,,"The #include directive shall be followed by either a or ""filename"" sequence",,,Easy,This is verified by the compiler -c,MISRA-C-2012,RULE-20-4,Yes,Required,,,A macro shall not be defined with the same name as a keyword,A17-0-1,Preprocessor,Medium, +c,MISRA-C-2012,RULE-20-4,Yes,Required,,,A macro shall not be defined with the same name as a keyword,A17-0-1,Preprocessor4,Medium, c,MISRA-C-2012,RULE-20-5,Yes,Advisory,,,#undef should not be used,,Preprocessor2,Easy, -c,MISRA-C-2012,RULE-20-6,Yes,Required,,,Tokens that look like a preprocessing directive shall not occur within a macro argument,M16-0-5,Preprocessor,Import, +c,MISRA-C-2012,RULE-20-6,Yes,Required,,,Tokens that look like a preprocessing directive shall not occur within a macro argument,M16-0-5,Preprocessor4,Import, c,MISRA-C-2012,RULE-20-7,Yes,Required,,,Expressions resulting from the expansion of macro parameters shall be enclosed in parentheses,M16-0-6,Preprocessor,Easy, c,MISRA-C-2012,RULE-20-8,Yes,Required,,,The controlling expression of a #if or #elif preprocessing directive shall evaluate to 0 or 1,,Preprocessor3,Hard, c,MISRA-C-2012,RULE-20-9,Yes,Required,,,All identifiers used in the controlling expression of #if or #elif preprocessing directives shall be #defined before evaluation,M16-0-7,Preprocessor1,Import, @@ -743,7 +743,7 @@ c,MISRA-C-2012,RULE-20-11,Yes,Required,,,A macro parameter immediately following c,MISRA-C-2012,RULE-20-12,Yes,Required,,,"A macro parameter used as an operand to the # or ## operators, which is itself subject to further macro replacement, shall only be used as an operand to these operators",,Preprocessor2,Medium, c,MISRA-C-2012,RULE-20-13,No,Required,,,A line whose first token is # shall be a valid preprocessing directive,M16-0-8,,,This is verified by the compiler in the cases where the token is not within an ifdef branch that's never taken c,MISRA-C-2012,RULE-20-14,No,Required,,,"All #else, #elif and #endif preprocessor directives shall reside in the same file as the #if, #ifdef or #ifndef directive to which they are related",M16-1-2,,,Compilers already prohibit this case -c,MISRA-C-2012,RULE-21-1,Yes,Required,,,#define and #undef shall not be used on a reserved identifier or reserved macro name,,,Hard, +c,MISRA-C-2012,RULE-21-1,Yes,Required,,,#define and #undef shall not be used on a reserved identifier or reserved macro name,,Preprocessor4,Hard, c,MISRA-C-2012,RULE-21-2,Yes,Required,,,A reserved identifier or reserved macro name shall not be declared,,Declarations,Hard, c,MISRA-C-2012,RULE-21-3,Yes,Required,,,The memory allocation and deallocation functions of shall not be used,,Banned,Medium, c,MISRA-C-2012,RULE-21-4,Yes,Required,,,The standard header file shall not be used ,,Banned,Easy,