From 834c46fe9bf32e9635a10d8deababdd1d3d1eae8 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 3 Nov 2026 18:32:11 +0100 Subject: [PATCH 1/4] Prepare MISRA C:2025 development and rule examples --- c/misra/src/codeql-suites/misra-c-2023.qls | 16 +++ .../src/codeql-suites/misra-c-advisory.qls | 3 +- c/misra/src/codeql-suites/misra-c-audit.qls | 2 +- c/misra/src/codeql-suites/misra-c-default.qls | 3 +- .../src/codeql-suites/misra-c-mandatory.qls | 3 +- .../src/codeql-suites/misra-c-required.qls | 3 +- c/misra/src/codeql-suites/misra-c-strict.qls | 2 +- .../c/misra/PlainNumericalTypes.qll | 108 ++++++++++++++++++ .../c/misra/ReservedMacroNames.qll | 22 ++++ .../c/misra/SinglePointOfExit.qll | 25 ++++ .../LanguageExtensionsShouldBeMinimized.ql | 25 ++++ ...PlainIntegerTypeUsedOverExplicitTypedef.ql | 22 ++++ ...ainNumericalTypeUsedOverExplicitTypedef.ql | 100 +--------------- .../LanguageExtensionsShouldNotBeUsed.ql | 1 + .../RULE-15-5/FunctionReturnCondition.ql | 27 +---- .../src/rules/RULE-15-5/SinglePointOfExit.ql | 22 ++++ .../RULE-17-3/FunctionDeclaredImplicitly.ql | 1 + .../ArrayFunctionArgumentNumberOfElements.ql | 1 + .../src/rules/RULE-17-6/UseOfArrayStatic.ql | 1 + .../src/rules/RULE-20-15/ReservedMacroName.ql | 22 ++++ ...ndefUsedOnReservedIdentifierOrMacroName.ql | 18 +-- .../DoNotDeclareAReservedIdentifier.ql | 1 + .../TerminationFunctionsOfStdlibhUsed.ql | 1 + .../RULE-5-10/ReservedIdentifierDeclared.ql | 25 ++++ .../rules/RULE-8-18/DefinitionsInHeader.ql | 26 +++++ ...anguageExtensionsShouldBeMinimized.testref | 1 + ...ntegerTypeUsedOverExplicitTypedef.expected | 17 +++ ...inIntegerTypeUsedOverExplicitTypedef.qlref | 1 + ...ericalTypeUsedOverExplicitTypedef.expected | 2 + c/misra/test/rules/DIR-4-6/test.c | 14 +-- .../test/rules/DIR-4-6/test_MISRA_C_2025.c | 2 + .../rules/RULE-15-5/SinglePointOfExit.testref | 1 + .../test/rules/RULE-15-5/coding-standards.xml | 10 ++ .../test/rules/RULE-15-5/coding-standards.yml | 3 + .../unconfigured/SinglePointOfExit.expected | 0 .../unconfigured/SinglePointOfExit.qlref | 1 + .../test/rules/RULE-15-5/unconfigured/test.c | 1 + .../FunctionDeclaredImplicitly.expected | 1 + c/misra/test/rules/RULE-17-3/test_c99.c | 8 ++ ...yFunctionArgumentNumberOfElements.expected | 2 + .../UseOfArrayStatic_MISRA_C_2012.expected | 1 + .../UseOfArrayStatic_MISRA_C_2012.qlref | 1 + .../test/rules/RULE-17-5/test_MISRA_C_2025.c | 12 ++ .../RULE-20-15/ReservedMacroName.testref | 1 + ...TerminationFunctionsOfStdlibhUsed.expected | 1 + c/misra/test/rules/RULE-21-8/assert.h | 8 ++ c/misra/test/rules/RULE-21-8/test.c | 13 +++ .../ReservedIdentifierDeclared.testref | 1 + .../RULE-8-18/DefinitionsInHeader.expected | 8 ++ .../rules/RULE-8-18/DefinitionsInHeader.qlref | 1 + c/misra/test/rules/RULE-8-18/sample.h | 23 ++++ c/misra/test/rules/RULE-8-18/test.c | 12 ++ .../2026-11-03-misra-c-2025-query-updates.md | 8 ++ .../cpp/exclusions/c/Declarations1.qll | 17 +++ .../cpp/exclusions/c/Declarations5.qll | 19 ++- .../cpp/exclusions/c/Language3.qll | 17 +++ .../cpp/exclusions/c/Preprocessor4.qll | 19 ++- .../cpp/exclusions/c/Statements5.qll | 19 ++- .../cpp/exclusions/c/Types1.qll | 19 ++- .../GuidelineRecategorizations.qll | 15 ++- .../DisappliedRecategorizations.expected | 3 + .../DisappliedRecategorizations.ql | 18 +++ docs/design/guideline_recategorization.md | 15 ++- docs/user_manual.md | 47 ++++++++ rule_packages/c/Banned.json | 3 +- rule_packages/c/Contracts6.json | 3 +- rule_packages/c/Contracts7.json | 9 ++ rule_packages/c/Declarations1.json | 31 ++++- rule_packages/c/Declarations5.json | 32 ++++++ rule_packages/c/Declarations6.json | 3 +- rule_packages/c/Language3.json | 29 ++++- rule_packages/c/Pointers1.json | 9 ++ rule_packages/c/Preprocessor4.json | 25 +++- rule_packages/c/Statements5.json | 25 +++- rule_packages/c/Static.json | 3 +- rule_packages/c/Types1.json | 24 +++- rules-misra-c-2025.csv | 26 +++++ rules.csv | 33 +++--- schemas/rule-package.schema.json | 12 +- scripts/PSCodingStandards/Config.ps1 | 2 +- .../PSCodingStandards/Get-TestDirectory.ps1 | 2 +- .../generate_rules/coding_standards_utils.py | 4 +- .../generate_rules/generate_package_files.py | 14 ++- .../matrix_testing/CreateMatrixTestReport.ps1 | 2 +- .../release/create_supported_rules_list.py | 2 +- scripts/release/generate_release_notes.py | 2 +- scripts/validate-amendments-csv.py | 4 +- scripts/verify_rule_package_consistency.py | 4 +- 88 files changed, 949 insertions(+), 196 deletions(-) create mode 100644 c/misra/src/codeql-suites/misra-c-2023.qls create mode 100644 c/misra/src/codingstandards/c/misra/PlainNumericalTypes.qll create mode 100644 c/misra/src/codingstandards/c/misra/ReservedMacroNames.qll create mode 100644 c/misra/src/codingstandards/c/misra/SinglePointOfExit.qll create mode 100644 c/misra/src/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.ql create mode 100644 c/misra/src/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.ql create mode 100644 c/misra/src/rules/RULE-15-5/SinglePointOfExit.ql create mode 100644 c/misra/src/rules/RULE-20-15/ReservedMacroName.ql create mode 100644 c/misra/src/rules/RULE-5-10/ReservedIdentifierDeclared.ql create mode 100644 c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql create mode 100644 c/misra/test/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.testref create mode 100644 c/misra/test/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.expected create mode 100644 c/misra/test/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.qlref create mode 100644 c/misra/test/rules/DIR-4-6/test_MISRA_C_2025.c create mode 100644 c/misra/test/rules/RULE-15-5/SinglePointOfExit.testref create mode 100644 c/misra/test/rules/RULE-15-5/coding-standards.xml create mode 100644 c/misra/test/rules/RULE-15-5/coding-standards.yml create mode 100644 c/misra/test/rules/RULE-15-5/unconfigured/SinglePointOfExit.expected create mode 100644 c/misra/test/rules/RULE-15-5/unconfigured/SinglePointOfExit.qlref create mode 100644 c/misra/test/rules/RULE-15-5/unconfigured/test.c create mode 100644 c/misra/test/rules/RULE-17-3/test_c99.c create mode 100644 c/misra/test/rules/RULE-17-5/UseOfArrayStatic_MISRA_C_2012.expected create mode 100644 c/misra/test/rules/RULE-17-5/UseOfArrayStatic_MISRA_C_2012.qlref create mode 100644 c/misra/test/rules/RULE-17-5/test_MISRA_C_2025.c create mode 100644 c/misra/test/rules/RULE-20-15/ReservedMacroName.testref create mode 100644 c/misra/test/rules/RULE-21-8/assert.h create mode 100644 c/misra/test/rules/RULE-5-10/ReservedIdentifierDeclared.testref create mode 100644 c/misra/test/rules/RULE-8-18/DefinitionsInHeader.expected create mode 100644 c/misra/test/rules/RULE-8-18/DefinitionsInHeader.qlref create mode 100644 c/misra/test/rules/RULE-8-18/sample.h create mode 100644 c/misra/test/rules/RULE-8-18/test.c create mode 100644 change_notes/2026-11-03-misra-c-2025-query-updates.md create mode 100644 cpp/common/test/guideline_recategorizations/DisappliedRecategorizations.expected create mode 100644 cpp/common/test/guideline_recategorizations/DisappliedRecategorizations.ql create mode 100644 rules-misra-c-2025.csv diff --git a/c/misra/src/codeql-suites/misra-c-2023.qls b/c/misra/src/codeql-suites/misra-c-2023.qls new file mode 100644 index 0000000000..70891f4d38 --- /dev/null +++ b/c/misra/src/codeql-suites/misra-c-2023.qls @@ -0,0 +1,16 @@ +- description: MISRA C 2023 - Third Edition, Second Revision +- qlpack: codeql/misra-c-coding-standards +- include: + kind: + - problem + - path-problem + tags contain: + - external/misra/c/2012/third-edition-first-revision + - external/misra/c/2012/amendment2 + - external/misra/c/2012/amendment3 + - external/misra/c/2012/amendment4 +- exclude: + tags contain: + - external/misra/audit + - external/misra/strict + - external/misra/default-disabled \ No newline at end of file diff --git a/c/misra/src/codeql-suites/misra-c-advisory.qls b/c/misra/src/codeql-suites/misra-c-advisory.qls index 39df0d6583..e4df57bf31 100644 --- a/c/misra/src/codeql-suites/misra-c-advisory.qls +++ b/c/misra/src/codeql-suites/misra-c-advisory.qls @@ -1,4 +1,4 @@ -- description: MISRA C 2023 (Advisory) +- description: MISRA C 2025 (Advisory) - qlpack: codeql/misra-c-coding-standards - include: kind: @@ -10,3 +10,4 @@ tags contain: - external/misra/audit - external/misra/default-disabled + - external/misra/legacy diff --git a/c/misra/src/codeql-suites/misra-c-audit.qls b/c/misra/src/codeql-suites/misra-c-audit.qls index cac9df04ae..57542584b2 100644 --- a/c/misra/src/codeql-suites/misra-c-audit.qls +++ b/c/misra/src/codeql-suites/misra-c-audit.qls @@ -1,4 +1,4 @@ -- description: MISRA C 2023 (Audit) +- description: MISRA C 2025 (Audit) - qlpack: codeql/misra-c-coding-standards - include: kind: diff --git a/c/misra/src/codeql-suites/misra-c-default.qls b/c/misra/src/codeql-suites/misra-c-default.qls index cdc6eb65ad..34cf22f8cd 100644 --- a/c/misra/src/codeql-suites/misra-c-default.qls +++ b/c/misra/src/codeql-suites/misra-c-default.qls @@ -1,4 +1,4 @@ -- description: MISRA C 2023 (Default) +- description: MISRA C 2025 (Default) - qlpack: codeql/misra-c-coding-standards - include: kind: @@ -9,3 +9,4 @@ - external/misra/audit - external/misra/strict - external/misra/default-disabled + - external/misra/legacy diff --git a/c/misra/src/codeql-suites/misra-c-mandatory.qls b/c/misra/src/codeql-suites/misra-c-mandatory.qls index 09eccdc50c..47f05d24fb 100644 --- a/c/misra/src/codeql-suites/misra-c-mandatory.qls +++ b/c/misra/src/codeql-suites/misra-c-mandatory.qls @@ -1,4 +1,4 @@ -- description: MISRA C 2023 (Advisory) +- description: MISRA C 2025 (Mandatory) - qlpack: codeql/misra-c-coding-standards - include: kind: @@ -10,3 +10,4 @@ tags contain: - external/misra/audit - external/misra/default-disabled + - external/misra/legacy diff --git a/c/misra/src/codeql-suites/misra-c-required.qls b/c/misra/src/codeql-suites/misra-c-required.qls index f7c77e937a..a10b894e4f 100644 --- a/c/misra/src/codeql-suites/misra-c-required.qls +++ b/c/misra/src/codeql-suites/misra-c-required.qls @@ -1,4 +1,4 @@ -- description: MISRA C 2023 (Required) +- description: MISRA C 2025 (Required) - qlpack: codeql/misra-c-coding-standards - include: kind: @@ -10,3 +10,4 @@ tags contain: - external/misra/audit - external/misra/default-disabled + - external/misra/legacy diff --git a/c/misra/src/codeql-suites/misra-c-strict.qls b/c/misra/src/codeql-suites/misra-c-strict.qls index b8f4885189..e6e1de2592 100644 --- a/c/misra/src/codeql-suites/misra-c-strict.qls +++ b/c/misra/src/codeql-suites/misra-c-strict.qls @@ -1,4 +1,4 @@ -- description: MISRA C 2023 (Strict) +- description: MISRA C 2025 (Strict) - qlpack: codeql/misra-c-coding-standards - include: kind: diff --git a/c/misra/src/codingstandards/c/misra/PlainNumericalTypes.qll b/c/misra/src/codingstandards/c/misra/PlainNumericalTypes.qll new file mode 100644 index 0000000000..9c9a8f71db --- /dev/null +++ b/c/misra/src/codingstandards/c/misra/PlainNumericalTypes.qll @@ -0,0 +1,108 @@ +/** Provides the numeric-type detector shared by legacy and current directive wrappers. */ + +import cpp +import codingstandards.cpp.BuiltInNumericTypes + +/** A numeric type covered by the legacy directive. */ +private class NumericType extends BuiltInType { + NumericType() { + this.(CharType).isExplicitlySigned() + or + this.(CharType).isExplicitlyUnsigned() + or + this instanceof BuiltInIntegerType + or + this instanceof FloatType + or + this instanceof DoubleType + or + this instanceof LongDoubleType + or + this instanceof ComplexNumberType + } +} + +/** A typedef name that encodes numeric width and signedness. */ +private class SizedTypeName extends string { + string pattern; + int width; + + bindingset[this] + pragma[inline] + SizedTypeName() { + pattern = "(u?int|c?float)(4|8|16|32|64|128)_t" and + this.regexpMatch(pattern) and + width = this.regexpCapture(pattern, 2).toInt() + } + + /** Gets the width encoded by this name. */ + bindingset[this] + pragma[inline] + int getWidth() { result = width } + + /** Holds if this name denotes a complex type. */ + bindingset[this] + pragma[inline] + predicate isComplex() { this.charAt(0) = "c" } +} + +/** Holds if `type` has an invalid complex name, with `message` explaining why. */ +private predicate forbiddenComplexType(CTypedefType type, string message) { + type.getName().(SizedTypeName).isComplex() and + ( + if type.getBaseType().stripTopLevelSpecifiers() instanceof ComplexNumberType + then + type.getSize() * 8 != type.getName().(SizedTypeName).getWidth() * 2 and + message = "The typedef type " + type.getName() + " does not have its indicated real size." + else message = "The typedef type " + type.getName() + " is not a complex type." + ) +} + +/** Holds if `type` has an invalid real name, with `message` explaining why. */ +private predicate forbiddenRealType(CTypedefType type, string message) { + not type.getName().(SizedTypeName).isComplex() and + ( + if type.getBaseType().stripTopLevelSpecifiers() instanceof ComplexNumberType + then message = "The typedef name " + type.getName() + " does not indicate a complex type." + else ( + type.getSize() * 8 != type.getName().(SizedTypeName).getWidth() and + message = "The typedef type " + type.getName() + " does not have its indicated size." + ) + ) +} + +/** Holds if `type` is a forbidden alias, with `message` explaining why. */ +private predicate forbiddenAlias(CTypedefType type, string message) { + if type.getName() instanceof SizedTypeName + then forbiddenRealType(type, message) or forbiddenComplexType(type, message) + else ( + ( + type.getBaseType() instanceof NumericType + or + forbiddenAlias(type.getBaseType(), _) + ) and + message = "The type " + type.getName() + " is not an alias to a fixed-width numeric type." + ) +} + +/** Holds if `element` violates the legacy directive, with `message` explaining why. */ +predicate problems(Element element, string message) { + exists(Variable variable | variable = element | + variable.getType() instanceof NumericType and + not variable instanceof ExcludedVariable and + message = "The type " + variable.getType() + " is not a fixed-width numeric type." + ) + or + forbiddenAlias(element, message) +} + +/** Holds if `element` declares or aliases a real or complex floating-point type. */ +predicate hasFloatingType(Element element) { + exists(Type type | + type = element.(Variable).getType() or type = element.(CTypedefType).getBaseType() + | + type.stripType() instanceof FloatingPointType + or + type.stripType() instanceof ComplexNumberType + ) +} diff --git a/c/misra/src/codingstandards/c/misra/ReservedMacroNames.qll b/c/misra/src/codingstandards/c/misra/ReservedMacroNames.qll new file mode 100644 index 0000000000..312168bd06 --- /dev/null +++ b/c/misra/src/codingstandards/c/misra/ReservedMacroNames.qll @@ -0,0 +1,22 @@ +/** Provides the reserved-macro detector shared by MISRA C guideline identities. */ + +import cpp +import codingstandards.cpp.Naming + +/** Gets a reserved name defined or undefined by `directive`. */ +string getReservedMacroName(PreprocessorDirective directive) { + ( + directive.(Macro).hasName(result) + or + directive.(PreprocessorUndef).getName() = result + ) and + ( + Naming::Cpp14::hasStandardLibraryMacroName(result) + or + Naming::Cpp14::hasStandardLibraryObjectName(result) + or + result.regexpMatch("_.*") + or + result = "defined" + ) +} diff --git a/c/misra/src/codingstandards/c/misra/SinglePointOfExit.qll b/c/misra/src/codingstandards/c/misra/SinglePointOfExit.qll new file mode 100644 index 0000000000..25e199e5a6 --- /dev/null +++ b/c/misra/src/codingstandards/c/misra/SinglePointOfExit.qll @@ -0,0 +1,25 @@ +/** Provides the exit detector shared by Advisory and Disapplied query wrappers. */ + +import cpp + +/** A return statement explicitly written in source code. */ +private class UserReturn extends ReturnStmt { + UserReturn() { not this.isCompilerGenerated() } +} + +/** Holds if `function` has an offending `statement`, with `message` explaining why. */ +predicate problems(Function function, string message, ReturnStmt statement) { + function.hasDefinition() and + count(function.getBlock()) = 1 and + not function.isCompilerGenerated() and + statement instanceof UserReturn and + statement.getEnclosingFunction() = function and + ( + count(UserReturn other | other.getEnclosingFunction() = function) > 1 and + message = "Function has more than one $@." + or + count(UserReturn other | other.getEnclosingFunction() = function) = 1 and + not function.getBlock().getLastStmt() instanceof UserReturn and + message = "The $@ is not the last statement of the function." + ) +} diff --git a/c/misra/src/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.ql b/c/misra/src/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.ql new file mode 100644 index 0000000000..a56e477492 --- /dev/null +++ b/c/misra/src/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/language-extensions-should-be-minimized + * @name DIR-1-2: Language extensions should be minimized + * @description Language extensions are not portable to other compilers and their use should be + * minimized. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/dir-1-2 + * maintainability + * readability + * external/misra/c/2025 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.AlertReporting +import codingstandards.c.Extensions + +from CCompilerExtension extension +where + not isExcluded(MacroUnwrapper::unwrapElement(extension), + Language3Package::languageExtensionsShouldBeMinimizedQuery()) +select MacroUnwrapper::unwrapElement(extension), extension.getMessage() diff --git a/c/misra/src/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.ql b/c/misra/src/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.ql new file mode 100644 index 0000000000..9f0166088c --- /dev/null +++ b/c/misra/src/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/plain-integer-type-used-over-explicit-typedef + * @name DIR-4-6: Plain integer types should use explicit typedefs + * @description Using integer typedefs makes their size and signedness explicit. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/dir-4-6 + * external/misra/c/2025 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.PlainNumericalTypes + +from Element element, string message +where + not isExcluded(element, Types1Package::plainIntegerTypeUsedOverExplicitTypedefQuery()) and + problems(element, message) and + not hasFloatingType(element) +select element, message diff --git a/c/misra/src/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.ql b/c/misra/src/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.ql index 0e6c902441..9d16a2127d 100644 --- a/c/misra/src/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.ql +++ b/c/misra/src/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.ql @@ -8,110 +8,16 @@ * @problem.severity error * @tags external/misra/id/dir-4-6 * external/misra/c/2012/third-edition-first-revision + * external/misra/legacy * external/misra/obligation/advisory */ import cpp import codingstandards.c.misra -import codingstandards.cpp.BuiltInNumericTypes - -class BuiltInNumericType extends BuiltInType { - BuiltInNumericType() { - /* Exclude the plain char because it does not count as a numeric type */ - this.(CharType).isExplicitlySigned() - or - this.(CharType).isExplicitlyUnsigned() - or - this instanceof BuiltInIntegerType - or - this instanceof FloatType - or - this instanceof DoubleType - or - this instanceof LongDoubleType - or - this instanceof ComplexNumberType - } -} - -predicate forbiddenBuiltinNumericUsedInDecl(Variable var, string message) { - var.getType() instanceof BuiltInNumericType and - not var instanceof ExcludedVariable and - message = "The type " + var.getType() + " is not a fixed-width numeric type." -} - -class SizedTypeString extends string { - string pattern; - int size; - - bindingset[this] - pragma[inline] - SizedTypeString() { - pattern = "(u?int|c?float)(4|8|16|32|64|128)_t" and - this.regexpMatch(pattern) and - size = this.regexpCapture(pattern, 2).toInt() - } - - bindingset[this] - pragma[inline] - int getSize() { result = size } - - bindingset[this] - pragma[inline] - predicate isComplex() { this.charAt(0) = "c" } -} - -predicate forbiddenComplexType(CTypedefType typedef, string message) { - typedef.getName().(SizedTypeString).isComplex() and - ( - if typedef.getBaseType().stripTopLevelSpecifiers() instanceof ComplexNumberType - then - typedef.getSize() * 8 != typedef.getName().(SizedTypeString).getSize() * 2 and - message = "The typedef type " + typedef.getName() + " does not have its indicated real size." - else message = "The typedef type " + typedef.getName() + " is not a complex type." - ) -} - -predicate forbiddenRealType(CTypedefType typedef, string message) { - not typedef.getName().(SizedTypeString).isComplex() and - ( - if typedef.getBaseType().stripTopLevelSpecifiers() instanceof ComplexNumberType - then message = "The typedef name " + typedef.getName() + " does not indicate a complex type." - else ( - typedef.getSize() * 8 != typedef.getName().(SizedTypeString).getSize() and - message = "The typedef type " + typedef.getName() + " does not have its indicated size." - ) - ) -} - -predicate forbiddenTypedef(CTypedefType typedef, string message) { - /* If the typedef's name contains an explicit size */ - ( - if typedef.getName() instanceof SizedTypeString - then ( - forbiddenRealType(typedef, message) - or - forbiddenComplexType(typedef, message) - ) else ( - ( - // type def is to a built in numeric type - typedef.getBaseType() instanceof BuiltInNumericType and - // but does not include the size in the name - not typedef.getName() instanceof SizedTypeString - or - // this is a typedef to a forbidden type def - forbiddenTypedef(typedef.getBaseType(), _) - ) and - message = "The type " + typedef.getName() + " is not an alias to a fixed-width numeric type." - ) - ) -} +import codingstandards.c.misra.PlainNumericalTypes from Element elem, string message where not isExcluded(elem, Types1Package::plainNumericalTypeUsedOverExplicitTypedefQuery()) and - ( - forbiddenBuiltinNumericUsedInDecl(elem, message) or - forbiddenTypedef(elem, message) - ) + problems(elem, message) select elem, message diff --git a/c/misra/src/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.ql b/c/misra/src/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.ql index 34a0f40fb6..ce9316b118 100644 --- a/c/misra/src/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.ql +++ b/c/misra/src/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.ql @@ -9,6 +9,7 @@ * maintainability * readability * external/misra/c/2012/third-edition-first-revision + * external/misra/legacy * external/misra/obligation/advisory */ diff --git a/c/misra/src/rules/RULE-15-5/FunctionReturnCondition.ql b/c/misra/src/rules/RULE-15-5/FunctionReturnCondition.ql index 8e777d7332..62366c9c54 100644 --- a/c/misra/src/rules/RULE-15-5/FunctionReturnCondition.ql +++ b/c/misra/src/rules/RULE-15-5/FunctionReturnCondition.ql @@ -10,35 +10,16 @@ * readability * correctness * external/misra/c/2012/third-edition-first-revision + * external/misra/legacy * external/misra/obligation/advisory */ import cpp import codingstandards.c.misra +import codingstandards.c.misra.SinglePointOfExit -class UserWrittenReturnStmt extends ReturnStmt { - UserWrittenReturnStmt() { not this.isCompilerGenerated() } -} - -from Function func, string message, UserWrittenReturnStmt returnStmt +from Function func, string message, ReturnStmt returnStmt where not isExcluded(func, Statements5Package::functionReturnConditionQuery()) and - func.hasDefinition() and - // Ignore functions which have multiple bodies - count(func.getBlock()) = 1 and - // Ignore functions which are compiler generated - not func.isCompilerGenerated() and - // Report all the return statements in the function - returnStmt.getEnclosingFunction() = func and - ( - // There is more than one return statement - count(UserWrittenReturnStmt return | return.getEnclosingFunction() = func) > 1 and - message = "Function has more than one $@." - or - // There is exactly one return statement - count(UserWrittenReturnStmt return | return.getEnclosingFunction() = func) = 1 and - // But it is not the last statement in the function - not func.getBlock().getLastStmt() instanceof UserWrittenReturnStmt and - message = "The $@ is not the last statement of the function." - ) + problems(func, message, returnStmt) select func, message, returnStmt, "return statement" diff --git a/c/misra/src/rules/RULE-15-5/SinglePointOfExit.ql b/c/misra/src/rules/RULE-15-5/SinglePointOfExit.ql new file mode 100644 index 0000000000..37a7227b86 --- /dev/null +++ b/c/misra/src/rules/RULE-15-5/SinglePointOfExit.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/single-point-of-exit + * @name RULE-15-5: A function should have a single point of exit at the end + * @description A single exit can be enforced when this Disapplied guideline is recategorized. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-5 + * maintainability + * external/misra/c/2025 + * external/misra/obligation/disapplied + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.SinglePointOfExit + +from Function function, string message, ReturnStmt statement +where + not isExcluded(function, Statements5Package::singlePointOfExitQuery()) and + problems(function, message, statement) +select function, message, statement, "return statement" diff --git a/c/misra/src/rules/RULE-17-3/FunctionDeclaredImplicitly.ql b/c/misra/src/rules/RULE-17-3/FunctionDeclaredImplicitly.ql index af6c9bccad..4d2544e7e7 100644 --- a/c/misra/src/rules/RULE-17-3/FunctionDeclaredImplicitly.ql +++ b/c/misra/src/rules/RULE-17-3/FunctionDeclaredImplicitly.ql @@ -10,6 +10,7 @@ * correctness * readability * external/misra/c/2012/third-edition-first-revision + * external/misra/c/2025 * external/misra/obligation/mandatory */ diff --git a/c/misra/src/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.ql b/c/misra/src/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.ql index 279003f6ff..af9145c44a 100644 --- a/c/misra/src/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.ql +++ b/c/misra/src/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.ql @@ -9,6 +9,7 @@ * @tags external/misra/id/rule-17-5 * correctness * external/misra/c/2012/third-edition-first-revision + * external/misra/c/2025 * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-17-6/UseOfArrayStatic.ql b/c/misra/src/rules/RULE-17-6/UseOfArrayStatic.ql index 0a1232b6ad..5316749c3c 100644 --- a/c/misra/src/rules/RULE-17-6/UseOfArrayStatic.ql +++ b/c/misra/src/rules/RULE-17-6/UseOfArrayStatic.ql @@ -9,6 +9,7 @@ * @tags external/misra/id/rule-17-6 * correctness * external/misra/c/2012/third-edition-first-revision + * external/misra/legacy * external/misra/obligation/mandatory */ diff --git a/c/misra/src/rules/RULE-20-15/ReservedMacroName.ql b/c/misra/src/rules/RULE-20-15/ReservedMacroName.ql new file mode 100644 index 0000000000..4727829bad --- /dev/null +++ b/c/misra/src/rules/RULE-20-15/ReservedMacroName.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/reserved-macro-name + * @name RULE-20-15: Reserved identifiers shall not be defined or undefined + * @description Defining or undefining reserved names can cause undefined behavior. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-20-15 + * correctness + * external/misra/c/2025 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.ReservedMacroNames + +from PreprocessorDirective directive, string name +where + not isExcluded(directive, Preprocessor4Package::reservedMacroNameQuery()) and + name = getReservedMacroName(directive) +select directive, "Reserved identifier '" + name + "' has been undefined or redefined." diff --git a/c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql b/c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql index 86d8426df8..13b9758828 100644 --- a/c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql +++ b/c/misra/src/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.ql @@ -11,28 +11,16 @@ * readability * maintainability * external/misra/c/2012/third-edition-first-revision + * external/misra/legacy * external/misra/obligation/required */ import cpp import codingstandards.c.misra -import codingstandards.cpp.Naming +import codingstandards.c.misra.ReservedMacroNames 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" - ) + name = getReservedMacroName(p) select p, "Reserved identifier '" + name + "' has been undefined or redefined." diff --git a/c/misra/src/rules/RULE-21-2/DoNotDeclareAReservedIdentifier.ql b/c/misra/src/rules/RULE-21-2/DoNotDeclareAReservedIdentifier.ql index 80ad8386bc..1302c5f530 100644 --- a/c/misra/src/rules/RULE-21-2/DoNotDeclareAReservedIdentifier.ql +++ b/c/misra/src/rules/RULE-21-2/DoNotDeclareAReservedIdentifier.ql @@ -10,6 +10,7 @@ * maintainability * readability * external/misra/c/2012/third-edition-first-revision + * external/misra/legacy * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql b/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql index cbc7dd5a92..f2107c70bd 100644 --- a/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql +++ b/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql @@ -10,6 +10,7 @@ * correctness * security * external/misra/c/2012/third-edition-first-revision + * external/misra/c/2025 * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-5-10/ReservedIdentifierDeclared.ql b/c/misra/src/rules/RULE-5-10/ReservedIdentifierDeclared.ql new file mode 100644 index 0000000000..1014f2c4ca --- /dev/null +++ b/c/misra/src/rules/RULE-5-10/ReservedIdentifierDeclared.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/reserved-identifier-declared + * @name RULE-5-10: A reserved identifier or reserved macro name shall not be declared + * @description Declaring a reserved identifier can lead to undefined behaviour. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-5-10 + * correctness + * maintainability + * readability + * external/misra/c/2025 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.declaredareservedidentifier.DeclaredAReservedIdentifier + +/** A query that reports reserved identifier declarations for MISRA C:2025 Rule 5.10. */ +class ReservedIdentifierDeclaredQuery extends DeclaredAReservedIdentifierSharedQuery { + ReservedIdentifierDeclaredQuery() { + this = Declarations1Package::reservedIdentifierDeclaredQuery() + } +} diff --git a/c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql b/c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql new file mode 100644 index 0000000000..ba7a836110 --- /dev/null +++ b/c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql @@ -0,0 +1,26 @@ +/** + * @id c/misra/definitions-in-header + * @name RULE-8-18: There shall be no tentative definitions in a header file + * @description Tentative definitions in headers can introduce unintended definitions in multiple + * translation units. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-8-18 + * correctness + * external/misra/c/2025 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from VariableDeclarationEntry declaration, GlobalVariable variable +where + not isExcluded(declaration, Declarations5Package::definitionsInHeaderQuery()) and + declaration.getVariable() = variable and + not declaration.hasSpecifier("extern") and + not (declaration.isDefinition() and exists(variable.getInitializer())) and + exists(Include include | include.getIncludedFile() = declaration.getFile()) +select declaration, + "Tentative definition of '" + declaration.getName() + "' appears in a header file." diff --git a/c/misra/test/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.testref b/c/misra/test/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.testref new file mode 100644 index 0000000000..469b703471 --- /dev/null +++ b/c/misra/test/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.testref @@ -0,0 +1 @@ +c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.qlref \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.expected b/c/misra/test/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.expected new file mode 100644 index 0000000000..60ebfed824 --- /dev/null +++ b/c/misra/test/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.expected @@ -0,0 +1,17 @@ +| test.c:14:5:14:10 | int4_t | The typedef type int4_t does not have its indicated size. | +| test.c:16:5:16:11 | uint4_t | The typedef type uint4_t does not have its indicated size. | +| test.c:31:5:31:26 | _astronomical_number_t | The type _astronomical_number_t is not an alias to a fixed-width numeric type. | +| test.c:38:15:38:16 | c2 | The type signed char is not a fixed-width numeric type. | +| test.c:39:17:39:18 | c3 | The type unsigned char is not a fixed-width numeric type. | +| test.c:42:9:42:10 | s1 | The type short is not a fixed-width numeric type. | +| test.c:43:16:43:17 | s2 | The type signed short is not a fixed-width numeric type. | +| test.c:44:18:44:19 | s3 | The type unsigned short is not a fixed-width numeric type. | +| test.c:47:7:47:8 | i1 | The type int is not a fixed-width numeric type. | +| test.c:48:14:48:15 | i2 | The type signed int is not a fixed-width numeric type. | +| test.c:49:16:49:17 | i3 | The type unsigned int is not a fixed-width numeric type. | +| test.c:52:8:52:9 | l1 | The type long is not a fixed-width numeric type. | +| test.c:53:15:53:16 | l2 | The type signed long is not a fixed-width numeric type. | +| test.c:54:17:54:18 | l3 | The type unsigned long is not a fixed-width numeric type. | +| test.c:57:13:57:15 | ll1 | The type long long is not a fixed-width numeric type. | +| test.c:58:20:58:22 | ll2 | The type signed long long is not a fixed-width numeric type. | +| test.c:59:22:59:24 | ll3 | The type unsigned long long is not a fixed-width numeric type. | diff --git a/c/misra/test/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.qlref b/c/misra/test/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.qlref new file mode 100644 index 0000000000..800f28d78a --- /dev/null +++ b/c/misra/test/rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.qlref @@ -0,0 +1 @@ +rules/DIR-4-6/PlainIntegerTypeUsedOverExplicitTypedef.ql \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.expected b/c/misra/test/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.expected index 49e0b1c34c..ce7adba31e 100644 --- a/c/misra/test/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.expected +++ b/c/misra/test/rules/DIR-4-6/PlainNumericalTypeUsedOverExplicitTypedef.expected @@ -22,3 +22,5 @@ | test.c:65:10:65:11 | d1 | The type double is not a fixed-width numeric type. | | test.c:68:15:68:17 | ld1 | The type long double is not a fixed-width numeric type. | | test.c:71:18:71:20 | cf1 | The type _Complex float is not a fixed-width numeric type. | +| test_MISRA_C_2025.c:1:15:1:23 | real_type | The type real_type is not an alias to a fixed-width numeric type. | +| test_MISRA_C_2025.c:2:19:2:28 | real_alias | The type real_alias is not an alias to a fixed-width numeric type. | diff --git a/c/misra/test/rules/DIR-4-6/test.c b/c/misra/test/rules/DIR-4-6/test.c index 0fc79faa2e..3218f1ac3e 100644 --- a/c/misra/test/rules/DIR-4-6/test.c +++ b/c/misra/test/rules/DIR-4-6/test.c @@ -16,12 +16,12 @@ typedef unsigned long long uint4_t; // NON_COMPLIANT: typedef does not have its indicated size typedef float float32_t; // COMPLIANT: exception, typedefs are permitted -typedef double _Complex float64_t; // NON-COMPLIANT: not complex floating type +typedef double _Complex float64_t; // NON_COMPLIANT (MISRA-C-2012 only) typedef long double float128_t; // COMPLIANT: exception, typedefs are permitted -typedef float cfloat32_t; // NON-COMPLIANT: not a complex floating type +typedef float cfloat32_t; // NON_COMPLIANT (MISRA-C-2012 only) typedef double _Complex cfloat64_t; // COMPLIANT: correct complex floating type -typedef double _Complex cfloat128_t; // NON-COMPLIANT: incorrect complex size +typedef double _Complex cfloat128_t; // NON_COMPLIANT (MISRA-C-2012 only) typedef int8_t astronomical_number_t; // COMPLIANT: aliasing a fixed-width numeric typedef @@ -59,15 +59,15 @@ main(int argc, // COMPLIANT: exception, argc's type can be plain int unsigned long long ll3 = 1; // NON_COMPLIANT: use typedef uint64_t int64_t ll4 = 1; // COMPLIANT: typedef used instead - float f1 = 1; // NON_COMPLIANT: float is a basic numeric type, use a typedef + float f1 = 1; // NON_COMPLIANT (MISRA-C-2012 only) float32_t f2 = 1; // COMPLIANT: typedef used instead - double d1 = 1; // NON_COMPLIANT: int is a basic numeric type + double d1 = 1; // NON_COMPLIANT (MISRA-C-2012 only) float64_t d2 = 1; // COMPLIANT: typedef used instead - long double ld1 = 1; // NON_COMPLIANT: int is a basic numeric type + long double ld1 = 1; // NON_COMPLIANT (MISRA-C-2012 only) float128_t ld2 = 1; // COMPLIANT: typedef used instead - float _Complex cf1 = 1; // NON_COMPLIANT: complex basic numeric type + float _Complex cf1 = 1; // NON_COMPLIANT (MISRA-C-2012 only) cfloat64_t cf2 = 1; // COMPLIANT: typedef used instead } \ No newline at end of file diff --git a/c/misra/test/rules/DIR-4-6/test_MISRA_C_2025.c b/c/misra/test/rules/DIR-4-6/test_MISRA_C_2025.c new file mode 100644 index 0000000000..60a6904544 --- /dev/null +++ b/c/misra/test/rules/DIR-4-6/test_MISRA_C_2025.c @@ -0,0 +1,2 @@ +typedef float real_type; // NON_COMPLIANT (MISRA-C-2012 only) +typedef real_type real_alias; // NON_COMPLIANT (MISRA-C-2012 only) \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-5/SinglePointOfExit.testref b/c/misra/test/rules/RULE-15-5/SinglePointOfExit.testref new file mode 100644 index 0000000000..ce126e9b24 --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/SinglePointOfExit.testref @@ -0,0 +1 @@ +c/misra/test/rules/RULE-15-5/FunctionReturnCondition.qlref \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-5/coding-standards.xml b/c/misra/test/rules/RULE-15-5/coding-standards.xml new file mode 100644 index 0000000000..df018140ac --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/coding-standards.xml @@ -0,0 +1,10 @@ + + + + + + RULE-15-5 + required + + + \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-5/coding-standards.yml b/c/misra/test/rules/RULE-15-5/coding-standards.yml new file mode 100644 index 0000000000..0199585464 --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/coding-standards.yml @@ -0,0 +1,3 @@ +guideline-recategorizations: + - rule-id: "RULE-15-5" + category: "required" \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-5/unconfigured/SinglePointOfExit.expected b/c/misra/test/rules/RULE-15-5/unconfigured/SinglePointOfExit.expected new file mode 100644 index 0000000000..e69de29bb2 diff --git a/c/misra/test/rules/RULE-15-5/unconfigured/SinglePointOfExit.qlref b/c/misra/test/rules/RULE-15-5/unconfigured/SinglePointOfExit.qlref new file mode 100644 index 0000000000..42a4ec3314 --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/unconfigured/SinglePointOfExit.qlref @@ -0,0 +1 @@ +rules/RULE-15-5/SinglePointOfExit.ql diff --git a/c/misra/test/rules/RULE-15-5/unconfigured/test.c b/c/misra/test/rules/RULE-15-5/unconfigured/test.c new file mode 100644 index 0000000000..ed072576bc --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/unconfigured/test.c @@ -0,0 +1 @@ +#include "../test.c" \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.expected b/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.expected index 8b53c721bb..d393ce32e2 100644 --- a/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.expected +++ b/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.expected @@ -1,2 +1,3 @@ | test.c:4:1:4:2 | declaration of f2 | Function declaration is implicit. | | test.c:12:15:12:15 | declaration of f3 | Function declaration is implicit. | +| test_c99.c:7:3:7:3 | declaration of undeclared | Function declaration is implicit. | diff --git a/c/misra/test/rules/RULE-17-3/test_c99.c b/c/misra/test/rules/RULE-17-3/test_c99.c new file mode 100644 index 0000000000..4abc3b2726 --- /dev/null +++ b/c/misra/test/rules/RULE-17-3/test_c99.c @@ -0,0 +1,8 @@ +// semmle-extractor-options:--clang -std=c99 -nostdinc +// -Wno-implicit-function-declaration +void declared(void); + +void c99_calls(void) { + declared(); // COMPLIANT + undeclared(); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.expected b/c/misra/test/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.expected index 913f6f1c34..318fa7c7dc 100644 --- a/c/misra/test/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.expected +++ b/c/misra/test/rules/RULE-17-5/ArrayFunctionArgumentNumberOfElements.expected @@ -7,3 +7,5 @@ | test.c:61:6:61:8 | ar2 | The function argument does not have a sufficient number or elements declared in the $@. | test.c:1:13:1:14 | ar | parameter | | test.c:62:6:62:9 | ar2b | The function argument does not have a sufficient number or elements declared in the $@. | test.c:1:13:1:14 | ar | parameter | | test.c:63:6:63:9 | ar2p | The function argument does not have a sufficient number or elements declared in the $@. | test.c:1:13:1:14 | ar | parameter | +| test_MISRA_C_2025.c:9:19:9:23 | small | The function argument does not have a sufficient number or elements declared in the $@. | test_MISRA_C_2025.c:2:26:2:31 | values | parameter | +| test_MISRA_C_2025.c:11:21:11:25 | small | The function argument does not have a sufficient number or elements declared in the $@. | test_MISRA_C_2025.c:3:28:3:33 | values | parameter | diff --git a/c/misra/test/rules/RULE-17-5/UseOfArrayStatic_MISRA_C_2012.expected b/c/misra/test/rules/RULE-17-5/UseOfArrayStatic_MISRA_C_2012.expected new file mode 100644 index 0000000000..43a3ae4cd2 --- /dev/null +++ b/c/misra/test/rules/RULE-17-5/UseOfArrayStatic_MISRA_C_2012.expected @@ -0,0 +1 @@ +| test_MISRA_C_2025.c:2:26:2:31 | values | Parameter values is declared as an array type using the static keyword. | diff --git a/c/misra/test/rules/RULE-17-5/UseOfArrayStatic_MISRA_C_2012.qlref b/c/misra/test/rules/RULE-17-5/UseOfArrayStatic_MISRA_C_2012.qlref new file mode 100644 index 0000000000..ecb67b2dfb --- /dev/null +++ b/c/misra/test/rules/RULE-17-5/UseOfArrayStatic_MISRA_C_2012.qlref @@ -0,0 +1 @@ +rules/RULE-17-6/UseOfArrayStatic.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-5/test_MISRA_C_2025.c b/c/misra/test/rules/RULE-17-5/test_MISRA_C_2025.c new file mode 100644 index 0000000000..2ba48c52d4 --- /dev/null +++ b/c/misra/test/rules/RULE-17-5/test_MISRA_C_2025.c @@ -0,0 +1,12 @@ +// semmle-extractor-options:--clang -std=c11 -nostdinc +void static_contract(int values[static 3]); // NON_COMPLIANT (MISRA-C-2012 only) +void ordinary_contract(int values[3]); // COMPLIANT + +void array_calls(void) { + int enough[3] = {0, 0, 0}; // COMPLIANT + int small[2] = {0, 0}; // COMPLIANT + static_contract(enough); // COMPLIANT + static_contract(small); // NON_COMPLIANT + ordinary_contract(enough); // COMPLIANT + ordinary_contract(small); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-20-15/ReservedMacroName.testref b/c/misra/test/rules/RULE-20-15/ReservedMacroName.testref new file mode 100644 index 0000000000..20b9ecb927 --- /dev/null +++ b/c/misra/test/rules/RULE-20-15/ReservedMacroName.testref @@ -0,0 +1 @@ +c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.qlref \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected b/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected index 7dad54e9cf..8859615930 100644 --- a/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected +++ b/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected @@ -4,3 +4,4 @@ | test.c:14:3:14:9 | call to exit | Call to banned function exit. | | test.c:15:3:15:7 | call to abort | Call to banned function abort. | | test.c:16:3:16:14 | call to system | Call to banned function system. | +| test.c:23:3:23:19 | call to abort | Call to banned function abort. | diff --git a/c/misra/test/rules/RULE-21-8/assert.h b/c/misra/test/rules/RULE-21-8/assert.h new file mode 100644 index 0000000000..33eab34fcf --- /dev/null +++ b/c/misra/test/rules/RULE-21-8/assert.h @@ -0,0 +1,8 @@ +#include + +#undef assert +#ifdef NDEBUG +#define assert(value) ((void)0) +#else +#define assert(value) ((value) ? (void)0 : abort()) +#endif \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-8/test.c b/c/misra/test/rules/RULE-21-8/test.c index e5348d7d72..3e84945c50 100644 --- a/c/misra/test/rules/RULE-21-8/test.c +++ b/c/misra/test/rules/RULE-21-8/test.c @@ -16,3 +16,16 @@ void f2() { SYSTEM("ls"); // NON_COMPLIANT - twice, one for function and one for macro MULTIPLY(1, 2); // COMPLIANT } + +#include "assert.h" + +void enabled_assertions(int condition) { + assert(condition); // NON_COMPLIANT +} + +#define NDEBUG +#include "assert.h" + +void disabled_assertions(int condition) { + assert(condition); // COMPLIANT +} diff --git a/c/misra/test/rules/RULE-5-10/ReservedIdentifierDeclared.testref b/c/misra/test/rules/RULE-5-10/ReservedIdentifierDeclared.testref new file mode 100644 index 0000000000..32b1b02bf4 --- /dev/null +++ b/c/misra/test/rules/RULE-5-10/ReservedIdentifierDeclared.testref @@ -0,0 +1 @@ +c/common/test/rules/declaredareservedidentifier/DeclaredAReservedIdentifier.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-18/DefinitionsInHeader.expected b/c/misra/test/rules/RULE-8-18/DefinitionsInHeader.expected new file mode 100644 index 0000000000..1a50ff1da2 --- /dev/null +++ b/c/misra/test/rules/RULE-8-18/DefinitionsInHeader.expected @@ -0,0 +1,8 @@ +| sample.h:4:5:4:20 | definition of header_tentative | Tentative definition of 'header_tentative' appears in a header file. | +| sample.h:5:12:5:24 | definition of static_object | Tentative definition of 'static_object' appears in a header file. | +| sample.h:6:11:6:22 | definition of const_object | Tentative definition of 'const_object' appears in a header file. | +| sample.h:9:22:9:34 | definition of uninitialized | Tentative definition of 'uninitialized' appears in a header file. | +| sample.h:12:5:12:25 | declaration of initialized_elsewhere | Tentative definition of 'initialized_elsewhere' appears in a header file. | +| sample.h:13:5:13:22 | declaration of same_header_object | Tentative definition of 'same_header_object' appears in a header file. | +| sample.h:18:5:18:21 | definition of redeclared_object | Tentative definition of 'redeclared_object' appears in a header file. | +| sample.h:23:16:23:34 | definition of macro_header_object | Tentative definition of 'macro_header_object' appears in a header file. | diff --git a/c/misra/test/rules/RULE-8-18/DefinitionsInHeader.qlref b/c/misra/test/rules/RULE-8-18/DefinitionsInHeader.qlref new file mode 100644 index 0000000000..d43d319b90 --- /dev/null +++ b/c/misra/test/rules/RULE-8-18/DefinitionsInHeader.qlref @@ -0,0 +1 @@ +rules/RULE-8-18/DefinitionsInHeader.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-18/sample.h b/c/misra/test/rules/RULE-8-18/sample.h new file mode 100644 index 0000000000..ad82fef707 --- /dev/null +++ b/c/misra/test/rules/RULE-8-18/sample.h @@ -0,0 +1,23 @@ +// Only uninitialized file-scope objects without extern are tentative. +extern int external_declaration; // COMPLIANT +int initialized_in_header = 0; // COMPLIANT +int header_tentative; // NON_COMPLIANT +static int static_object; // NON_COMPLIANT +const int const_object; // NON_COMPLIANT + +// An initializer applies only to its own declarator. +int initialized = 1, uninitialized; // NON_COMPLIANT: uninitialized + +// Later definitions do not excuse earlier tentative declarations. +int initialized_elsewhere; // NON_COMPLIANT: initialized in test.c +int same_header_object; // NON_COMPLIANT +int same_header_object = 1; // COMPLIANT + +// Extern declarations around a tentative declaration remain compliant. +extern int redeclared_object; // COMPLIANT +int redeclared_object; // NON_COMPLIANT +extern int redeclared_object; // COMPLIANT + +// Macro-generated declarations follow their expansion file. +#define DECLARE_OBJECT(name) int name +DECLARE_OBJECT(macro_header_object); // NON_COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-18/test.c b/c/misra/test/rules/RULE-8-18/test.c new file mode 100644 index 0000000000..6c02841ac3 --- /dev/null +++ b/c/misra/test/rules/RULE-8-18/test.c @@ -0,0 +1,12 @@ +// semmle-extractor-options:--clang -std=c11 -nostdinc +#include "sample.h" + +// Source definitions are allowed, including completion of header declarations. +int initialized_elsewhere = 0; // COMPLIANT +int source_tentative; // COMPLIANT +DECLARE_OBJECT(macro_source_object); // COMPLIANT + +// Static locals are not file-scope tentative definitions. +void local_objects(void) { + static int static_local; // COMPLIANT +} \ No newline at end of file diff --git a/change_notes/2026-11-03-misra-c-2025-query-updates.md b/change_notes/2026-11-03-misra-c-2025-query-updates.md new file mode 100644 index 0000000000..40a85a50e5 --- /dev/null +++ b/change_notes/2026-11-03-misra-c-2025-query-updates.md @@ -0,0 +1,8 @@ +- Default MISRA C suites now target MISRA C:2025; support remains incomplete. + Explicit 2023 and 2012 suites retain their guideline sets and query IDs. +- `DIR-4-6` - `PlainIntegerTypeUsedOverExplicitTypedef.ql`: + - The 2025 query excludes floating-point declarations and aliases. +- `RULE-15-5` - `SinglePointOfExit.ql`: + - Disapplied by default; recategorization can reactivate the query. +- `RULE-17-6` - `UseOfArrayStatic.ql`: + - Excluded from 2025 suites; Rule 17.5 array-size contract checks remain enabled. \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations1.qll index c5ffaa56e3..e301018c12 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations1.qll @@ -4,6 +4,7 @@ import RuleMetadata import codingstandards.cpp.exclusions.RuleMetadata newtype Declarations1Query = + TReservedIdentifierDeclaredQuery() or TDeclareIdentifiersBeforeUsingThemQuery() or TDoNotDeclareOrDefineAReservedIdentifierQuery() or TDoNotDeclareAReservedIdentifierQuery() or @@ -12,6 +13,15 @@ newtype Declarations1Query = TMacroIdentifierNotDistinctFromParameterQuery() predicate isDeclarations1QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `reservedIdentifierDeclared` query + Declarations1Package::reservedIdentifierDeclaredQuery() and + queryId = + // `@id` for the `reservedIdentifierDeclared` query + "c/misra/reserved-identifier-declared" and + ruleId = "RULE-5-10" and + category = "required" + or query = // `Query` instance for the `declareIdentifiersBeforeUsingThem` query Declarations1Package::declareIdentifiersBeforeUsingThemQuery() and @@ -68,6 +78,13 @@ predicate isDeclarations1QueryMetadata(Query query, string queryId, string ruleI } module Declarations1Package { + Query reservedIdentifierDeclaredQuery() { + //autogenerate `Query` type + result = + // `Query` type for `reservedIdentifierDeclared` query + TQueryC(TDeclarations1PackageQuery(TReservedIdentifierDeclaredQuery())) + } + Query declareIdentifiersBeforeUsingThemQuery() { //autogenerate `Query` type result = diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations5.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations5.qll index b1fe8eacb6..63c5a5c349 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations5.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Declarations5.qll @@ -8,7 +8,8 @@ newtype Declarations5Query = TExternalObjectOrFunctionNotDeclaredInOneFileQuery() or TMissingStaticSpecifierFunctionRedeclarationCQuery() or TMissingStaticSpecifierObjectRedeclarationCQuery() or - TUnnecessaryExposedIdentifierDeclarationCQuery() + TUnnecessaryExposedIdentifierDeclarationCQuery() or + TDefinitionsInHeaderQuery() predicate isDeclarations5QueryMetadata(Query query, string queryId, string ruleId, string category) { query = @@ -55,6 +56,15 @@ predicate isDeclarations5QueryMetadata(Query query, string queryId, string ruleI "c/misra/unnecessary-exposed-identifier-declaration-c" and ruleId = "RULE-8-9" and category = "advisory" + or + query = + // `Query` instance for the `definitionsInHeader` query + Declarations5Package::definitionsInHeaderQuery() and + queryId = + // `@id` for the `definitionsInHeader` query + "c/misra/definitions-in-header" and + ruleId = "RULE-8-18" and + category = "required" } module Declarations5Package { @@ -92,4 +102,11 @@ module Declarations5Package { // `Query` type for `unnecessaryExposedIdentifierDeclarationC` query TQueryC(TDeclarations5PackageQuery(TUnnecessaryExposedIdentifierDeclarationCQuery())) } + + Query definitionsInHeaderQuery() { + //autogenerate `Query` type + result = + // `Query` type for `definitionsInHeader` query + TQueryC(TDeclarations5PackageQuery(TDefinitionsInHeaderQuery())) + } } diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Language3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Language3.qll index 836f8f7010..738cde84dc 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Language3.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Language3.qll @@ -4,10 +4,20 @@ import RuleMetadata import codingstandards.cpp.exclusions.RuleMetadata newtype Language3Query = + TLanguageExtensionsShouldBeMinimizedQuery() or TLanguageExtensionsShouldNotBeUsedQuery() or TOccurrenceOfUndefinedBehaviorQuery() predicate isLanguage3QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `languageExtensionsShouldBeMinimized` query + Language3Package::languageExtensionsShouldBeMinimizedQuery() and + queryId = + // `@id` for the `languageExtensionsShouldBeMinimized` query + "c/misra/language-extensions-should-be-minimized" and + ruleId = "DIR-1-2" and + category = "advisory" + or query = // `Query` instance for the `languageExtensionsShouldNotBeUsed` query Language3Package::languageExtensionsShouldNotBeUsedQuery() and @@ -28,6 +38,13 @@ predicate isLanguage3QueryMetadata(Query query, string queryId, string ruleId, s } module Language3Package { + Query languageExtensionsShouldBeMinimizedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `languageExtensionsShouldBeMinimized` query + TQueryC(TLanguage3PackageQuery(TLanguageExtensionsShouldBeMinimizedQuery())) + } + Query languageExtensionsShouldNotBeUsedQuery() { //autogenerate `Query` type result = diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll index 8b30501183..6863c743a4 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor4.qll @@ -6,7 +6,8 @@ import codingstandards.cpp.exclusions.RuleMetadata newtype Preprocessor4Query = TMacroDefinedWithTheSameNameAsKeywordQuery() or TFunctionLikeMacroArgsContainHashTokenCQueryQuery() or - TDefineAndUndefUsedOnReservedIdentifierOrMacroNameQuery() + TDefineAndUndefUsedOnReservedIdentifierOrMacroNameQuery() or + TReservedMacroNameQuery() predicate isPreprocessor4QueryMetadata(Query query, string queryId, string ruleId, string category) { query = @@ -35,6 +36,15 @@ predicate isPreprocessor4QueryMetadata(Query query, string queryId, string ruleI "c/misra/define-and-undef-used-on-reserved-identifier-or-macro-name" and ruleId = "RULE-21-1" and category = "required" + or + query = + // `Query` instance for the `reservedMacroName` query + Preprocessor4Package::reservedMacroNameQuery() and + queryId = + // `@id` for the `reservedMacroName` query + "c/misra/reserved-macro-name" and + ruleId = "RULE-20-15" and + category = "required" } module Preprocessor4Package { @@ -58,4 +68,11 @@ module Preprocessor4Package { // `Query` type for `defineAndUndefUsedOnReservedIdentifierOrMacroName` query TQueryC(TPreprocessor4PackageQuery(TDefineAndUndefUsedOnReservedIdentifierOrMacroNameQuery())) } + + Query reservedMacroNameQuery() { + //autogenerate `Query` type + result = + // `Query` type for `reservedMacroName` query + TQueryC(TPreprocessor4PackageQuery(TReservedMacroNameQuery())) + } } diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements5.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements5.qll index d8312d11d7..eb7d8234aa 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements5.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements5.qll @@ -6,7 +6,8 @@ import codingstandards.cpp.exclusions.RuleMetadata newtype Statements5Query = TControllingExprInvariantQuery() or TFunctionReturnConditionQuery() or - TNonVoidFunctionReturnConditionQuery() + TNonVoidFunctionReturnConditionQuery() or + TSinglePointOfExitQuery() predicate isStatements5QueryMetadata(Query query, string queryId, string ruleId, string category) { query = @@ -35,6 +36,15 @@ predicate isStatements5QueryMetadata(Query query, string queryId, string ruleId, "c/misra/non-void-function-return-condition" and ruleId = "RULE-17-4" and category = "mandatory" + or + query = + // `Query` instance for the `singlePointOfExit` query + Statements5Package::singlePointOfExitQuery() and + queryId = + // `@id` for the `singlePointOfExit` query + "c/misra/single-point-of-exit" and + ruleId = "RULE-15-5" and + category = "disapplied" } module Statements5Package { @@ -58,4 +68,11 @@ module Statements5Package { // `Query` type for `nonVoidFunctionReturnCondition` query TQueryC(TStatements5PackageQuery(TNonVoidFunctionReturnConditionQuery())) } + + Query singlePointOfExitQuery() { + //autogenerate `Query` type + result = + // `Query` type for `singlePointOfExit` query + TQueryC(TStatements5PackageQuery(TSinglePointOfExitQuery())) + } } diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Types1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Types1.qll index ab7333b4c0..d4ef1a2252 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Types1.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Types1.qll @@ -8,7 +8,8 @@ newtype Types1Query = TConvertingAPointerToIntegerOrIntegerToPointerQuery() or TPlainNumericalTypeUsedOverExplicitTypedefQuery() or TSizeofOperatorUsedOnArrayTypeParamQuery() or - TStringLiteralAssignedToNonConstCharQuery() + TStringLiteralAssignedToNonConstCharQuery() or + TPlainIntegerTypeUsedOverExplicitTypedefQuery() predicate isTypes1QueryMetadata(Query query, string queryId, string ruleId, string category) { query = @@ -55,6 +56,15 @@ predicate isTypes1QueryMetadata(Query query, string queryId, string ruleId, stri "c/misra/string-literal-assigned-to-non-const-char" and ruleId = "RULE-7-4" and category = "required" + or + query = + // `Query` instance for the `plainIntegerTypeUsedOverExplicitTypedef` query + Types1Package::plainIntegerTypeUsedOverExplicitTypedefQuery() and + queryId = + // `@id` for the `plainIntegerTypeUsedOverExplicitTypedef` query + "c/misra/plain-integer-type-used-over-explicit-typedef" and + ruleId = "DIR-4-6" and + category = "advisory" } module Types1Package { @@ -92,4 +102,11 @@ module Types1Package { // `Query` type for `stringLiteralAssignedToNonConstChar` query TQueryC(TTypes1PackageQuery(TStringLiteralAssignedToNonConstCharQuery())) } + + Query plainIntegerTypeUsedOverExplicitTypedefQuery() { + //autogenerate `Query` type + result = + // `Query` type for `plainIntegerTypeUsedOverExplicitTypedef` query + TQueryC(TTypes1PackageQuery(TPlainIntegerTypeUsedOverExplicitTypedefQuery())) + } } diff --git a/cpp/common/src/codingstandards/cpp/guideline_recategorizations/GuidelineRecategorizations.qll b/cpp/common/src/codingstandards/cpp/guideline_recategorizations/GuidelineRecategorizations.qll index ec5731f1bf..da2a8ac37d 100644 --- a/cpp/common/src/codingstandards/cpp/guideline_recategorizations/GuidelineRecategorizations.qll +++ b/cpp/common/src/codingstandards/cpp/guideline_recategorizations/GuidelineRecategorizations.qll @@ -31,13 +31,17 @@ class GuidelineRecategorization extends XmlElement { recategorization = getCategory() | result = TMandatory() and - category = ["advisory", "required"] and + category = ["disapplied", "advisory", "required"] and recategorization = "mandatory" or result = TRequired() and - category = "advisory" and + category = ["disapplied", "advisory"] and recategorization = "required" or + result = TAdvisory() and + category = "disapplied" and + recategorization = "advisory" + or result = TDisapplied() and category = "advisory" and recategorization = "disapplied" @@ -45,12 +49,15 @@ class GuidelineRecategorization extends XmlElement { } private predicate isValidRecategorization(string category, string recategorization) { - category = ["advisory", "required"] and + category = ["disapplied", "advisory", "required"] and recategorization = "mandatory" or - category = "advisory" and + category = ["disapplied", "advisory"] and recategorization = "required" or + category = "disapplied" and + recategorization = "advisory" + or category = "advisory" and recategorization = "disapplied" } diff --git a/cpp/common/test/guideline_recategorizations/DisappliedRecategorizations.expected b/cpp/common/test/guideline_recategorizations/DisappliedRecategorizations.expected new file mode 100644 index 0000000000..e1c7711eef --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/DisappliedRecategorizations.expected @@ -0,0 +1,3 @@ +| disapplied | advisory | advisory | 0 | +| disapplied | mandatory | mandatory | 0 | +| disapplied | required | required | 0 | diff --git a/cpp/common/test/guideline_recategorizations/DisappliedRecategorizations.ql b/cpp/common/test/guideline_recategorizations/DisappliedRecategorizations.ql new file mode 100644 index 0000000000..9ef74d8ef2 --- /dev/null +++ b/cpp/common/test/guideline_recategorizations/DisappliedRecategorizations.ql @@ -0,0 +1,18 @@ +/** Tests promotions from the Disapplied category using existing configuration fixtures. */ + +import cpp +import codingstandards.cpp.exclusions.RuleMetadata +import codingstandards.cpp.guideline_recategorizations.GuidelineRecategorizations + +/** A query with a disapplied default category for recategorization tests. */ +class DisappliedDefaultQuery extends Query { + DisappliedDefaultQuery() { getRuleId() = ["A0-1-1", "A10-4-1"] } + + override string getCategory() { result = "disapplied" } +} + +from GuidelineRecategorization recategorization, DisappliedDefaultQuery query +where recategorization.getQuery() = query +select query.getCategory(), recategorization.getCategory(), + recategorization.getEffectiveCategory().toString(), + count(string reason | reason = recategorization.getAnInvalidReason()) diff --git a/docs/design/guideline_recategorization.md b/docs/design/guideline_recategorization.md index e488cf88de..89c167e46c 100644 --- a/docs/design/guideline_recategorization.md +++ b/docs/design/guideline_recategorization.md @@ -35,11 +35,16 @@ Guideline recategorization is possible by means of a guideline recategorization The GRP defines the additional category Disapplied to be used for Advisory guidelines which are to be ignored. Any other category can be recategorized into stricter categories to ensure that a guideline adheres to the associated policy. The following table summarizes the possible recategorizations. -| Category | Recategorizations | -| --------- | ------------------------------- | -| Mandatory | | -| Required | Mandatory | -| Advisory | Disapplied, Required, Mandatory | +| Category | Recategorizations | +| ---------- | ------------------------------- | +| Mandatory | | +| Required | Mandatory | +| Advisory | Disapplied, Required, Mandatory | +| Disapplied | Advisory, Required, Mandatory | + +MISRA C:2025 can assign Disapplied as a guideline's default category. Such a query +remains selectable but produces no alerts until the existing configuration promotes +it to Advisory, Required or Mandatory. Other recategorizations are invalid, not applied, and reported to the user. diff --git a/docs/user_manual.md b/docs/user_manual.md index e6cd061763..54e08d8271 100644 --- a/docs/user_manual.md +++ b/docs/user_manual.md @@ -71,6 +71,7 @@ The _CodeQL Coding Standards_ product is a set of CodeQL queries for identifying | | [2012 Amendment 3](https://misra.org.uk/app/uploads/2021/06/MISRA-C-2012-AMD3.pdf) | 24 | 24 | 24 | Implemented | | | [2012 Amendment 4](https://misra.org.uk/app/uploads/2021/06/MISRA-C-2012-AMD4.pdf) | 22 | 22 | 21[^4] | Implemented | | | [2023 Third Edition, Second Revision](https://misra.org.uk/product/misra-c2023/) | 221 | 210 | 207[^5] | Implemented | +| | 2025 (default suite) | 223 | Under review | Under development | Under development | | MISRA C++ | [2023](https://misra.org.uk/product/misra-cpp2023/) | 179 | 176[^6] | 175[^7] | Implemented | Not all rules in these standards are amenable to static analysis by CodeQL - some rules require external or domain specific knowledge to validate, or refer to properties which are not present in our representation of the codebase under analysis. In addition, some rules are natively enforced by the supported compilers. As CodeQL requires that the program under analysis compiles, we are unable to implement queries for these rules, and doing so would be redundant. @@ -85,6 +86,11 @@ Each supported rule is implemented as one or more CodeQL queries, with each quer The datasheet _"CodeQL Coding Standards: supported rules"_, provided with each release, lists which rules are supported for that particular release, and the _scope of analysis_ for that rule. +The generic MISRA C query suites now select MISRA C:2025. The migration is under +development; selecting the 2025 default does not establish complete rule coverage +or extend the functional-safety qualification scope. Use the supported-rules list +for the selected release to determine its implemented coverage. + [^1]: AUTOSAR C++ versions R22-11, R21-11, R20-11, R19-11 and R19-03 are all identical as indicated in the document change history. [^2]: The unimplemented supportable AUTOSAR rules are `A7-1-8` and `A8-2-1`. These rules require additional support in the CodeQL CLI to ensure the required information is available in the CodeQL database to identify violations of these rules. [^3]: The unimplemented supportable MISRA C 2012 rules are `Rule 9.5`, `Rule 17.13`. `Rule 9.5` and `Rule 17.13` require additional support in the CodeQL CLI to ensure the required information is available in the CodeQL database to identify violations of these rules. Note: `Dir 4.14` is covered by the default CodeQL queries, which identify potential security vulnerabilities caused by not validating external input. @@ -271,6 +277,28 @@ If modifying the query suite, ensure that all Rules you expect to be covered by codeql resolve queries codeql/--coding-standards@:codeql-suites/.qls ``` +##### Selecting a MISRA C edition + +The default MISRA C suite and its generic obligation, audit and strict suites target +MISRA C:2025. The pack name remains `codeql/misra-c-coding-standards`. +Older guideline sets can be selected explicitly without downloading a different pack: + +```bash +codeql database analyze --format=sarifv2.1.0 --output=results.sarif codeql/misra-c-coding-standards@:codeql-suites/misra-c-2023.qls +``` + +For MISRA C:2012 including Amendment 2, select: + +```bash +codeql database analyze --format=sarifv2.1.0 --output=results.sarif codeql/misra-c-coding-standards@:codeql-suites/misra-c-2012-third-edition-with-amendment-2.qls +``` + +Older suites retain their guideline sets and legacy query identifiers; interpretations +may incorporate later clarifications. Current suites omit superseded legacy wrappers. +For example, the default uses Rule 20.15 instead of Rule 21.1 and does not select the +retired Rule 17.6 static-array-parameter prohibition. Rule 17.5 array contracts remain +checked. Run `codeql resolve queries` for the chosen suite to review its exact membership. + ##### Supported SARIF versions The only supported SARIF version for use in a functional safety environment is version 2.1.0. @@ -596,6 +624,12 @@ The following tables described the re-categorizations are permitted. | Mandatory | Permitted | | | | | Required | Permitted | Permitted | | | | Advisory | Permitted | Permitted | Permitted | Permitted | +| Disapplied | Permitted | Permitted | Permitted | | + +A guideline that is Disapplied by default, such as MISRA C:2025 Rule 15.5, reports no +violations unless it is recategorized. Its query remains in the default suite so it +can be reactivated as Advisory, Required or Mandatory. This does not change the +Advisory default for the legacy Rule 15.5 wrapper selected by older-edition suites. Each guideline re-categorization **must** be specified in the `guideline-recategorizations` section of a `coding-standards.yml` file that **must** be anywhere in the source repository. @@ -613,6 +647,19 @@ guideline-recategorizations: category: "mandatory" ``` +For example, reactivate Rule 15.5 in the 2025 default analysis with: + +```yaml +guideline-recategorizations: + - rule-id: "RULE-15-5" + category: "advisory" +``` + +Process this configuration during database creation using +`process_coding_standards_config.py`, as described under _Deviation records_, so the +query can apply it during evaluation. SARIF post-processing updates report metadata; +it cannot recover violations that were suppressed during query evaluation. + Application of the guideline re-categorization plan to the analysis results requires an additional post-processing step. The post-processing step is implemented by the Python script `path/to/codeql-coding-standards/scripts/guideline_recategorization/recategorize.py`. The script will update the `external//obligation/` tag for each query implementing a recategorized guideline such that `` is equal to the new category and diff --git a/rule_packages/c/Banned.json b/rule_packages/c/Banned.json index 265a41de51..636b07a566 100644 --- a/rule_packages/c/Banned.json +++ b/rule_packages/c/Banned.json @@ -285,7 +285,8 @@ "tags": [ "correctness", "security", - "external/misra/c/2012/third-edition-first-revision" + "external/misra/c/2012/third-edition-first-revision", + "external/misra/c/2025" ] }, { diff --git a/rule_packages/c/Contracts6.json b/rule_packages/c/Contracts6.json index d89617d6dc..247baa4f8f 100644 --- a/rule_packages/c/Contracts6.json +++ b/rule_packages/c/Contracts6.json @@ -43,7 +43,8 @@ "short_name": "ArrayFunctionArgumentNumberOfElements", "tags": [ "correctness", - "external/misra/c/2012/third-edition-first-revision" + "external/misra/c/2012/third-edition-first-revision", + "external/misra/c/2025" ] } ], diff --git a/rule_packages/c/Contracts7.json b/rule_packages/c/Contracts7.json index 8b9ac838ca..0aecbfa29e 100644 --- a/rule_packages/c/Contracts7.json +++ b/rule_packages/c/Contracts7.json @@ -1,4 +1,13 @@ { + "MISRA-C-2025": { + "RULE-19-3": { + "properties": { + "obligation": "required" + }, + "queries": [], + "title": "A union member shall not be read unless it has been previously set" + } + }, "CERT-C": { "MSC33-C": { "properties": { diff --git a/rule_packages/c/Declarations1.json b/rule_packages/c/Declarations1.json index dba6a07eeb..d112d79e22 100644 --- a/rule_packages/c/Declarations1.json +++ b/rule_packages/c/Declarations1.json @@ -1,4 +1,32 @@ { + "MISRA-C-2025": { + "RULE-5-10": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Declaring a reserved identifier can lead to undefined behaviour.", + "kind": "problem", + "name": "A reserved identifier or reserved macro name shall not be declared", + "precision": "very-high", + "severity": "warning", + "short_name": "ReservedIdentifierDeclared", + "shared_implementation_short_name": "DeclaredAReservedIdentifier", + "tags": [ + "correctness", + "maintainability", + "readability", + "external/misra/c/2025" + ], + "implementation_scope": { + "description": "This implementation reuses the reserved-identifier detector for MISRA C:2012 Rule 21.2. This renumbering does not extend its reserved-name coverage." + } + } + ], + "title": "A reserved identifier or reserved macro name shall not be declared" + } + }, "CERT-C": { "DCL31-C": { "properties": { @@ -80,7 +108,8 @@ "correctness", "maintainability", "readability", - "external/misra/c/2012/third-edition-first-revision" + "external/misra/c/2012/third-edition-first-revision", + "external/misra/legacy" ] } ], diff --git a/rule_packages/c/Declarations5.json b/rule_packages/c/Declarations5.json index 36591e575b..275f9a693d 100644 --- a/rule_packages/c/Declarations5.json +++ b/rule_packages/c/Declarations5.json @@ -101,5 +101,37 @@ ], "title": "An object should be defined at block scope if its identifier only appears in a single function" } + }, + "MISRA-C-2025": { + "RULE-8-18": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Tentative definitions in headers can introduce unintended definitions in multiple translation units.", + "kind": "problem", + "name": "There shall be no tentative definitions in a header file", + "precision": "high", + "severity": "warning", + "short_name": "DefinitionsInHeader", + "tags": [ + "correctness", + "external/misra/c/2025" + ], + "implementation_scope": { + "description": "Reports tentative file-scope declarations in included files of any extension. Excludes extern declarations and initialized definitions." + } + } + ], + "title": "There shall be no tentative definitions in a header file" + }, + "RULE-8-19": { + "properties": { + "obligation": "advisory" + }, + "queries": [], + "title": "There should be no external declarations in a source file" + } } } \ No newline at end of file diff --git a/rule_packages/c/Declarations6.json b/rule_packages/c/Declarations6.json index 198b4e8351..3e36eb61c5 100644 --- a/rule_packages/c/Declarations6.json +++ b/rule_packages/c/Declarations6.json @@ -15,7 +15,8 @@ "tags": [ "correctness", "readability", - "external/misra/c/2012/third-edition-first-revision" + "external/misra/c/2012/third-edition-first-revision", + "external/misra/c/2025" ] } ], diff --git a/rule_packages/c/Language3.json b/rule_packages/c/Language3.json index c19881e05c..4d4dddf893 100644 --- a/rule_packages/c/Language3.json +++ b/rule_packages/c/Language3.json @@ -1,4 +1,30 @@ { + "MISRA-C-2025": { + "DIR-1-2": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Language extensions are not portable to other compilers and their use should be minimized.", + "kind": "problem", + "name": "Language extensions should be minimized", + "precision": "high", + "severity": "error", + "short_name": "LanguageExtensionsShouldBeMinimized", + "tags": [ + "maintainability", + "readability", + "external/misra/c/2025" + ], + "implementation_scope": { + "description": "This implementation reuses the compiler-extension detector for MISRA C:2012 Rule 1.2. It reports detected extensions for review but does not assess their justification or provide comprehensive compiler-extension coverage." + } + } + ], + "title": "Language extensions should be minimized" + } + }, "MISRA-C-2012": { "RULE-1-2": { "properties": { @@ -15,7 +41,8 @@ "tags": [ "maintainability", "readability", - "external/misra/c/2012/third-edition-first-revision" + "external/misra/c/2012/third-edition-first-revision", + "external/misra/legacy" ], "implementation_scope": { "description": "This implementation attempts to cover a broad section of the compiler specific extensions documented in: https://clang.llvm.org/docs/LanguageExtensions.html and https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html but is not comprehensive. The following topics are addressed in this query: Builtin macros, Variable Attributes, Function Attributes, Statement Expressions, Non-Local Gotos, Conditionals, Extended Integer / Numeric Types, Zero Length Structures, Zero Length Arrays, Variable Length Arrays, Case Attributes, Alignment, __sync and __fetch builtins. Other topics listed in the extension references are not covered by this query." diff --git a/rule_packages/c/Pointers1.json b/rule_packages/c/Pointers1.json index 1b54fc1fb6..e038132826 100644 --- a/rule_packages/c/Pointers1.json +++ b/rule_packages/c/Pointers1.json @@ -1,4 +1,13 @@ { + "MISRA-C-2025": { + "RULE-11-11": { + "properties": { + "obligation": "required" + }, + "queries": [], + "title": "Pointers shall not be implicitly compared to NULL" + } + }, "MISRA-C-2012": { "RULE-11-1": { "properties": { diff --git a/rule_packages/c/Preprocessor4.json b/rule_packages/c/Preprocessor4.json index 608a23d974..e38496c317 100644 --- a/rule_packages/c/Preprocessor4.json +++ b/rule_packages/c/Preprocessor4.json @@ -60,11 +60,34 @@ "correctness", "readability", "maintainability", - "external/misra/c/2012/third-edition-first-revision" + "external/misra/c/2012/third-edition-first-revision", + "external/misra/legacy" ] } ], "title": "#define and #undef shall not be used on a reserved identifier or reserved macro name" } + }, + "MISRA-C-2025": { + "RULE-20-15": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Defining or undefining reserved names can cause undefined behavior.", + "kind": "problem", + "name": "Reserved identifiers shall not be defined or undefined", + "precision": "high", + "severity": "warning", + "short_name": "ReservedMacroName", + "tags": [ + "correctness", + "external/misra/c/2025" + ] + } + ], + "title": "Reserved identifiers shall not be defined or undefined" + } } } \ No newline at end of file diff --git a/rule_packages/c/Statements5.json b/rule_packages/c/Statements5.json index 03380f4897..9687265ed8 100644 --- a/rule_packages/c/Statements5.json +++ b/rule_packages/c/Statements5.json @@ -41,7 +41,8 @@ "maintainability", "readability", "correctness", - "external/misra/c/2012/third-edition-first-revision" + "external/misra/c/2012/third-edition-first-revision", + "external/misra/legacy" ] } ], @@ -70,5 +71,27 @@ ], "title": "All exit paths from a function with non-void return type shall have an explicit return statement with an expression" } + }, + "MISRA-C-2025": { + "RULE-15-5": { + "properties": { + "obligation": "disapplied" + }, + "queries": [ + { + "description": "A single exit can be enforced when this Disapplied guideline is recategorized.", + "kind": "problem", + "name": "A function should have a single point of exit at the end", + "precision": "very-high", + "severity": "recommendation", + "short_name": "SinglePointOfExit", + "tags": [ + "maintainability", + "external/misra/c/2025" + ] + } + ], + "title": "A function should have a single point of exit at the end" + } } } \ No newline at end of file diff --git a/rule_packages/c/Static.json b/rule_packages/c/Static.json index 2af2af402a..c47da749e1 100644 --- a/rule_packages/c/Static.json +++ b/rule_packages/c/Static.json @@ -14,7 +14,8 @@ "short_name": "UseOfArrayStatic", "tags": [ "correctness", - "external/misra/c/2012/third-edition-first-revision" + "external/misra/c/2012/third-edition-first-revision", + "external/misra/legacy" ], "implementation_scope": { "description": "The static keyword is associated with particular array types in our model. This means we can get false positives when two parameter use the same array type and size, but only one of which uses the `static` keyword." diff --git a/rule_packages/c/Types1.json b/rule_packages/c/Types1.json index bb451eba70..467077fb58 100644 --- a/rule_packages/c/Types1.json +++ b/rule_packages/c/Types1.json @@ -61,7 +61,8 @@ "severity": "error", "short_name": "PlainNumericalTypeUsedOverExplicitTypedef", "tags": [ - "external/misra/c/2012/third-edition-first-revision" + "external/misra/c/2012/third-edition-first-revision", + "external/misra/legacy" ] } ], @@ -105,5 +106,26 @@ ], "title": "A string literal shall not be assigned to an object unless the object's type is 'pointer to const-qualified char'" } + }, + "MISRA-C-2025": { + "DIR-4-6": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Using integer typedefs makes their size and signedness explicit.", + "kind": "problem", + "name": "Plain integer types should use explicit typedefs", + "precision": "high", + "severity": "error", + "short_name": "PlainIntegerTypeUsedOverExplicitTypedef", + "tags": [ + "external/misra/c/2025" + ] + } + ], + "title": "typedefs that indicate size and signedness should be used in place of the basic integer types" + } } } \ No newline at end of file diff --git a/rules-misra-c-2025.csv b/rules-misra-c-2025.csv new file mode 100644 index 0000000000..3224d73902 --- /dev/null +++ b/rules-misra-c-2025.csv @@ -0,0 +1,26 @@ +language,standard,amendment,rule_id,supportable,implementation_category,implemented,difficulty +c,MISRA-C-2025,Edition2025,DIR-1-2,Yes,Renumber,Yes,Easy +c,MISRA-C-2025,Edition2025,DIR-4-6,Yes,Refine,Yes,Medium +c,MISRA-C-2025,Edition2025,DIR-4-10,Yes,Expand,No,Medium +c,MISRA-C-2025,Edition2025,RULE-1-2,Yes,Renumber,Yes,Easy +c,MISRA-C-2025,Edition2025,RULE-5-10,Yes,Renumber,Yes,Easy +c,MISRA-C-2025,Edition2025,RULE-8-18,Yes,New,Yes,Medium +c,MISRA-C-2025,Edition2025,RULE-8-19,Yes,New,No,Medium +c,MISRA-C-2025,Edition2025,RULE-10-1,Yes,Refine,No,Hard +c,MISRA-C-2025,Edition2025,RULE-11-3,Yes,Refine,No,Medium +c,MISRA-C-2025,Edition2025,RULE-11-4,Yes,Merge,No,Hard +c,MISRA-C-2025,Edition2025,RULE-11-5,Yes,Refine,No,Medium +c,MISRA-C-2025,Edition2025,RULE-11-6,Yes,Refine,No,Medium +c,MISRA-C-2025,Edition2025,RULE-11-7,Yes,Merge,No,Easy +c,MISRA-C-2025,Edition2025,RULE-11-11,Yes,New,No,Medium +c,MISRA-C-2025,Edition2025,RULE-14-3,Yes,Refine,No,Medium +c,MISRA-C-2025,Edition2025,RULE-15-5,Yes,Disapply,Yes,Medium +c,MISRA-C-2025,Edition2025,RULE-17-3,Yes,Expand,Yes,Easy +c,MISRA-C-2025,Edition2025,RULE-17-5,Yes,Merge,Yes,Medium +c,MISRA-C-2025,Edition2025,RULE-17-6,Yes,Merge,Yes,Easy +c,MISRA-C-2025,Edition2025,RULE-19-3,Yes,New,No,Very Hard +c,MISRA-C-2025,Edition2025,RULE-20-7,Yes,Refine,No,Hard +c,MISRA-C-2025,Edition2025,RULE-20-15,Yes,Renumber,Yes,Easy +c,MISRA-C-2025,Edition2025,RULE-21-1,Yes,Renumber,Yes,Easy +c,MISRA-C-2025,Edition2025,RULE-21-2,Yes,Renumber,Yes,Easy +c,MISRA-C-2025,Edition2025,RULE-21-8,Yes,Clarification,Yes,Easy \ No newline at end of file diff --git a/rules.csv b/rules.csv index 7723a15674..a70ed48ae9 100644 --- a/rules.csv +++ b/rules.csv @@ -603,6 +603,7 @@ c,CERT-C,STR37-C,Yes,Rule,,,Arguments to character-handling functions must be re c,CERT-C,STR38-C,Yes,Rule,,,Do not confuse narrow and wide character strings and functions,,Strings3,Medium, c,CERT-C,WIN30-C,OutOfScope,Rule,,,Properly pair allocation and deallocation functions,DCL54-CPP,,Easy, c,MISRA-C-2012,DIR-1-1,No,Required,,,Any implementation-defined behaviour on which the output of the program depends shall be documented and understood,,,, +c,MISRA-C-2025,DIR-1-2,Yes,Advisory,,,Language extensions should be minimized,,Language3,Hard, c,MISRA-C-2012,DIR-2-1,No,Required,,,All source files shall compile without any compilation errors,A1-4-3,,Medium,"This should be checked via the compiler output, rather than CodeQL, which adds unnecessary steps." c,MISRA-C-2012,DIR-3-1,No,Required,,,All code shall be traceable to documented requirements,,,, c,MISRA-C-2012,DIR-4-1,No,Required,,,Run-time failures shall be minimized,,,, @@ -610,11 +611,11 @@ c,MISRA-C-2012,DIR-4-2,Yes,Advisory,,,All usage of assembly language should be d c,MISRA-C-2012,DIR-4-3,Yes,Required,,,Assembly language shall be encapsulated and isolated,,Language1,Medium, c,MISRA-C-2012,DIR-4-4,Yes,Advisory,,,Sections of code should not be commented out,A2-7-2,Syntax,Import, c,MISRA-C-2012,DIR-4-5,Yes,Advisory,,,Identifiers in the same name space with overlapping visibility should be typographically unambiguous,M2-10-1,Syntax,Easy, -c,MISRA-C-2012,DIR-4-6,Yes,Advisory,,,typedefs that indicate size and signedness should be used in place of the basic numerical types,,Types1,Hard, +c,MISRA-C-2025,DIR-4-6,Yes,Advisory,,,typedefs that indicate size and signedness should be used in place of the basic integer types,,Types1,Hard, c,MISRA-C-2012,DIR-4-7,Yes,Required,,,"If a function returns error information, then that error information shall be tested",M0-3-2,Contracts,Import, c,MISRA-C-2012,DIR-4-8,Yes,Advisory,,,"If a pointer to a structure or union is never dereferenced within a translation unit, then the implementation of the object should be hidden",,Pointers1,Medium, c,MISRA-C-2012,DIR-4-9,Yes,Advisory,,,A function should be used in preference to a function-like macro where they are interchangeable,,Preprocessor6,Medium,Audit -c,MISRA-C-2012,DIR-4-10,Yes,Required,,,Precautions shall be taken in order to prevent the contents of a header file being included more than once,M16-2-3,Preprocessor2,Medium, +c,MISRA-C-2025,DIR-4-10,Yes,Required,,,Precautions shall be taken in order to prevent the contents of a header file being included more than once,M16-2-3,Preprocessor2,Medium, c,MISRA-C-2012,DIR-4-11,Yes,Required,,,The validity of values passed to library functions shall be checked,,Contracts,Hard, c,MISRA-C-2012,DIR-4-12,Yes,Required,,,Dynamic memory allocation shall not be used,,Banned,Medium, c,MISRA-C-2012,DIR-4-13,No,Advisory,,,Functions which are designed to provide operations on a resource should be called in an appropriate sequence,,,,"Rule 22.1, 22.2 and 22.6 cover aspects of this rule. In other cases this is a design issue and needs to be checked manually." @@ -649,6 +650,7 @@ c,MISRA-C-2012,RULE-5-6,Yes,Required,,,A typedef name shall be a unique identifi c,MISRA-C-2012,RULE-5-7,Yes,Required,,,A tag name shall be a unique identifier,,Declarations3,Easy, c,MISRA-C-2012,RULE-5-8,Yes,Required,,,Identifiers that define objects or functions with external linkage shall be unique,,Declarations6,Easy, c,MISRA-C-2012,RULE-5-9,Yes,Advisory,,,Identifiers that define objects or functions with internal linkage should be unique,,Declarations6,Easy, +c,MISRA-C-2025,RULE-5-10,Yes,Required,,,A reserved identifier or reserved macro name shall not be declared,,Declarations1,Hard, c,MISRA-C-2012,RULE-6-1,Yes,Required,,,Bit-fields shall only be declared with an appropriate type,M9-6-4,BitfieldTypes,Medium, c,MISRA-C-2012,RULE-6-2,Yes,Required,,,Single-bit named bit fields shall not be of a signed type,M9-6-4,BitfieldTypes,Import, c,MISRA-C-2012,RULE-6-3,Yes,Required,,,A bit field shall not be declared as a member of a union,DCL39-C,BitfieldTypes2,Easy, @@ -675,6 +677,8 @@ c,MISRA-C-2012,RULE-8-14,Yes,Required,,,The restrict type qualifier shall not be c,MISRA-C-2012,RULE-8-15,Yes,Required,,,All declarations of an object with an explicit alignment specification shall specify the same alignment,,Alignment,Easy, c,MISRA-C-2012,RULE-8-16,Yes,Advisory,,,The alignment specification of zero should not appear in an object declaration,,Alignment,Easy, c,MISRA-C-2012,RULE-8-17,Yes,Advisory,,,At most one explicit alignment specifier should appear in an object declaration,,Alignment,Easy, +c,MISRA-C-2025,RULE-8-18,Yes,Required,,,There shall be no tentative definitions in a header file,,Declarations5,Medium, +c,MISRA-C-2025,RULE-8-19,Yes,Advisory,,,There should be no external declarations in a source file,,Declarations5,Medium, c,MISRA-C-2012,RULE-9-1,Yes,Mandatory,,,The value of an object with automatic storage duration shall not be read before it has been set,,InvalidMemory1,Import, c,MISRA-C-2012,RULE-9-2,Yes,Required,,,The initializer for an aggregate or union shall be enclosed in braces,,Memory1,Easy, c,MISRA-C-2012,RULE-9-3,Yes,Required,,,Arrays shall not be partially initialized,,Memory1,Medium, @@ -682,7 +686,7 @@ c,MISRA-C-2012,RULE-9-4,Yes,Required,,,An element of an object shall not be init c,MISRA-C-2012,RULE-9-5,No,Required,,,Where designated initializers are used to initialize an array object the size of the array shall be specified explicitly,,,Medium, c,MISRA-C-2012,RULE-9-6,Yes,Required,,,An initializer using chained designators shall not contain initializers without designators,,Declarations10,Hard, c,MISRA-C-2012,RULE-9-7,Yes,Mandatory,,,Atomic objects shall be appropriately initialized before being accessed,,Concurrency7,Hard, -c,MISRA-C-2012,RULE-10-1,Yes,Required,,,Operands shall not be of an inappropriate essential type,,EssentialTypes,Hard, +c,MISRA-C-2025,RULE-10-1,Yes,Required,,,Operands shall not be of an inappropriate essential type,,EssentialTypes,Hard, c,MISRA-C-2012,RULE-10-2,Yes,Required,,,Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations,,EssentialTypes,Medium, c,MISRA-C-2012,RULE-10-3,Yes,Required,,,The value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category,,EssentialTypes,Hard, c,MISRA-C-2012,RULE-10-4,Yes,Required,,,Both operands of an operator in which the usual arithmetic conversions are performed shall have the same essential type category,,EssentialTypes,Medium, @@ -692,14 +696,15 @@ c,MISRA-C-2012,RULE-10-7,Yes,Required,,,If a composite expression is used as one c,MISRA-C-2012,RULE-10-8,Yes,Required,,,The value of a composite expression shall not be cast to a different essential type category or a wider essential type,,EssentialTypes,Medium, c,MISRA-C-2012,RULE-11-1,Yes,Required,,,Conversions shall not be performed between a pointer to a function and any other type,M5-2-6,Pointers1,Import, c,MISRA-C-2012,RULE-11-2,Yes,Required,,,Conversions shall not be performed between a pointer to an incomplete type and any other type,,Pointers1,Easy, -c,MISRA-C-2012,RULE-11-3,Yes,Required,,,A cast shall not be performed between a pointer to object type and a pointer to a different object type,,Pointers1,Easy, -c,MISRA-C-2012,RULE-11-4,Yes,Advisory,,,A conversion should not be performed between a pointer to object and an integer type,M-2-9,Pointers1,Easy, -c,MISRA-C-2012,RULE-11-5,Yes,Advisory,,,A conversion should not be performed from pointer to void into pointer to object,,Pointers1,Easy, -c,MISRA-C-2012,RULE-11-6,Yes,Required,,,A cast shall not be performed between pointer to void and an arithmetic type,,Pointers1,Easy, +c,MISRA-C-2025,RULE-11-3,Yes,Required,,,A cast shall not be performed between a pointer to object type and a pointer to a different object type,,Pointers1,Medium, +c,MISRA-C-2025,RULE-11-4,Yes,Required,,,A conversion shall not be performed between a pointer to object and an arithmetic type,M-2-9,Pointers1,Hard, +c,MISRA-C-2025,RULE-11-5,Yes,Advisory,,,A conversion should not be performed from pointer to void into pointer to object,,Pointers1,Medium, +c,MISRA-C-2025,RULE-11-6,Yes,Required,,,A cast shall not be performed between pointer to void and an arithmetic type,,Pointers1,Medium, c,MISRA-C-2012,RULE-11-7,Yes,Required,,,A cast shall not be performed between pointer to object and a non- integer arithmetic type,,Pointers1,Easy, c,MISRA-C-2012,RULE-11-8,Yes,Required,,,A cast shall not remove any const or volatile qualification from the type pointed to by a pointer,,Pointers1,Easy, c,MISRA-C-2012,RULE-11-9,Yes,Required,,,The macro NULL shall be the only permitted form of integer null pointer constant,,Pointers1,Easy, c,MISRA-C-2012,RULE-11-10,Yes,Required,,,The _Atomic qualifier shall not be applied to the incomplete type void,,Declarations9,Easy, +c,MISRA-C-2025,RULE-11-11,Yes,Required,,,Pointers shall not be implicitly compared to NULL,,Pointers1,Medium, c,MISRA-C-2012,RULE-12-1,Yes,Advisory,,,The precedence of operators within expressions should be made explicit,,SideEffects1,Medium, c,MISRA-C-2012,RULE-12-2,Yes,Required,,,The right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the essential type of the left hand operand,,Contracts7,Medium, c,MISRA-C-2012,RULE-12-3,Yes,Advisory,,,The comma operator should not be used,M5-18-1,Banned,Import, @@ -714,13 +719,13 @@ c,MISRA-C-2012,RULE-13-5,Yes,Required,,,The right hand operand of a logical && o c,MISRA-C-2012,RULE-13-6,Yes,Required,,,The operand of the sizeof operator shall not contain any expressiosn which has potential side effects,M5-3-4,SideEffects1,Import, c,MISRA-C-2012,RULE-14-1,Yes,Required,,,A loop counter shall not have essentially floating type,FLP30-C A6-5-2,EssentialTypes,Hard, c,MISRA-C-2012,RULE-14-2,Yes,Required,,,A for loop shall be well-formed,M6-5-1...M6-5-6,Statements4,Medium, -c,MISRA-C-2012,RULE-14-3,Yes,Required,,,Controlling expressions shall not be invariant,,Statements5,Medium, +c,MISRA-C-2025,RULE-14-3,Yes,Required,,,Controlling expressions shall not be invariant,,Statements5,Medium, c,MISRA-C-2012,RULE-14-4,Yes,Required,,,The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type,A5-0-2,Statements4,Medium, c,MISRA-C-2012,RULE-15-1,Yes,Advisory,,,The goto statement should not be used,A6-6-1,Statements6,Import, c,MISRA-C-2012,RULE-15-2,Yes,Required,,,The goto statement shall jump to a label declared later in the same function,M6-6-2,Statements2,Import, c,MISRA-C-2012,RULE-15-3,Yes,Required,,,"Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement",M6-6-1,Statements2,Import, c,MISRA-C-2012,RULE-15-4,Yes,Advisory,,,There should be no more than one break or goto statement used to terminate any iteration statement,,Statements2,Medium, -c,MISRA-C-2012,RULE-15-5,Yes,Advisory,,,A function should have a single point of exit at the end,,Statements5,Medium, +c,MISRA-C-2025,RULE-15-5,Yes,Disapplied,,,A function should have a single point of exit at the end,,Statements5,Medium, c,MISRA-C-2012,RULE-15-6,Yes,Required,,,The body of an iteration-statement or a selection-statement shall be a compund-statement,M6-3-1,Statements3,Import, c,MISRA-C-2012,RULE-15-7,Yes,Required,,,All if else if constructs shall be terminated with an else statement,M6-4-2,Statements3,Import, c,MISRA-C-2012,RULE-16-1,Yes,Required,,,All switch statements shall be well-formed,M6-4-3,Statements3,Import, @@ -732,9 +737,9 @@ c,MISRA-C-2012,RULE-16-6,Yes,Required,,,Every switch statement shall have at lea c,MISRA-C-2012,RULE-16-7,Yes,Required,,,A switch-expression shall not have essentially Boolean type,M6-4-7,Statements2,Medium, c,MISRA-C-2012,RULE-17-1,Yes,Required,,,The features of shall not be used,,Banned,Easy, c,MISRA-C-2012,RULE-17-2,Yes,Required,,,"Functions shall not call themselves, either directly or indirectly",A7-5-2,Statements3,Import, -c,MISRA-C-2012,RULE-17-3,Yes,Mandatory,,,A function shall not be declared implicitly,,Declarations6,Medium, +c,MISRA-C-2025,RULE-17-3,Yes,Mandatory,,,A function shall not be declared implicitly,,Declarations6,Medium, c,MISRA-C-2012,RULE-17-4,Yes,Mandatory,,,All exit paths from a function with non-void return type shall have an explicit return statement with an expression,MSC52-CPP,Statements5,Medium, -c,MISRA-C-2012,RULE-17-5,Yes,Required,,,The function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elements,,Contracts6,Hard, +c,MISRA-C-2025,RULE-17-5,Yes,Required,,,The function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elements,,Contracts6,Hard, c,MISRA-C-2012,RULE-17-6,Yes,Mandatory,,,The declaration of an array parameter shall not contain the static keyword between the [ ],,Static,Easy, c,MISRA-C-2012,RULE-17-7,Yes,Required,,,The value returned by a function having non-void return type shall be used,A0-1-2,Contracts6,Easy, c,MISRA-C-2012,RULE-17-8,Yes,Advisory,,,A function parameter should not be modified,,SideEffects2,Medium, @@ -755,13 +760,14 @@ c,MISRA-C-2012,RULE-18-9,Yes,Required,,,An object with temporary lifetime shall c,MISRA-C-2012,RULE-18-10,Yes,Mandatory,,,Pointers to variably-modified array types shall not be used,,InvalidMemory3,Import, c,MISRA-C-2012,RULE-19-1,Yes,Mandatory,,,An object shall not be assigned or copied to an overlapping object,M0-2-1,Contracts7,Hard, c,MISRA-C-2012,RULE-19-2,Yes,Advisory,,,The union keyword should not be used,A9-5-1,Banned,Import, +c,MISRA-C-2025,RULE-19-3,Yes,Required,,,A union member shall not be read unless it has been previously set,,Contracts7,Very Hard, 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,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,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,Preprocessor5,Easy, +c,MISRA-C-2025,RULE-20-7,Yes,Required,,,Expressions resulting from the expansion of macro parameters shall be enclosed in parentheses,M16-0-6,Preprocessor5,Hard, 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, c,MISRA-C-2012,RULE-20-10,Yes,Advisory,,,The # and ## preprocessor operators should not be used,M16-3-2,Preprocessor1,Import, @@ -769,6 +775,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-2025,RULE-20-15,Yes,Required,,,#define and #undef shall not be used on a reserved identifier or reserved macro name,,Preprocessor4,Easy, 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,,Declarations1,Hard, c,MISRA-C-2012,RULE-21-3,Yes,Required,,,The memory allocation and deallocation functions of shall not be used,,Banned,Medium, @@ -776,7 +783,7 @@ c,MISRA-C-2012,RULE-21-4,Yes,Required,,,The standard header file shall not be us c,MISRA-C-2012,RULE-21-5,Yes,Required,,,The standard header file shall not be used ,,Banned,Easy, c,MISRA-C-2012,RULE-21-6,Yes,Required,,,The Standard Library input/output functions shall not be used,,Banned,Easy, c,MISRA-C-2012,RULE-21-7,Yes,Required,,,"The Standard Library functions atof, atoi, atol and atoll of shall not be used",,Banned,Easy, -c,MISRA-C-2012,RULE-21-8,Yes,Required,,,The Standard Library termination functions of shall not be used,ERR50-CPP,Banned,Easy, +c,MISRA-C-2025,RULE-21-8,Yes,Required,,,The Standard Library termination functions of shall not be used,ERR50-CPP,Banned,Easy, c,MISRA-C-2012,RULE-21-9,Yes,Required,,,The Standard Library functions bsearch and qsort of shall not be used,,Banned,Easy, c,MISRA-C-2012,RULE-21-10,Yes,Required,,,The Standard Library time and date functions shall not be used,,Banned,Easy, c,MISRA-C-2012,RULE-21-11,Yes,Advisory,,,The standard header file shall not be used,,Banned,Easy, diff --git a/schemas/rule-package.schema.json b/schemas/rule-package.schema.json index 78714e08f7..335015d700 100644 --- a/schemas/rule-package.schema.json +++ b/schemas/rule-package.schema.json @@ -179,7 +179,7 @@ "description": "Rules part of the MISRA C 2012 standard", "type": "object", "patternProperties": { - "^RULE-\\d+-\\d+": { + "^(RULE|DIR)-\\d+-\\d+": { "description": "A coding standard rule", "type": "object", "properties": { @@ -191,7 +191,8 @@ "enum": [ "required", "advisory", - "mandatory" + "mandatory", + "disapplied" ] } }, @@ -222,6 +223,9 @@ } }, "minProperties": 1 + }, + "MISRA-C-2025": { + "$ref": "#/allOf/3/properties/MISRA-C-2012" } } }, @@ -345,10 +349,14 @@ "scope/single-translation-unit", "scope/system", "external/misra/audit", + "external/misra/c/audit", "external/misra/c/2012/third-edition-first-revision", "external/misra/c/2012/amendment2", "external/misra/c/2012/amendment3", "external/misra/c/2012/amendment4", + "external/misra/c/2025", + "external/misra/default-disabled", + "external/misra/legacy", "external/misra/c/strict", "external/cert/severity/low", "external/cert/severity/medium", diff --git a/scripts/PSCodingStandards/Config.ps1 b/scripts/PSCodingStandards/Config.ps1 index 53605c89f3..d464206f97 100644 --- a/scripts/PSCodingStandards/Config.ps1 +++ b/scripts/PSCodingStandards/Config.ps1 @@ -1,2 +1,2 @@ -$AVAILABLE_SUITES = @("CERT-C++", "AUTOSAR", "MISRA-C-2012", "CERT-C", "MISRA-C++-2023") +$AVAILABLE_SUITES = @("CERT-C++", "AUTOSAR", "MISRA-C-2025", "MISRA-C-2012", "CERT-C", "MISRA-C++-2023") $AVAILABLE_LANGUAGES = @("c", "cpp") \ No newline at end of file diff --git a/scripts/PSCodingStandards/Get-TestDirectory.ps1 b/scripts/PSCodingStandards/Get-TestDirectory.ps1 index 154a49dabe..2d6cd4f9a2 100644 --- a/scripts/PSCodingStandards/Get-TestDirectory.ps1 +++ b/scripts/PSCodingStandards/Get-TestDirectory.ps1 @@ -24,7 +24,7 @@ function Get-TestDirectory { elseif ($RuleObject.__memberof_suite -eq "CERT-C") { $standardString = "cert" } - elseif ($RuleObject.__memberof_suite -eq "MISRA-C-2012") { + elseif ($RuleObject.__memberof_suite -in @("MISRA-C-2012", "MISRA-C-2025")) { $standardString = "misra" } elseif ($RuleObject.__memberof_suite -eq "MISRA-C++-2023") { diff --git a/scripts/generate_rules/coding_standards_utils.py b/scripts/generate_rules/coding_standards_utils.py index b0bcd48443..959134a9b0 100644 --- a/scripts/generate_rules/coding_standards_utils.py +++ b/scripts/generate_rules/coding_standards_utils.py @@ -79,6 +79,7 @@ def write_exclusion_template(template: Type[Template], args: Dict[str, str], pac def extract_metadata_from_query(rule_id, title, rule_category, q, rule_query_tags, language_name, ql_language_name, standard_name, standard_short_name, standard_metadata, anonymise): metadata = q.copy() + metadata["tags"] = list(q["tags"]) # Add rule properties to the query dict, ready for template generation metadata["standard_name"] = standard_name @@ -130,7 +131,8 @@ def write_query_help_file(help_dir: Type[Path], env: any, query: any, package_na # Add Markdown help file, partially overwriting elements that are automatically generated. help_path = help_dir.joinpath( query["short_name"] + ".md") - help_template = env.get_template(f"{standard_name.lower()}-help.md.template") + help_standard = "MISRA-C-2012" if standard_name == "MISRA-C-2025" else standard_name + help_template = env.get_template(f"{help_standard.lower()}-help.md.template") if not help_path.exists(): print( diff --git a/scripts/generate_rules/generate_package_files.py b/scripts/generate_rules/generate_package_files.py index 174d3365a5..919f371dbd 100644 --- a/scripts/generate_rules/generate_package_files.py +++ b/scripts/generate_rules/generate_package_files.py @@ -59,6 +59,10 @@ "standard_title" : "MISRA-C:2012 Guidelines for the use of the C language in critical systems", "standard_url" : "https://www.misra.org.uk/" }, + "MISRA-C-2025" : { + "standard_title" : "MISRA C:2025 Guidelines for the use of the C language in critical systems", + "standard_url" : "https://www.misra.org.uk/" + }, "MISRA-C++-2023" : { "standard_title" : "MISRA C++:2023 Guidelines for the use C++:17 in critical systems", "standard_url" : "https://misra.org.uk/product/misra-cpp2023/" @@ -66,7 +70,7 @@ } # The help files of these standards cannot be distributed in our repository. -external_help_file_standards = ["AUTOSAR", "MISRA-C-2012", "MISRA-C++-2023"] +external_help_file_standards = ["AUTOSAR", "MISRA-C-2025", "MISRA-C-2012", "MISRA-C++-2023"] # Mapping from the QL language to source file extension used to generate a help example file. ql_language_ext_mappings = { @@ -213,7 +217,10 @@ def write_shared_implementation(package_name, rule_id, query, language_name, ql_ repo_root)).replace("\\", "/")) def write_non_shared_testfiles(rule_id, query, language_name, query_path, test_src_dir, src_pack_dir): - # Add qlref test file + # A testref reuses existing coverage, so do not generate duplicate test files. + if test_src_dir.joinpath(query["short_name"] + ".testref").exists(): + return + # Add qlref test file print( rule_id + ": Writing out query test files to " + str(test_src_dir)) with open(test_src_dir.joinpath(query["short_name"] + ".qlref"), "w", newline="\n") as f: @@ -282,6 +289,9 @@ def generate_package_files(package_name: str) -> None: # Identify the source pack for this standard src_pack_dir = standard_dir.joinpath("src") for rule_id, rule_details in rules.items(): + # Skip planned rules to avoid creating empty source and test directories. + if not rule_details["queries"]: + continue # Identify and create the directories required for this rule rule_src_dir = src_pack_dir.joinpath("rules").joinpath(rule_id) rule_src_dir.mkdir(exist_ok=True, parents=True) diff --git a/scripts/matrix_testing/CreateMatrixTestReport.ps1 b/scripts/matrix_testing/CreateMatrixTestReport.ps1 index a8de5034ce..a370c48b61 100644 --- a/scripts/matrix_testing/CreateMatrixTestReport.ps1 +++ b/scripts/matrix_testing/CreateMatrixTestReport.ps1 @@ -149,7 +149,7 @@ param( # For a suite, the suites we support. Valid values are 'CERT-C++' and # 'AUTOSAR' and MISRA-C-2012, MISRA-C++-2023 and CERT-C [Parameter(Mandatory, ParameterSetName = 'Suite')] - [ValidateSet("CERT-C++", "AUTOSAR", "MISRA-C-2012", "CERT-C", "MISRA-C++-2023")] + [ValidateSet("CERT-C++", "AUTOSAR", "MISRA-C-2012", "MISRA-C-2025", "CERT-C", "MISRA-C++-2023")] [string] $SuiteName, diff --git a/scripts/release/create_supported_rules_list.py b/scripts/release/create_supported_rules_list.py index 6d8f3e0991..771626c684 100644 --- a/scripts/release/create_supported_rules_list.py +++ b/scripts/release/create_supported_rules_list.py @@ -27,7 +27,7 @@ repo_root = Path(__file__).parent.parent.parent -rules_covered = {"AUTOSAR" : {}, "CERT-C++" : {}, "MISRA-C-2012" : {}, "CERT-C" : {}, "MISRA-C++-2023" : {},} +rules_covered = {"AUTOSAR" : {}, "CERT-C++" : {}, "MISRA-C-2025" : {}, "MISRA-C-2012" : {}, "CERT-C" : {}, "MISRA-C++-2023" : {},} # Iterate over rule packages for language_name in ["cpp", "c"]: diff --git a/scripts/release/generate_release_notes.py b/scripts/release/generate_release_notes.py index 3852a5eeb7..32083e23c0 100644 --- a/scripts/release/generate_release_notes.py +++ b/scripts/release/generate_release_notes.py @@ -79,7 +79,7 @@ def transform_legacy_rule_path(p): diff_from_last_release = latest_release_commit.diff(head_commit) # Store a mapping from standard -> rules with new queries -> new queries for those rules -new_rules = {"AUTOSAR" : {}, "CERT-C++" : {}, "MISRA-C-2012" : {}, "CERT-C" : {}, "MISRA-C++-2023" : {}} +new_rules = {"AUTOSAR" : {}, "CERT-C++" : {}, "MISRA-C-2025" : {}, "MISRA-C-2012" : {}, "CERT-C" : {}, "MISRA-C++-2023" : {}} # Store the text of the newly added change notes change_notes = [] # Store the names of the rule packages with new queries diff --git a/scripts/validate-amendments-csv.py b/scripts/validate-amendments-csv.py index 9d83b7d0c9..b067244eba 100644 --- a/scripts/validate-amendments-csv.py +++ b/scripts/validate-amendments-csv.py @@ -93,7 +93,9 @@ rule = rules_from_csv[rule_id] - if rule["standard"] != standard: + if rule["standard"] != standard and not ( + rule["standard"] == "MISRA-C-2025" and standard == "MISRA-C-2012" + ): print(f"🟡 Invalid: {amendment_id} has a different standard than the {rule_id} in rules.csv") print(f" '{standard}' vs '{rule['standard']}'") failed = True diff --git a/scripts/verify_rule_package_consistency.py b/scripts/verify_rule_package_consistency.py index b9eaa5b934..acd1b7496e 100644 --- a/scripts/verify_rule_package_consistency.py +++ b/scripts/verify_rule_package_consistency.py @@ -101,11 +101,11 @@ f" - ERROR: Rule {rule_id} included in {package_name}.json but not marked as supportable in rules.csv.") failed = True for query in rule_details["queries"]: - if standard_name == "MISRA-C-2012" and not any(tag for tag in query["tags"] if tag.startswith("external/misra/c/2012/")): + if standard_name in {"MISRA-C-2025", "MISRA-C-2012"} and not any(tag for tag in query["tags"] if tag.startswith("external/misra/c/")): print( f' - ERROR: MISRA C 2012 query {query["short_name"]}.ql for Rule {rule_id} in {package_name}.json is missing a `external/misra/c/2012/...` tag.') failed = True - if not standard_name == "MISRA-C-2012" and any(tag for tag in query["tags"] if tag.startswith("external/misra/c/2012/")): + if standard_name not in {"MISRA-C-2025", "MISRA-C-2012"} and any(tag for tag in query["tags"] if tag.startswith("external/misra/c/")): print( f' - ERROR: {standard_name} query {query["short_name"]}.ql for Rule {rule_id} in {package_name}.json has a spurious `external/misra/c/2012/...` tag.') failed = True From 1831cc949a7f22b1dcd8d7f1512f3bb02caaf139 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 3 Nov 2026 19:08:17 +0100 Subject: [PATCH 2/4] Extract tentative definitions into a local query class Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../rules/RULE-8-18/DefinitionsInHeader.ql | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql b/c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql index ba7a836110..b87b5c5359 100644 --- a/c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql +++ b/c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql @@ -15,12 +15,25 @@ import cpp import codingstandards.c.misra -from VariableDeclarationEntry declaration, GlobalVariable variable +/** + * A tentative definition of a file-scope variable, for example: + * ```c + * int global_object; + * static int internal_object; + * ``` + */ +class TentativeDefinition extends VariableDeclarationEntry { + TentativeDefinition() { + this.getVariable() instanceof GlobalVariable and + not this.hasSpecifier("extern") and + // An initializer on another declaration does not make this declaration non-tentative. + not (this.isDefinition() and exists(this.getVariable().getInitializer())) + } +} + +from TentativeDefinition definition where - not isExcluded(declaration, Declarations5Package::definitionsInHeaderQuery()) and - declaration.getVariable() = variable and - not declaration.hasSpecifier("extern") and - not (declaration.isDefinition() and exists(variable.getInitializer())) and - exists(Include include | include.getIncludedFile() = declaration.getFile()) -select declaration, - "Tentative definition of '" + declaration.getName() + "' appears in a header file." + not isExcluded(definition, Declarations5Package::definitionsInHeaderQuery()) and + exists(Include include | include.getIncludedFile() = definition.getFile()) +select definition, + "Tentative definition of '" + definition.getName() + "' appears in a header file." From 3a9b8cbe5f7d0c4799d749be7b20df50aecc629d Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 3 Nov 2026 20:24:05 +0100 Subject: [PATCH 3/4] Address MISRA C:2025 review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../TerminationFunctionsOfStdlibhUsed.ql | 19 ++- .../rules/RULE-8-18/DefinitionsInHeader.ql | 33 ++++- ...anguageExtensionsShouldBeMinimized.testref | 2 +- ...nguageExtensionsShouldBeMinimized.expected | 51 ++++++++ .../LanguageExtensionsShouldBeMinimized.qlref | 1 + .../RULE-15-5/SinglePointOfExit.expected | 5 + .../rules/RULE-15-5/SinglePointOfExit.qlref | 1 + .../rules/RULE-15-5/SinglePointOfExit.testref | 1 - .../test/rules/RULE-15-5/unconfigured/test.c | 12 +- .../FunctionDeclaredImplicitly.expected | 6 +- c/misra/test/rules/RULE-17-3/test.c | 5 +- c/misra/test/rules/RULE-17-3/test_c99.c | 5 +- .../RULE-20-15/ReservedMacroName.testref | 2 +- .../RULE-21-1/ReservedMacroName.expected | 3 + .../rules/RULE-21-1/ReservedMacroName.qlref | 1 + ...TerminationFunctionsOfStdlibhUsed.expected | 2 +- c/misra/test/rules/RULE-21-8/assert.h | 8 -- c/misra/test/rules/RULE-21-8/test.c | 10 +- .../RULE-8-18/DefinitionsInHeader.expected | 3 + c/misra/test/rules/RULE-8-18/conditional.h | 11 ++ c/misra/test/rules/RULE-8-18/other.c | 5 + c/misra/test/rules/RULE-8-18/sample.h | 7 +- c/misra/test/rules/RULE-8-18/test.c | 1 + .../2026-11-03-misra-c-2025-query-updates.md | 8 +- .../PSCodingStandards/Get-TestDirectory.ps1 | 30 +---- scripts/PSCodingStandards/Get-TestFile.ps1 | 65 ++++++++++ scripts/PSCodingStandards/README.md | 17 +++ .../matrix_testing/CreateMatrixTestReport.ps1 | 17 +-- scripts/matrix_testing/Get-TestFile.Tests.ps1 | 43 +++++++ .../release/create_supported_rules_list.py | 21 +++- .../create_supported_rules_list_test.py | 116 ++++++++++++++++++ scripts/verify_rule_package_consistency.py | 19 ++- .../verify_rule_package_consistency_test.py | 53 ++++++++ 33 files changed, 507 insertions(+), 76 deletions(-) create mode 100644 c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldBeMinimized.expected create mode 100644 c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldBeMinimized.qlref create mode 100644 c/misra/test/rules/RULE-15-5/SinglePointOfExit.expected create mode 100644 c/misra/test/rules/RULE-15-5/SinglePointOfExit.qlref delete mode 100644 c/misra/test/rules/RULE-15-5/SinglePointOfExit.testref create mode 100644 c/misra/test/rules/RULE-21-1/ReservedMacroName.expected create mode 100644 c/misra/test/rules/RULE-21-1/ReservedMacroName.qlref delete mode 100644 c/misra/test/rules/RULE-21-8/assert.h create mode 100644 c/misra/test/rules/RULE-8-18/conditional.h create mode 100644 c/misra/test/rules/RULE-8-18/other.c create mode 100644 scripts/PSCodingStandards/Get-TestFile.ps1 create mode 100644 scripts/matrix_testing/Get-TestFile.Tests.ps1 create mode 100644 scripts/release/create_supported_rules_list_test.py create mode 100644 scripts/verify_rule_package_consistency_test.py diff --git a/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql b/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql index f2107c70bd..8b9fbf1b4d 100644 --- a/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql +++ b/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql @@ -16,7 +16,9 @@ import cpp import codingstandards.c.misra +import semmle.code.cpp.commons.Assertions +/** A Standard Library termination function prohibited by this rule. */ class BannedFunction extends Function { BannedFunction() { this.getName() = ["abort", "exit", "system"] and @@ -24,8 +26,19 @@ class BannedFunction extends Function { } } -from FunctionCall fc, BannedFunction f +from FunctionCall fc, Function f, string message where not isExcluded(fc, BannedPackage::terminationFunctionsOfStdlibhUsedQuery()) and - f = fc.getTarget() -select fc, "Call to banned function " + f.getName() + "." + f = fc.getTarget() and + ( + f instanceof BannedFunction + or + any(Options options).exits(f) and + exists(LibcAssert assertion | assertion.getAnExpandedElement() = fc) + ) and + ( + if f instanceof BannedFunction + then message = "Call to banned function " + f.getName() + "." + else message = "Enabled assertion calls non-returning function '" + f.getName() + "'." + ) +select fc, message diff --git a/c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql b/c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql index b87b5c5359..cd23a02cdb 100644 --- a/c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql +++ b/c/misra/src/rules/RULE-8-18/DefinitionsInHeader.ql @@ -15,6 +15,36 @@ import cpp import codingstandards.c.misra +/** + * An initialized definition of a variable, for example: + * ```c + * int global_object = 1; + * ``` + */ +class InitializedDefinition extends VariableDeclarationEntry { + InitializedDefinition() { + exists(Initializer initializer | + this.isDefinition() and + initializer = this.getVariable().getInitializer() and + ( + initializer.getLocation().subsumes(this.getLocation()) + or + // Initializers follow their own declarators, not definitions in other translation units. + this.getLocation().isBefore(initializer.getLocation(), _) and + not exists(VariableDeclarationEntry later | + later.getVariable() = this.getVariable() and + later.isDefinition() and + this.getLocation().isBefore(later.getLocation(), _) and + ( + later.getLocation().isBefore(initializer.getLocation(), _) or + initializer.getLocation().subsumes(later.getLocation()) + ) + ) + ) + ) + } +} + /** * A tentative definition of a file-scope variable, for example: * ```c @@ -26,8 +56,7 @@ class TentativeDefinition extends VariableDeclarationEntry { TentativeDefinition() { this.getVariable() instanceof GlobalVariable and not this.hasSpecifier("extern") and - // An initializer on another declaration does not make this declaration non-tentative. - not (this.isDefinition() and exists(this.getVariable().getInitializer())) + not this instanceof InitializedDefinition } } diff --git a/c/misra/test/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.testref b/c/misra/test/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.testref index 469b703471..9203fcac8b 100644 --- a/c/misra/test/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.testref +++ b/c/misra/test/rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.testref @@ -1 +1 @@ -c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldNotBeUsed.qlref \ No newline at end of file +c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldBeMinimized.qlref \ No newline at end of file diff --git a/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldBeMinimized.expected b/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldBeMinimized.expected new file mode 100644 index 0000000000..0e2cbb26ee --- /dev/null +++ b/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldBeMinimized.expected @@ -0,0 +1,51 @@ +| test.c:34:1:34:23 | #define A __BASE_FILE__ | Use of builtin macro '__BASE_FILE__' is a compiler extension and is not portable to other compilers. | +| test.c:35:1:35:23 | #define B __FILE_NAME__ | Use of builtin macro '__FILE_NAME__' is a compiler extension and is not portable to other compilers. | +| test.c:36:1:36:21 | #define C __COUNTER__ | Use of builtin macro '__COUNTER__' is a compiler extension and is not portable to other compilers. | +| test.c:37:1:37:27 | #define D __INCLUDE_LEVEL__ | Use of builtin macro '__INCLUDE_LEVEL__' is a compiler extension and is not portable to other compilers. | +| test.c:39:1:39:19 | #define F __clang__ | Use of builtin macro '__clang__' is a compiler extension and is not portable to other compilers. | +| test.c:40:1:40:25 | #define G __clang_major__ | Use of builtin macro '__clang_major__' is a compiler extension and is not portable to other compilers. | +| test.c:41:1:41:25 | #define H __clang_minor__ | Use of builtin macro '__clang_minor__' is a compiler extension and is not portable to other compilers. | +| test.c:42:1:42:30 | #define I __clang_patchlevel__ | Use of builtin macro '__clang_patchlevel__' is a compiler extension and is not portable to other compilers. | +| test.c:43:1:43:27 | #define J __clang_version__ | Use of builtin macro '__clang_version__' is a compiler extension and is not portable to other compilers. | +| test.c:44:1:44:36 | #define K __clang_literal_encoding__ | Use of builtin macro '__clang_literal_encoding__' is a compiler extension and is not portable to other compilers. | +| test.c:45:1:45:41 | #define L __clang_wide_literal_encoding__ | Use of builtin macro '__clang_wide_literal_encoding__' is a compiler extension and is not portable to other compilers. | +| test.c:53:33:53:43 | vector_size | Use of attribute 'vector_size' is a compiler extension and is not portable to other compilers. | +| test.c:54:33:54:47 | vector_size | Use of attribute 'vector_size' is a compiler extension and is not portable to other compilers. | +| test.c:55:37:55:51 | ext_vector_type | Use of attribute 'ext_vector_type' is a compiler extension and is not portable to other compilers. | +| test.c:56:37:56:51 | ext_vector_type | Use of attribute 'ext_vector_type' is a compiler extension and is not portable to other compilers. | +| test.c:61:3:69:4 | (statement expression) | Statement expressions are a compiler extension and are not portable to other compilers. | +| test.c:96:3:96:18 | call to __builtin_setjmp | Call to builtin function '__builtin_setjmp' is a compiler extension and is not portable to other compilers. | +| test.c:97:3:97:19 | call to __builtin_longjmp | Call to builtin function '__builtin_longjmp' is a compiler extension and is not portable to other compilers. | +| test.c:113:11:113:16 | ... ? ... : ... | Ternaries with omitted middle operands are a compiler extension and is not portable to other compilers. | +| test.c:124:12:124:12 | definition of a | 128-bit integers are a compiler extension and are not portable to other compilers. | +| test.c:128:17:128:17 | definition of a | Double-Word integers are a compiler extension and are not portable to other compilers. | +| test.c:165:8:165:15 | definition of contents | Zero length arrays are a compiler extension and are not portable to other compilers. | +| test.c:182:8:182:11 | gf19 | Empty structures are a compiler extension and are not portable to other compilers. | +| test.c:216:9:216:10 | definition of x1 | Zero length arrays are a compiler extension and are not portable to other compilers. | +| test.c:266:16:266:21 | access | Use of attribute 'access' is a compiler extension and is not portable to other compilers. | +| test.c:269:27:269:31 | alias | Use of attribute 'alias' is a compiler extension and is not portable to other compilers. | +| test.c:272:23:272:29 | aligned | Use of attribute 'aligned' is a compiler extension and is not portable to other compilers. | +| test.c:283:25:283:34 | deprecated | Use of attribute 'deprecated' is a compiler extension and is not portable to other compilers. | +| test.c:295:20:295:30 | fallthrough | Use of attribute 'fallthrough' is a compiler extension and is not portable to other compilers. | +| test.c:319:3:319:22 | alignof() | '__alignof__' is a compiler extension and is not portable to other compilers. | +| test.c:338:3:338:31 | call to __builtin_extract_return_addr | Call to builtin function '__builtin_extract_return_addr' is a compiler extension and is not portable to other compilers. | +| test.c:339:3:339:28 | call to __builtin_frob_return_addr | Call to builtin function '__builtin_frob_return_addr' is a compiler extension and is not portable to other compilers. | +| test.c:340:3:340:25 | call to __builtin_frame_address | Call to builtin function '__builtin_frame_address' is a compiler extension and is not portable to other compilers. | +| test.c:361:3:361:22 | call to __sync_fetch_and_add_4 | Call to builtin function '__sync_fetch_and_add_4' is a compiler extension and is not portable to other compilers. | +| test.c:362:3:362:22 | call to __sync_fetch_and_sub_4 | Call to builtin function '__sync_fetch_and_sub_4' is a compiler extension and is not portable to other compilers. | +| test.c:363:3:363:21 | call to __sync_fetch_and_or_4 | Call to builtin function '__sync_fetch_and_or_4' is a compiler extension and is not portable to other compilers. | +| test.c:364:3:364:22 | call to __sync_fetch_and_and_4 | Call to builtin function '__sync_fetch_and_and_4' is a compiler extension and is not portable to other compilers. | +| test.c:365:3:365:22 | call to __sync_fetch_and_xor_4 | Call to builtin function '__sync_fetch_and_xor_4' is a compiler extension and is not portable to other compilers. | +| test.c:366:3:366:23 | call to __sync_fetch_and_nand_4 | Call to builtin function '__sync_fetch_and_nand_4' is a compiler extension and is not portable to other compilers. | +| test.c:367:3:367:22 | call to __sync_add_and_fetch_4 | Call to builtin function '__sync_add_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:368:3:368:22 | call to __sync_sub_and_fetch_4 | Call to builtin function '__sync_sub_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:369:3:369:21 | call to __sync_or_and_fetch_4 | Call to builtin function '__sync_or_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:370:3:370:22 | call to __sync_and_and_fetch_4 | Call to builtin function '__sync_and_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:371:3:371:22 | call to __sync_xor_and_fetch_4 | Call to builtin function '__sync_xor_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:372:3:372:23 | call to __sync_nand_and_fetch_4 | Call to builtin function '__sync_nand_and_fetch_4' is a compiler extension and is not portable to other compilers. | +| test.c:374:3:374:30 | call to __sync_bool_compare_and_swap_4 | Call to builtin function '__sync_bool_compare_and_swap_4' is a compiler extension and is not portable to other compilers. | +| test.c:375:3:375:29 | call to __sync_val_compare_and_swap_4 | Call to builtin function '__sync_val_compare_and_swap_4' is a compiler extension and is not portable to other compilers. | +| test.c:376:3:376:26 | call to __sync_lock_test_and_set_4 | Call to builtin function '__sync_lock_test_and_set_4' is a compiler extension and is not portable to other compilers. | +| test.c:377:3:377:21 | call to __sync_lock_release_4 | Call to builtin function '__sync_lock_release_4' is a compiler extension and is not portable to other compilers. | +| test.c:405:3:405:18 | call to __builtin_alloca | Call to builtin function '__builtin_alloca' is a compiler extension and is not portable to other compilers. | +| test.c:409:1:411:8 | #define BUILTIN __builtin_alloca( 0) | Call to builtin function '__builtin_alloca' is a compiler extension and is not portable to other compilers. | diff --git a/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldBeMinimized.qlref b/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldBeMinimized.qlref new file mode 100644 index 0000000000..8fac333187 --- /dev/null +++ b/c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldBeMinimized.qlref @@ -0,0 +1 @@ +rules/DIR-1-2/LanguageExtensionsShouldBeMinimized.ql diff --git a/c/misra/test/rules/RULE-15-5/SinglePointOfExit.expected b/c/misra/test/rules/RULE-15-5/SinglePointOfExit.expected new file mode 100644 index 0000000000..dde5d709dd --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/SinglePointOfExit.expected @@ -0,0 +1,5 @@ +| test.c:1:6:1:7 | f1 | Function has more than one $@. | test.c:3:5:3:11 | return ... | return statement | +| test.c:1:6:1:7 | f1 | Function has more than one $@. | test.c:5:3:5:9 | return ... | return statement | +| test.c:14:6:14:7 | f3 | The $@ is not the last statement of the function. | test.c:17:3:17:9 | return ... | return statement | +| test.c:21:6:21:7 | f4 | Function has more than one $@. | test.c:23:5:23:11 | return ... | return statement | +| test.c:21:6:21:7 | f4 | Function has more than one $@. | test.c:25:3:25:9 | return ... | return statement | diff --git a/c/misra/test/rules/RULE-15-5/SinglePointOfExit.qlref b/c/misra/test/rules/RULE-15-5/SinglePointOfExit.qlref new file mode 100644 index 0000000000..c0576d5bf0 --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/SinglePointOfExit.qlref @@ -0,0 +1 @@ +rules/RULE-15-5/SinglePointOfExit.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-5/SinglePointOfExit.testref b/c/misra/test/rules/RULE-15-5/SinglePointOfExit.testref deleted file mode 100644 index ce126e9b24..0000000000 --- a/c/misra/test/rules/RULE-15-5/SinglePointOfExit.testref +++ /dev/null @@ -1 +0,0 @@ -c/misra/test/rules/RULE-15-5/FunctionReturnCondition.qlref \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-5/unconfigured/test.c b/c/misra/test/rules/RULE-15-5/unconfigured/test.c index ed072576bc..ac0e47d906 100644 --- a/c/misra/test/rules/RULE-15-5/unconfigured/test.c +++ b/c/misra/test/rules/RULE-15-5/unconfigured/test.c @@ -1 +1,11 @@ -#include "../test.c" \ No newline at end of file +void multiple_exits( + int condition) { // COMPLIANT: the guideline is Disapplied by default. + if (condition) { + return; + } + return; +} + +void single_exit(void) { // COMPLIANT + return; +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.expected b/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.expected index d393ce32e2..8002053fcd 100644 --- a/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.expected +++ b/c/misra/test/rules/RULE-17-3/FunctionDeclaredImplicitly.expected @@ -1,3 +1,3 @@ -| test.c:4:1:4:2 | declaration of f2 | Function declaration is implicit. | -| test.c:12:15:12:15 | declaration of f3 | Function declaration is implicit. | -| test_c99.c:7:3:7:3 | declaration of undeclared | Function declaration is implicit. | +| test.c:5:1:5:2 | declaration of f2 | Function declaration is implicit. | +| test.c:13:15:13:15 | declaration of f3 | Function declaration is implicit. | +| test_c99.c:8:3:8:3 | declaration of undeclared | Function declaration is implicit. | diff --git a/c/misra/test/rules/RULE-17-3/test.c b/c/misra/test/rules/RULE-17-3/test.c index 6c9dd64836..b6786cb0eb 100644 --- a/c/misra/test/rules/RULE-17-3/test.c +++ b/c/misra/test/rules/RULE-17-3/test.c @@ -1,5 +1,6 @@ -// semmle-extractor-options:--clang -std=c11 -nostdinc -// -I../../../../common/test/includes/standard-library +// clang-format off +// semmle-extractor-options:--clang -std=c11 -nostdinc -Wno-implicit +// clang-format on double f1(double x); // COMPLIANT f2(double x); // NON_COMPLIANT diff --git a/c/misra/test/rules/RULE-17-3/test_c99.c b/c/misra/test/rules/RULE-17-3/test_c99.c index 4abc3b2726..b0e4202c2c 100644 --- a/c/misra/test/rules/RULE-17-3/test_c99.c +++ b/c/misra/test/rules/RULE-17-3/test_c99.c @@ -1,5 +1,6 @@ -// semmle-extractor-options:--clang -std=c99 -nostdinc -// -Wno-implicit-function-declaration +// clang-format off +// semmle-extractor-options:--clang -std=c99 -nostdinc -Wno-implicit-function-declaration +// clang-format on void declared(void); void c99_calls(void) { diff --git a/c/misra/test/rules/RULE-20-15/ReservedMacroName.testref b/c/misra/test/rules/RULE-20-15/ReservedMacroName.testref index 20b9ecb927..41abe76818 100644 --- a/c/misra/test/rules/RULE-20-15/ReservedMacroName.testref +++ b/c/misra/test/rules/RULE-20-15/ReservedMacroName.testref @@ -1 +1 @@ -c/misra/test/rules/RULE-21-1/DefineAndUndefUsedOnReservedIdentifierOrMacroName.qlref \ No newline at end of file +c/misra/test/rules/RULE-21-1/ReservedMacroName.qlref \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-1/ReservedMacroName.expected b/c/misra/test/rules/RULE-21-1/ReservedMacroName.expected new file mode 100644 index 0000000000..ef9700d8d3 --- /dev/null +++ b/c/misra/test/rules/RULE-21-1/ReservedMacroName.expected @@ -0,0 +1,3 @@ +| 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:5:1:5:13 | #define errno | Reserved identifier 'errno' has been undefined or redefined. | diff --git a/c/misra/test/rules/RULE-21-1/ReservedMacroName.qlref b/c/misra/test/rules/RULE-21-1/ReservedMacroName.qlref new file mode 100644 index 0000000000..3de1f6dd86 --- /dev/null +++ b/c/misra/test/rules/RULE-21-1/ReservedMacroName.qlref @@ -0,0 +1 @@ +rules/RULE-20-15/ReservedMacroName.ql diff --git a/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected b/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected index 8859615930..6f6ba7090d 100644 --- a/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected +++ b/c/misra/test/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.expected @@ -4,4 +4,4 @@ | test.c:14:3:14:9 | call to exit | Call to banned function exit. | | test.c:15:3:15:7 | call to abort | Call to banned function abort. | | test.c:16:3:16:14 | call to system | Call to banned function system. | -| test.c:23:3:23:19 | call to abort | Call to banned function abort. | +| test.c:23:3:23:19 | call to __assert_fail | Enabled assertion calls non-returning function '__assert_fail'. | diff --git a/c/misra/test/rules/RULE-21-8/assert.h b/c/misra/test/rules/RULE-21-8/assert.h deleted file mode 100644 index 33eab34fcf..0000000000 --- a/c/misra/test/rules/RULE-21-8/assert.h +++ /dev/null @@ -1,8 +0,0 @@ -#include - -#undef assert -#ifdef NDEBUG -#define assert(value) ((void)0) -#else -#define assert(value) ((value) ? (void)0 : abort()) -#endif \ No newline at end of file diff --git a/c/misra/test/rules/RULE-21-8/test.c b/c/misra/test/rules/RULE-21-8/test.c index 3e84945c50..a97bed72b1 100644 --- a/c/misra/test/rules/RULE-21-8/test.c +++ b/c/misra/test/rules/RULE-21-8/test.c @@ -17,15 +17,21 @@ void f2() { MULTIPLY(1, 2); // COMPLIANT } -#include "assert.h" +#include void enabled_assertions(int condition) { assert(condition); // NON_COMPLIANT } #define NDEBUG -#include "assert.h" +#include void disabled_assertions(int condition) { assert(condition); // COMPLIANT } + +_Noreturn void non_assertion_termination(void); + +void non_assertion_calls(void) { + non_assertion_termination(); // COMPLIANT +} diff --git a/c/misra/test/rules/RULE-8-18/DefinitionsInHeader.expected b/c/misra/test/rules/RULE-8-18/DefinitionsInHeader.expected index 1a50ff1da2..31f190469c 100644 --- a/c/misra/test/rules/RULE-8-18/DefinitionsInHeader.expected +++ b/c/misra/test/rules/RULE-8-18/DefinitionsInHeader.expected @@ -1,3 +1,5 @@ +| conditional.h:4:5:4:21 | definition of conditional_after | Tentative definition of 'conditional_after' appears in a header file. | +| conditional.h:8:5:8:22 | definition of conditional_before | Tentative definition of 'conditional_before' appears in a header file. | | sample.h:4:5:4:20 | definition of header_tentative | Tentative definition of 'header_tentative' appears in a header file. | | sample.h:5:12:5:24 | definition of static_object | Tentative definition of 'static_object' appears in a header file. | | sample.h:6:11:6:22 | definition of const_object | Tentative definition of 'const_object' appears in a header file. | @@ -6,3 +8,4 @@ | sample.h:13:5:13:22 | declaration of same_header_object | Tentative definition of 'same_header_object' appears in a header file. | | sample.h:18:5:18:21 | definition of redeclared_object | Tentative definition of 'redeclared_object' appears in a header file. | | sample.h:23:16:23:34 | definition of macro_header_object | Tentative definition of 'macro_header_object' appears in a header file. | +| sample.h:25:5:25:19 | definition of cross_tu_object | Tentative definition of 'cross_tu_object' appears in a header file. | diff --git a/c/misra/test/rules/RULE-8-18/conditional.h b/c/misra/test/rules/RULE-8-18/conditional.h new file mode 100644 index 0000000000..7bfc514566 --- /dev/null +++ b/c/misra/test/rules/RULE-8-18/conditional.h @@ -0,0 +1,11 @@ +#ifdef INITIALIZE_CONDITIONAL +int conditional_after = 1; // COMPLIANT +#else +int conditional_after; // NON_COMPLIANT +#endif + +#ifndef INITIALIZE_CONDITIONAL +int conditional_before; // NON_COMPLIANT +#else +int conditional_before = 1; // COMPLIANT +#endif diff --git a/c/misra/test/rules/RULE-8-18/other.c b/c/misra/test/rules/RULE-8-18/other.c new file mode 100644 index 0000000000..8645d9f3a0 --- /dev/null +++ b/c/misra/test/rules/RULE-8-18/other.c @@ -0,0 +1,5 @@ +// semmle-extractor-options:--clang -std=c11 -nostdinc +int cross_tu_object = 0; // COMPLIANT + +#define INITIALIZE_CONDITIONAL +#include "conditional.h" diff --git a/c/misra/test/rules/RULE-8-18/sample.h b/c/misra/test/rules/RULE-8-18/sample.h index ad82fef707..a59e0535a9 100644 --- a/c/misra/test/rules/RULE-8-18/sample.h +++ b/c/misra/test/rules/RULE-8-18/sample.h @@ -20,4 +20,9 @@ extern int redeclared_object; // COMPLIANT // Macro-generated declarations follow their expansion file. #define DECLARE_OBJECT(name) int name -DECLARE_OBJECT(macro_header_object); // NON_COMPLIANT \ No newline at end of file +DECLARE_OBJECT(macro_header_object); // NON_COMPLIANT + +int cross_tu_object; // NON_COMPLIANT + +#define DEFINE_INITIALIZED_OBJECT(name) int name = 0 +DEFINE_INITIALIZED_OBJECT(macro_initialized_object); // COMPLIANT \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-18/test.c b/c/misra/test/rules/RULE-8-18/test.c index 6c02841ac3..7baff1df43 100644 --- a/c/misra/test/rules/RULE-8-18/test.c +++ b/c/misra/test/rules/RULE-8-18/test.c @@ -1,4 +1,5 @@ // semmle-extractor-options:--clang -std=c11 -nostdinc +#include "conditional.h" #include "sample.h" // Source definitions are allowed, including completion of header declarations. diff --git a/change_notes/2026-11-03-misra-c-2025-query-updates.md b/change_notes/2026-11-03-misra-c-2025-query-updates.md index 40a85a50e5..ca9fd38a63 100644 --- a/change_notes/2026-11-03-misra-c-2025-query-updates.md +++ b/change_notes/2026-11-03-misra-c-2025-query-updates.md @@ -1,8 +1,14 @@ - Default MISRA C suites now target MISRA C:2025; support remains incomplete. Explicit 2023 and 2012 suites retain their guideline sets and query IDs. +- The supported-rules report lists unchanged applicable guidelines under MISRA C:2025 + while retaining their older-edition entries. - `DIR-4-6` - `PlainIntegerTypeUsedOverExplicitTypedef.ql`: - The 2025 query excludes floating-point declarations and aliases. - `RULE-15-5` - `SinglePointOfExit.ql`: - Disapplied by default; recategorization can reactivate the query. - `RULE-17-6` - `UseOfArrayStatic.ql`: - - Excluded from 2025 suites; Rule 17.5 array-size contract checks remain enabled. \ No newline at end of file + - Excluded from 2025 suites; Rule 17.5 array-size contract checks remain enabled. +- `RULE-8-18` - `DefinitionsInHeader.ql`: + - Report tentative header definitions even when another translation unit initializes the variable. +- `RULE-21-8` - `TerminationFunctionsOfStdlibhUsed.ql`: + - Report enabled assertions that call non-returning assertion-failure helpers. \ No newline at end of file diff --git a/scripts/PSCodingStandards/Get-TestDirectory.ps1 b/scripts/PSCodingStandards/Get-TestDirectory.ps1 index 2d6cd4f9a2..7950d87433 100644 --- a/scripts/PSCodingStandards/Get-TestDirectory.ps1 +++ b/scripts/PSCodingStandards/Get-TestDirectory.ps1 @@ -9,33 +9,5 @@ function Get-TestDirectory { $Language ) - if ($RuleObject.shared_implementation_short_name) { - $standardString = "common" - $ruleDir = $RuleObject.shared_implementation_short_name.ToLower() - } - else { - - if ($RuleObject.__memberof_suite -eq "AUTOSAR") { - $standardString = "autosar" - } - elseif ($RuleObject.__memberof_suite -eq "CERT-C++") { - $standardString = "cert" - } - elseif ($RuleObject.__memberof_suite -eq "CERT-C") { - $standardString = "cert" - } - elseif ($RuleObject.__memberof_suite -in @("MISRA-C-2012", "MISRA-C-2025")) { - $standardString = "misra" - } - elseif ($RuleObject.__memberof_suite -eq "MISRA-C++-2023") { - $standardString = "misra" - } - else { - throw "Unknown standard $($RuleObject.__memberof_suite)" - } - - $ruleDir = $RuleObject.__memberof_rule - } - - return Join-Path (Join-Path (Join-Path (Join-Path $Language $standardString) "test") "rules") $ruleDir + return Split-Path -Path (Get-TestFile -RuleObject $RuleObject -Language $Language) -Parent } diff --git a/scripts/PSCodingStandards/Get-TestFile.ps1 b/scripts/PSCodingStandards/Get-TestFile.ps1 new file mode 100644 index 0000000000..6d5fa68600 --- /dev/null +++ b/scripts/PSCodingStandards/Get-TestFile.ps1 @@ -0,0 +1,65 @@ +function Get-TestFile { + param( + [Parameter(Mandatory)] + [object] + $RuleObject, + [Parameter(Mandatory)] + [ValidateSet('c', 'cpp')] + [string] + $Language + ) + + if ($RuleObject.shared_implementation_short_name) { + $standardString = "common" + $queryName = $RuleObject.shared_implementation_short_name + $ruleDir = $queryName.ToLower() + $extension = ".ql" + } + else { + if ($RuleObject.__memberof_suite -eq "AUTOSAR") { + $standardString = "autosar" + } + elseif ($RuleObject.__memberof_suite -in @("CERT-C", "CERT-C++")) { + $standardString = "cert" + } + elseif ($RuleObject.__memberof_suite -in @( + "MISRA-C-2012", "MISRA-C-2025", "MISRA-C++-2023" + )) { + $standardString = "misra" + } + else { + throw "Unknown standard $($RuleObject.__memberof_suite)" + } + + $queryName = $RuleObject.short_name + $ruleDir = $RuleObject.__memberof_rule + $extension = ".qlref" + } + + $testDirectory = Join-Path (Join-Path (Join-Path (Join-Path $Language $standardString) "test") "rules") $ruleDir + $referenceFile = Join-Path $testDirectory "$queryName.testref" + if (Test-Path -LiteralPath $referenceFile -PathType Leaf) { + $reference = Get-Content -LiteralPath $referenceFile -Raw -ErrorAction Stop + if ([string]::IsNullOrWhiteSpace($reference)) { + throw "Empty test reference: $referenceFile" + } + if ([IO.Path]::IsPathRooted($reference.Trim())) { + throw "Test reference must be repository-relative: $referenceFile" + } + + $testFile = Join-Path (Get-RepositoryRoot) $reference.Trim() + if ([IO.Path]::GetExtension($testFile) -notin @(".ql", ".qlref")) { + throw "Test reference must name a .ql or .qlref file: $referenceFile" + } + if (-not (Test-Path -LiteralPath $testFile -PathType Leaf)) { + throw "Referenced test does not exist: $testFile" + } + $expectedFile = [IO.Path]::ChangeExtension($testFile, ".expected") + if (-not (Test-Path -LiteralPath $expectedFile -PathType Leaf)) { + throw "Referenced test has no expected results: $expectedFile" + } + return (Resolve-Path -LiteralPath $testFile).Path + } + + return Join-Path $testDirectory "$queryName$extension" +} diff --git a/scripts/PSCodingStandards/README.md b/scripts/PSCodingStandards/README.md index 954547f4f8..610d331664 100644 --- a/scripts/PSCodingStandards/README.md +++ b/scripts/PSCodingStandards/README.md @@ -45,6 +45,23 @@ Get-RulesInPackageAndSuite -Suite cert-c -Package (Get-Packages -Language c | Wh (Get-RulesInSuite -Language cpp -Suite autosar | Where-Object { $_.severity -eq 'error' }) | ForEach-Object { Get-TestDirectory -Language c -RuleObject $_ } ``` +**Get the runnable test file, following reused-fixture references** + +``` +(Get-RulesInSuite -Language c -Suite MISRA-C-2025) | ForEach-Object { Get-TestFile -Language c -RuleObject $_ } +``` + +A `.testref` contains a repository-relative path to a runnable `.ql` or `.qlref` file +with matching `.expected` results. A new query variant needs its own runnable test, +but can reuse an existing C/C++ fixture. The matrix runner uses the referenced +file's directory, query name, and expected results. + +Run the test-reference regression checks with: + +``` +pwsh -NoLogo -NoProfile -File scripts/matrix_testing/Get-TestFile.Tests.ps1 +``` + **Get the Root of Coding Standards** ``` Get-RepositoryRoot diff --git a/scripts/matrix_testing/CreateMatrixTestReport.ps1 b/scripts/matrix_testing/CreateMatrixTestReport.ps1 index a370c48b61..2317d6d1e1 100644 --- a/scripts/matrix_testing/CreateMatrixTestReport.ps1 +++ b/scripts/matrix_testing/CreateMatrixTestReport.ps1 @@ -293,6 +293,9 @@ $jobRows = $queriesToCheck | ForEach-Object -ThrottleLimit $NumThreads -Parallel $CurrentQueryName = $q.short_name $CurrentPackageName = $q.__memberof_package + $testFile = Get-TestFile -RuleObject $q -Language $using:Language + $testFileName = Split-Path -Path $testFile -Leaf + $testQueryName = [IO.Path]::GetFileNameWithoutExtension($testFile) # all the test directories -- there may be more than one for a given rule $testDirs = (Get-ATestDirectory -RuleObject $q -Language $using:Language) @@ -327,12 +330,7 @@ $jobRows = $queriesToCheck | ForEach-Object -ThrottleLimit $NumThreads -Parallel # Push context ########################################################### - if ($q.shared_implementation_short_name) { - $fileSet = (Get-CompilerSpecificFiles -Configuration $using:Configuration -Language $using:Language -TestDirectory $testDirectory -Query $q.shared_implementation_short_name) - } - else { - $fileSet = (Get-CompilerSpecificFiles -Configuration $using:Configuration -Language $using:Language -TestDirectory $testDirectory -Query $CurrentQueryName) - } + $fileSet = Get-CompilerSpecificFiles -Configuration $using:Configuration -Language $using:Language -TestDirectory $testDirectory -Query $testQueryName if($fileSet){ $context = Push-CompilerSpecificFiles -Configuration $using:Configuration -Language $using:Language -FileSet $fileSet @@ -382,12 +380,7 @@ $jobRows = $queriesToCheck | ForEach-Object -ThrottleLimit $NumThreads -Parallel # Actually do the qltest run. # codeql test run --dataset "relpath" - if ($q.shared_implementation_short_name) { - $qlRefFile = Join-Path $rulePath "$($q.shared_implementation_short_name).ql" - } - else { - $qlRefFile = Join-Path $rulePath "$CurrentQueryName.qlref" - } + $qlRefFile = Join-Path $rulePath $testFileName Write-Host "codeql test run $qlRefFile --search-path . --dataset=`"$datasetRelPath`"" diff --git a/scripts/matrix_testing/Get-TestFile.Tests.ps1 b/scripts/matrix_testing/Get-TestFile.Tests.ps1 new file mode 100644 index 0000000000..f7c7d91e28 --- /dev/null +++ b/scripts/matrix_testing/Get-TestFile.Tests.ps1 @@ -0,0 +1,43 @@ +$ErrorActionPreference = "Stop" +$repositoryRoot = (Resolve-Path "$PSScriptRoot/../..").Path +Push-Location $repositoryRoot +try { + Import-Module "$repositoryRoot/scripts/PSCodingStandards/CodingStandards" + $queries = Get-RulesInSuite -Language c -Suite "MISRA-C-2025" + $expectedFiles = @{ + "LanguageExtensionsShouldBeMinimized" = "c/misra/test/rules/RULE-1-2/LanguageExtensionsShouldBeMinimized.qlref" + "ReservedMacroName" = "c/misra/test/rules/RULE-21-1/ReservedMacroName.qlref" + "SinglePointOfExit" = "c/misra/test/rules/RULE-15-5/SinglePointOfExit.qlref" + } + + foreach ($name in $expectedFiles.Keys) { + $query = @($queries | Where-Object { $_.short_name -eq $name }) + if ($query.Count -ne 1) { + throw "Expected exactly one query named $name" + } + $testFile = Get-TestFile -RuleObject $query[0] -Language c + $expected = [IO.Path]::GetFullPath((Join-Path $repositoryRoot $expectedFiles[$name])) + if ([IO.Path]::GetFullPath($testFile) -ne $expected) { + throw "Unexpected test file for ${name}: $testFile" + } + $directory = Get-TestDirectory -RuleObject $query[0] -Language c + if ([IO.Path]::GetFullPath($directory) -ne (Split-Path $expected -Parent)) { + throw "Unexpected fixture directory for ${name}: $directory" + } + if (-not (Test-Path ([IO.Path]::ChangeExtension($testFile, ".expected")))) { + throw "Missing expected results for $name" + } + } + + $shared = [pscustomobject]@{ + shared_implementation_short_name = "SharedQuery" + } + $sharedFile = Get-TestFile -RuleObject $shared -Language cpp + if ($sharedFile.Replace("\", "/") -ne "cpp/common/test/rules/sharedquery/SharedQuery.ql") { + throw "Unexpected shared test file: $sharedFile" + } + Write-Host "Get-TestFile regression tests passed." +} +finally { + Pop-Location +} diff --git a/scripts/release/create_supported_rules_list.py b/scripts/release/create_supported_rules_list.py index 771626c684..f4a2ae87d5 100644 --- a/scripts/release/create_supported_rules_list.py +++ b/scripts/release/create_supported_rules_list.py @@ -12,6 +12,9 @@ When run without any arguments, the script iterates through each of the rule package description files stored in the `rule_packages` directory, and identifies which rules are supported by one or more queries. + +MISRA C edition membership follows applicability tags and current-suite legacy exclusions, +rather than only the package metadata's owning standard. """ if (len(sys.argv) == 2 and sys.argv[1] == "--help"): @@ -43,9 +46,21 @@ package_definition = json.load(rule_package_file) for standard_name, rules in package_definition.items(): for rule_id, rule_details in rules.items(): - # If we have at least one query specified - if len(rule_details["queries"]) > 0: - rules_covered[standard_name][rule_id] = get_query_short_names(rule_details) + for query in rule_details["queries"]: + if standard_name in {"MISRA-C-2012", "MISRA-C-2025"}: + applicable_standards = [] + tags = query["tags"] + if any(tag.startswith("external/misra/c/2012/") for tag in tags): + applicable_standards.append("MISRA-C-2012") + # Current suites reuse unchanged queries but exclude legacy variants. + if "external/misra/legacy" not in tags: + applicable_standards.append("MISRA-C-2025") + else: + applicable_standards = [standard_name] + for applicable_standard in applicable_standards: + queries = rules_covered[applicable_standard].setdefault(rule_id, []) + if query["short_name"] not in queries: + queries.append(query["short_name"]) if is_csv: diff --git a/scripts/release/create_supported_rules_list_test.py b/scripts/release/create_supported_rules_list_test.py new file mode 100644 index 0000000000..124f1b3874 --- /dev/null +++ b/scripts/release/create_supported_rules_list_test.py @@ -0,0 +1,116 @@ +import csv +import io +import json +from pathlib import Path +import shutil +import subprocess +import sys + +import pytest + + +LEGACY_EDITION = "external/misra/c/2012/third-edition-first-revision" +CURRENT_EDITION = "external/misra/c/2025" +LEGACY_VARIANT = "external/misra/legacy" + + +def supported_rules(tmp_path, packages): + script_directory = tmp_path / "scripts" / "release" + script_directory.mkdir(parents=True) + for name in ("create_supported_rules_list.py", "utils.py"): + shutil.copyfile(Path(__file__).parent / name, script_directory / name) + for language in ("c", "cpp"): + (tmp_path / "rule_packages" / language).mkdir(parents=True) + (tmp_path / "rule_packages" / "c" / "Fixture.json").write_text(json.dumps(packages)) + result = subprocess.run( + [sys.executable, str(script_directory / "create_supported_rules_list.py"), "--csv"], + check=True, + capture_output=True, + text=True, + ) + return { + (row["Coding Standard"], row["Rule ID"]): row["Queries"] + for row in csv.DictReader(io.StringIO(result.stdout)) + } + + +@pytest.mark.parametrize("tags", [[LEGACY_EDITION], [LEGACY_EDITION, CURRENT_EDITION]]) +def test_unchanged_rules_are_supported_in_both_editions(tmp_path, tags): + packages = { + "MISRA-C-2012": { + rule: {"queries": [{"short_name": name, "tags": tags}]} + for rule, name in ( + ("RULE-17-3", "FunctionDeclaredImplicitly"), + ("RULE-17-5", "ArrayFunctionArgumentNumberOfElements"), + ("RULE-21-8", "TerminationFunctionsOfStdlibhUsed"), + ) + } + } + rows = supported_rules(tmp_path, packages) + for rule, details in packages["MISRA-C-2012"].items(): + expected = details["queries"][0]["short_name"] + ".ql" + assert rows["MISRA-C-2012", rule] == expected + assert rows["MISRA-C-2025", rule] == expected + + +def test_edition_specific_variants_do_not_overwrite_each_other(tmp_path): + rows = supported_rules(tmp_path, { + "MISRA-C-2012": { + "RULE-15-5": {"queries": [{ + "short_name": "FunctionReturnCondition", + "tags": [LEGACY_EDITION, LEGACY_VARIANT], + }]}, + }, + "MISRA-C-2025": { + "RULE-15-5": {"queries": [{ + "short_name": "SinglePointOfExit", + "tags": [CURRENT_EDITION, "external/misra/default-disabled"], + }]}, + }, + }) + assert rows["MISRA-C-2012", "RULE-15-5"] == "FunctionReturnCondition.ql" + assert rows["MISRA-C-2025", "RULE-15-5"] == "SinglePointOfExit.ql" + + +def test_applicability_is_filtered_per_query(tmp_path): + rows = supported_rules(tmp_path, { + "MISRA-C-2012": { + "RULE-1-1": {"queries": [ + {"short_name": "Legacy", "tags": [LEGACY_EDITION, LEGACY_VARIANT]}, + {"short_name": "Current", "tags": [LEGACY_EDITION, CURRENT_EDITION]}, + ]}, + }, + }) + assert rows["MISRA-C-2012", "RULE-1-1"] == "Legacy.ql;Current.ql" + assert rows["MISRA-C-2025", "RULE-1-1"] == "Current.ql" + + +def test_renumbered_and_new_rules_do_not_leak_into_older_editions(tmp_path): + rows = supported_rules(tmp_path, { + "MISRA-C-2012": { + "RULE-21-1": {"queries": [{ + "short_name": "LegacyReservedMacro", + "tags": [LEGACY_EDITION, LEGACY_VARIANT], + }]}, + }, + "MISRA-C-2025": { + rule: {"queries": [{"short_name": name, "tags": [CURRENT_EDITION]}]} + for rule, name in ( + ("RULE-20-15", "ReservedMacroName"), + ("RULE-8-18", "DefinitionsInHeader"), + ) + }, + }) + assert ("MISRA-C-2025", "RULE-21-1") not in rows + assert ("MISRA-C-2012", "RULE-20-15") not in rows + assert ("MISRA-C-2012", "RULE-8-18") not in rows + assert rows["MISRA-C-2025", "RULE-20-15"] == "ReservedMacroName.ql" + + +def test_other_standards_are_unchanged(tmp_path): + rows = supported_rules(tmp_path, { + "CERT-C": { + "EXP01-C": {"queries": [{"short_name": "Example", "tags": ["correctness"]}]}, + }, + }) + assert rows == {("CERT-C", "EXP01-C"): "Example.ql"} diff --git a/scripts/verify_rule_package_consistency.py b/scripts/verify_rule_package_consistency.py index acd1b7496e..3eb86e856c 100644 --- a/scripts/verify_rule_package_consistency.py +++ b/scripts/verify_rule_package_consistency.py @@ -5,6 +5,19 @@ import sys import json + +def has_misra_c_edition_tag(standard_name, tags): + """Checks that the tags identify the metadata's MISRA C edition.""" + if standard_name == "MISRA-C-2012": + return any(tag.startswith("external/misra/c/2012/") for tag in tags) + if standard_name == "MISRA-C-2025": + return any( + tag == "external/misra/c/2025" or tag.startswith("external/misra/c/2025/") + for tag in tags + ) + raise ValueError(f"Unsupported MISRA C edition: {standard_name}") + + help_statement = """ Usage: {script_name} @@ -101,13 +114,13 @@ f" - ERROR: Rule {rule_id} included in {package_name}.json but not marked as supportable in rules.csv.") failed = True for query in rule_details["queries"]: - if standard_name in {"MISRA-C-2025", "MISRA-C-2012"} and not any(tag for tag in query["tags"] if tag.startswith("external/misra/c/")): + if standard_name in {"MISRA-C-2025", "MISRA-C-2012"} and not has_misra_c_edition_tag(standard_name, query["tags"]): print( - f' - ERROR: MISRA C 2012 query {query["short_name"]}.ql for Rule {rule_id} in {package_name}.json is missing a `external/misra/c/2012/...` tag.') + f' - ERROR: {standard_name} query {query["short_name"]}.ql for Rule {rule_id} in {package_name}.json is missing a matching MISRA C edition tag.') failed = True if standard_name not in {"MISRA-C-2025", "MISRA-C-2012"} and any(tag for tag in query["tags"] if tag.startswith("external/misra/c/")): print( - f' - ERROR: {standard_name} query {query["short_name"]}.ql for Rule {rule_id} in {package_name}.json has a spurious `external/misra/c/2012/...` tag.') + f' - ERROR: {standard_name} query {query["short_name"]}.ql for Rule {rule_id} in {package_name}.json has a spurious `external/misra/c/...` tag.') failed = True if standard_name == "CERT-C" or standard_name == "CERT-C++": expected_properties = [ diff --git a/scripts/verify_rule_package_consistency_test.py b/scripts/verify_rule_package_consistency_test.py new file mode 100644 index 0000000000..cb5d244e76 --- /dev/null +++ b/scripts/verify_rule_package_consistency_test.py @@ -0,0 +1,53 @@ +import csv +import json +from pathlib import Path +import shutil +import subprocess +import sys + +import pytest + + +@pytest.mark.parametrize("standard,tags,valid", [ + ("MISRA-C-2012", ["external/misra/c/2012/third-edition-first-revision"], True), + ("MISRA-C-2012", ["external/misra/c/2012/amendment4"], True), + ("MISRA-C-2025", ["external/misra/c/2025"], True), + ("MISRA-C-2025", ["external/misra/c/2025/amendment1"], True), + ("MISRA-C-2012", ["external/misra/c/2025"], False), + ("MISRA-C-2025", ["external/misra/c/2012/amendment4"], False), + ("MISRA-C-2012", ["external/misra/c/2012"], False), + ("MISRA-C-2025", ["external/misra/c/20250"], False), + ("MISRA-C-2012", ["external/misra/c/audit"], False), + ("MISRA-C-2012", ["external/misra/c/strict"], False), + ("MISRA-C-2025", ["external/misra/c/audit"], False), + ("MISRA-C-2025", ["external/misra/c/strict"], False), + ("MISRA-C-2025", ["correctness"], False), +]) +def test_misra_c_requires_a_matching_edition_tag(tmp_path, standard, tags, valid): + script_directory = tmp_path / "scripts" + script_directory.mkdir() + script = script_directory / "verify_rule_package_consistency.py" + shutil.copyfile(Path(__file__).parent / script.name, script) + with (tmp_path / "rules.csv").open("w", newline="") as output: + writer = csv.writer(output) + writer.writerow(["language", "standard", "rule_id", "supportable", "obligation", + "enforcement", "targets", "title", "similar", "package", "difficulty"]) + writer.writerow(["c", standard, "RULE-1-1", "Yes", "Required", "Automated", + "", "Fixture", "", "Fixture", "Easy"]) + package_directory = tmp_path / "rule_packages" / "c" + package_directory.mkdir(parents=True) + (package_directory / "Fixture.json").write_text(json.dumps({ + standard: { + "RULE-1-1": { + "queries": [{"short_name": "Fixture", "tags": tags}], + }, + }, + })) + result = subprocess.run( + [sys.executable, str(script), "c"], + capture_output=True, + text=True, + ) + assert result.returncode == (0 if valid else 1), result.stdout + result.stderr + if not valid: + assert "missing" in result.stdout From 5b4dd367ce6d15e9f661b6fd811e4ed2c1007805 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 3 Nov 2026 21:10:11 +0100 Subject: [PATCH 4/4] Simplify MISRA C rule matching and reporting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../TerminationFunctionsOfStdlibhUsed.ql | 28 ++++-------- ...TerminationFunctionsOfStdlibhUsed.expected | 3 ++ .../TerminationFunctionsOfStdlibhUsed.qlref | 1 + .../qualified-names/headers/stdlib.h | 11 +++++ .../rules/RULE-21-8/qualified-names/test.cpp | 15 +++++++ .../release/create_supported_rules_list.py | 45 ++++++++++--------- 6 files changed, 63 insertions(+), 40 deletions(-) create mode 100644 c/misra/test/rules/RULE-21-8/qualified-names/TerminationFunctionsOfStdlibhUsed.expected create mode 100644 c/misra/test/rules/RULE-21-8/qualified-names/TerminationFunctionsOfStdlibhUsed.qlref create mode 100644 c/misra/test/rules/RULE-21-8/qualified-names/headers/stdlib.h create mode 100644 c/misra/test/rules/RULE-21-8/qualified-names/test.cpp diff --git a/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql b/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql index 8b9fbf1b4d..fe63731998 100644 --- a/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql +++ b/c/misra/src/rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql @@ -18,27 +18,17 @@ import cpp import codingstandards.c.misra import semmle.code.cpp.commons.Assertions -/** A Standard Library termination function prohibited by this rule. */ -class BannedFunction extends Function { - BannedFunction() { - this.getName() = ["abort", "exit", "system"] and - this.getFile().getBaseName() = "stdlib.h" - } -} - -from FunctionCall fc, Function f, string message +from FunctionCall fc, string message where not isExcluded(fc, BannedPackage::terminationFunctionsOfStdlibhUsedQuery()) and - f = fc.getTarget() and - ( - f instanceof BannedFunction - or - any(Options options).exits(f) and - exists(LibcAssert assertion | assertion.getAnExpandedElement() = fc) - ) and - ( - if f instanceof BannedFunction + exists(Function f | f = fc.getTarget() | + if f.hasGlobalOrStdName(["abort", "exit", "system"]) and f.getFile().getBaseName() = "stdlib.h" then message = "Call to banned function " + f.getName() + "." - else message = "Enabled assertion calls non-returning function '" + f.getName() + "'." + else + exists(Options options, LibcAssert assertion | + options.exits(f) and + assertion.getAnExpandedElement() = fc and + message = "Enabled assertion calls non-returning function '" + f.getName() + "'." + ) ) select fc, message diff --git a/c/misra/test/rules/RULE-21-8/qualified-names/TerminationFunctionsOfStdlibhUsed.expected b/c/misra/test/rules/RULE-21-8/qualified-names/TerminationFunctionsOfStdlibhUsed.expected new file mode 100644 index 0000000000..d05cef5142 --- /dev/null +++ b/c/misra/test/rules/RULE-21-8/qualified-names/TerminationFunctionsOfStdlibhUsed.expected @@ -0,0 +1,3 @@ +| test.cpp:12:3:12:7 | call to abort | Call to banned function abort. | +| test.cpp:13:3:13:6 | call to exit | Call to banned function exit. | +| test.cpp:14:3:14:8 | call to system | Call to banned function system. | diff --git a/c/misra/test/rules/RULE-21-8/qualified-names/TerminationFunctionsOfStdlibhUsed.qlref b/c/misra/test/rules/RULE-21-8/qualified-names/TerminationFunctionsOfStdlibhUsed.qlref new file mode 100644 index 0000000000..f4b871f3b5 --- /dev/null +++ b/c/misra/test/rules/RULE-21-8/qualified-names/TerminationFunctionsOfStdlibhUsed.qlref @@ -0,0 +1 @@ +rules/RULE-21-8/TerminationFunctionsOfStdlibhUsed.ql diff --git a/c/misra/test/rules/RULE-21-8/qualified-names/headers/stdlib.h b/c/misra/test/rules/RULE-21-8/qualified-names/headers/stdlib.h new file mode 100644 index 0000000000..00e97a4e3a --- /dev/null +++ b/c/misra/test/rules/RULE-21-8/qualified-names/headers/stdlib.h @@ -0,0 +1,11 @@ +namespace application { +void abort(); +void exit(int); +int system(const char *); +} // namespace application + +namespace std { +void abort(); +void exit(int); +int system(const char *); +} // namespace std diff --git a/c/misra/test/rules/RULE-21-8/qualified-names/test.cpp b/c/misra/test/rules/RULE-21-8/qualified-names/test.cpp new file mode 100644 index 0000000000..822c2431e3 --- /dev/null +++ b/c/misra/test/rules/RULE-21-8/qualified-names/test.cpp @@ -0,0 +1,15 @@ +// semmle-extractor-options:--clang -std=c++14 -nostdinc +#include "headers/stdlib.h" + +using std::abort; +using std::exit; +using std::system; + +void qualified_library_calls() { + application::abort(); // COMPLIANT + application::exit(0); // COMPLIANT + application::system(""); // COMPLIANT + abort(); // NON_COMPLIANT + exit(0); // NON_COMPLIANT + system(""); // NON_COMPLIANT +} diff --git a/scripts/release/create_supported_rules_list.py b/scripts/release/create_supported_rules_list.py index f4a2ae87d5..f788fe520f 100644 --- a/scripts/release/create_supported_rules_list.py +++ b/scripts/release/create_supported_rules_list.py @@ -4,6 +4,20 @@ import json from utils import * + +def get_applicable_standards(standard_name, query): + """Yield applicable standards, including unchanged queries reused by MISRA C:2025.""" + if standard_name not in {"MISRA-C-2012", "MISRA-C-2025"}: + yield standard_name + return + + tags = query["tags"] + if any(tag.startswith("external/misra/c/2012/") for tag in tags): + yield "MISRA-C-2012" + if "external/misra/legacy" not in tags: + yield "MISRA-C-2025" + + help_statement = """ Usage: {script_name} @@ -37,30 +51,19 @@ rule_packages_file_path = repo_root.joinpath('rule_packages', language_name) for rule_package_file_name in os.listdir(rule_packages_file_path): try: - rule_package_file = open(rule_packages_file_path.joinpath(rule_package_file_name), "r") + package_definition = json.loads( + rule_packages_file_path.joinpath(rule_package_file_name).read_text() + ) except PermissionError: print("Error: No permission to read the rule package file located at '" + str(rule_package_file_name) + "'") sys.exit(1) - else: - with rule_package_file: - package_definition = json.load(rule_package_file) - for standard_name, rules in package_definition.items(): - for rule_id, rule_details in rules.items(): - for query in rule_details["queries"]: - if standard_name in {"MISRA-C-2012", "MISRA-C-2025"}: - applicable_standards = [] - tags = query["tags"] - if any(tag.startswith("external/misra/c/2012/") for tag in tags): - applicable_standards.append("MISRA-C-2012") - # Current suites reuse unchanged queries but exclude legacy variants. - if "external/misra/legacy" not in tags: - applicable_standards.append("MISRA-C-2025") - else: - applicable_standards = [standard_name] - for applicable_standard in applicable_standards: - queries = rules_covered[applicable_standard].setdefault(rule_id, []) - if query["short_name"] not in queries: - queries.append(query["short_name"]) + for standard_name, rules in package_definition.items(): + for rule_id, rule_details in rules.items(): + for query in rule_details["queries"]: + for applicable_standard in get_applicable_standards(standard_name, query): + queries = rules_covered[applicable_standard].setdefault(rule_id, []) + if query["short_name"] not in queries: + queries.append(query["short_name"]) if is_csv: