From 64b7684697b055b17df9a054e9f8141ab625b58f Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 9 Mar 2025 22:42:15 -0700 Subject: [PATCH 01/12] Implement generics package --- ...otAddOrSubtractAScaledIntegerToAPointer.ql | 2 +- .../IncompatibleFunctionDeclarations.ql | 8 +- c/common/src/codingstandards/c/Generic.qll | 155 +++++++ .../src/codingstandards/c/OutOfBounds.qll | 2 +- .../codingstandards/c/UndefinedBehavior.qll | 2 +- .../c/misra/EssentialTypes.qll | 18 +- ...rsionBetweenFunctionPointerAndOtherType.ql | 2 +- ...etweenIncompleteTypePointerAndOtherType.ql | 2 +- ...weenObjectPointerAndDifferentObjectType.ql | 2 +- ...ionBetweenPointerToObjectAndIntegerType.ql | 2 +- ...ionFromPointerToVoidIntoPointerToObject.ql | 2 +- ...stBetweenPointerToVoidAndArithmeticType.ql | 2 +- ...nPointerToObjectAndNonIntArithmeticType.ql | 2 +- ...NullNotUsedAsIntegerNullPointerConstant.ql | 2 +- ...ointersToVariablyModifiedArrayTypesUsed.ql | 2 +- .../rules/RULE-2-4/UnusedTagDeclaration.ql | 2 +- .../rules/RULE-2-8/UnusedObjectDefinition.ql | 2 +- .../RULE-2-8/UnusedObjectDefinitionStrict.ql | 2 +- ...veMemcmpArgNotPointersToCompatibleTypes.ql | 2 +- ...ricSelectionDoesntDependOnMacroArgument.ql | 26 ++ .../GenericSelectionNotExpandedFromAMacro.ql | 25 ++ ...ricSelectionNotFromMacroWithSideEffects.ql | 87 ++++ .../GenericWithoutNonDefaultAssociation.ql | 35 ++ .../GenericAssociationWithUnselectableType.ql | 111 +++++ ...rousDefaultSelectionForPointerInGeneric.ql | 78 ++++ ...ricExpressionWithIncorrectEssentialType.ql | 62 +++ .../InvalidGenericMacroArgumentEvaluation.ql | 60 +++ .../DefaultGenericSelectionNotFirstOrLast.ql | 93 ++++ ...interShouldPointToConstTypeWhenPossible.ql | 2 +- .../DeclarationsOfAFunctionSameNameAndType.ql | 18 +- .../DeclarationsOfAnObjectSameNameAndType.ql | 30 +- .../CompatibleDeclarationFunctionDefined.ql | 10 +- .../CompatibleDeclarationObjectDefined.ql | 14 +- .../RULE-2-8/UnusedObjectDefinition.expected | 24 +- .../UnusedObjectDefinitionStrict.expected | 8 +- ...ectionDoesntDependOnMacroArgument.expected | 3 + ...SelectionDoesntDependOnMacroArgument.qlref | 1 + ...ricSelectionNotExpandedFromAMacro.expected | 1 + ...enericSelectionNotExpandedFromAMacro.qlref | 1 + c/misra/test/rules/RULE-23-1/test.c | 25 ++ ...ectionNotFromMacroWithSideEffects.expected | 3 + ...SelectionNotFromMacroWithSideEffects.qlref | 1 + c/misra/test/rules/RULE-23-2/test.c | 49 ++ ...nericWithoutNonDefaultAssociation.expected | 2 + .../GenericWithoutNonDefaultAssociation.qlref | 1 + c/misra/test/rules/RULE-23-3/test.c | 23 + ...icAssociationWithUnselectableType.expected | 13 + ...nericAssociationWithUnselectableType.qlref | 1 + c/misra/test/rules/RULE-23-4/test.c | 73 +++ ...faultSelectionForPointerInGeneric.expected | 84 ++++ ...sDefaultSelectionForPointerInGeneric.qlref | 1 + c/misra/test/rules/RULE-23-5/test.c | 236 ++++++++++ ...ressionWithIncorrectEssentialType.expected | 4 + ...ExpressionWithIncorrectEssentialType.qlref | 1 + c/misra/test/rules/RULE-23-6/test.c | 33 ++ ...lidGenericMacroArgumentEvaluation.expected | 12 + ...nvalidGenericMacroArgumentEvaluation.qlref | 1 + c/misra/test/rules/RULE-23-7/test.c | 71 +++ ...ultGenericSelectionNotFirstOrLast.expected | 4 + ...efaultGenericSelectionNotFirstOrLast.qlref | 1 + c/misra/test/rules/RULE-23-8/test.c | 49 ++ ...rationsOfAFunctionSameNameAndType.expected | 26 +- ...arationsOfAnObjectSameNameAndType.expected | 44 +- c/misra/test/rules/RULE-8-3/function1.c | 2 +- c/misra/test/rules/RULE-8-3/function2.c | 2 +- .../2025-02-25-move-type-related-libraries.md | 2 + ...2-25-update-macro-deduplication-library.md | 4 + ...sential-types-with-explicit-conversions.md | 2 + ...25-03-04-more-accurate-type-comparisons.md | 6 + ...plementationShallComplyWithIeeeStandard.ql | 2 +- .../MoveConstructorUsesCopySemantics.ql | 2 +- .../NonTemplateMemberDefinedInTemplate.ql | 2 +- .../rules/A7-1-2/VariableMissingConstexpr.ql | 2 +- .../A8-4-7/TriviallyCopyableSmallType.qll | 2 +- .../src/rules/M0-1-4/SingleUsePODVariable.qll | 2 +- .../MEM53-CPP/ManuallyManagedLifetime.qll | 2 +- ...ConstructorCallForManuallyManagedObject.ql | 2 +- .../src/codingstandards/cpp/Compatible.qll | 30 -- .../cpp/MatchingParenthesis.qll | 264 +++++++++++ cpp/common/src/codingstandards/cpp/Type.qll | 98 +--- .../DeduplicateMacroResults.qll | 28 +- .../cpp/deadcode/UnusedObjects.qll | 6 +- .../cpp/exclusions/c/Generics.qll | 163 +++++++ .../cpp/exclusions/c/RuleMetadata.qll | 3 + .../CatchExceptionsByLvalueReference.qll | 2 +- ...dPointerToRestrictQualifiedParamShared.qll | 2 +- .../UnusedTypeDeclarations.qll | 2 +- .../codingstandards/cpp/types/Compatible.qll | 421 ++++++++++++++++++ .../src/codingstandards/cpp/types/Graph.qll | 15 + .../cpp/types/LvalueConversion.qll | 38 ++ .../cpp/{ => types}/Pointers.qll | 0 .../cpp/types/SimpleAssignment.qll | 45 ++ .../cpp/{ => types}/TrivialType.qll | 0 .../src/codingstandards/cpp/types/Type.qll | 96 ++++ .../cpp/{TypeUses.qll => types/Uses.qll} | 0 .../cpp/{ => types}/VariablyModifiedTypes.qll | 0 .../DisappliedQuery.ql | 2 +- .../cpp/trivialtypes/LiteralType.ql | 2 +- .../cpp/trivialtypes/TrivialType.ql | 2 +- .../cpp/trivialtypes/TriviallyCopyableType.ql | 2 +- rule_packages/c/Generics.json | 194 ++++++++ 101 files changed, 2846 insertions(+), 263 deletions(-) create mode 100644 c/common/src/codingstandards/c/Generic.qll create mode 100644 c/misra/src/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql create mode 100644 c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql create mode 100644 c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql create mode 100644 c/misra/src/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql create mode 100644 c/misra/src/rules/RULE-23-4/GenericAssociationWithUnselectableType.ql create mode 100644 c/misra/src/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.ql create mode 100644 c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql create mode 100644 c/misra/src/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.ql create mode 100644 c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql create mode 100644 c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.expected create mode 100644 c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.qlref create mode 100644 c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.expected create mode 100644 c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.qlref create mode 100644 c/misra/test/rules/RULE-23-1/test.c create mode 100644 c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.expected create mode 100644 c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.qlref create mode 100644 c/misra/test/rules/RULE-23-2/test.c create mode 100644 c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.expected create mode 100644 c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.qlref create mode 100644 c/misra/test/rules/RULE-23-3/test.c create mode 100644 c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.expected create mode 100644 c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.qlref create mode 100644 c/misra/test/rules/RULE-23-4/test.c create mode 100644 c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.expected create mode 100644 c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.qlref create mode 100644 c/misra/test/rules/RULE-23-5/test.c create mode 100644 c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.expected create mode 100644 c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.qlref create mode 100644 c/misra/test/rules/RULE-23-6/test.c create mode 100644 c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.expected create mode 100644 c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.qlref create mode 100644 c/misra/test/rules/RULE-23-7/test.c create mode 100644 c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.expected create mode 100644 c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.qlref create mode 100644 c/misra/test/rules/RULE-23-8/test.c create mode 100644 change_notes/2025-02-25-move-type-related-libraries.md create mode 100644 change_notes/2025-02-25-update-macro-deduplication-library.md create mode 100644 change_notes/2025-03-04-essential-types-with-explicit-conversions.md create mode 100644 change_notes/2025-03-04-more-accurate-type-comparisons.md delete mode 100644 cpp/common/src/codingstandards/cpp/Compatible.qll create mode 100644 cpp/common/src/codingstandards/cpp/MatchingParenthesis.qll create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/c/Generics.qll create mode 100644 cpp/common/src/codingstandards/cpp/types/Compatible.qll create mode 100644 cpp/common/src/codingstandards/cpp/types/Graph.qll create mode 100644 cpp/common/src/codingstandards/cpp/types/LvalueConversion.qll rename cpp/common/src/codingstandards/cpp/{ => types}/Pointers.qll (100%) create mode 100644 cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll rename cpp/common/src/codingstandards/cpp/{ => types}/TrivialType.qll (100%) create mode 100644 cpp/common/src/codingstandards/cpp/types/Type.qll rename cpp/common/src/codingstandards/cpp/{TypeUses.qll => types/Uses.qll} (100%) rename cpp/common/src/codingstandards/cpp/{ => types}/VariablyModifiedTypes.qll (100%) create mode 100644 rule_packages/c/Generics.json diff --git a/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql b/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql index fd57bd6f75..61dd77f6f4 100644 --- a/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql +++ b/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql @@ -13,7 +13,7 @@ import cpp import codingstandards.c.cert -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers import semmle.code.cpp.dataflow.TaintTracking import ScaledIntegerPointerArithmeticFlow::PathGraph diff --git a/c/cert/src/rules/DCL40-C/IncompatibleFunctionDeclarations.ql b/c/cert/src/rules/DCL40-C/IncompatibleFunctionDeclarations.ql index 20b6e5e59e..95ef0fd682 100644 --- a/c/cert/src/rules/DCL40-C/IncompatibleFunctionDeclarations.ql +++ b/c/cert/src/rules/DCL40-C/IncompatibleFunctionDeclarations.ql @@ -16,7 +16,7 @@ import cpp import codingstandards.c.cert -import codingstandards.cpp.Compatible +import codingstandards.cpp.types.Compatible import ExternalIdentifiers from ExternalIdentifiers d, FunctionDeclarationEntry f1, FunctionDeclarationEntry f2 @@ -29,12 +29,10 @@ where f1.getName() = f2.getName() and ( //return type check - not typesCompatible(f1.getType(), f2.getType()) + not FunctionDeclarationTypeEquivalence::equalReturnTypes(f1, f2) or //parameter type check - parameterTypesIncompatible(f1, f2) - or - not f1.getNumberOfParameters() = f2.getNumberOfParameters() + not FunctionDeclarationTypeEquivalence::equalParameterTypes(f1, f2) ) and // Apply ordering on start line, trying to avoid the optimiser applying this join too early // in the pipeline diff --git a/c/common/src/codingstandards/c/Generic.qll b/c/common/src/codingstandards/c/Generic.qll new file mode 100644 index 0000000000..1bf4282017 --- /dev/null +++ b/c/common/src/codingstandards/c/Generic.qll @@ -0,0 +1,155 @@ +import cpp +import codingstandards.cpp.Macro +import codingstandards.cpp.MatchingParenthesis + +string genericRegexp() { result = ".*_Generic\\s*\\(\\s*(.+),.*" } + +bindingset[input] +string deparenthesize(string input) { + input = "(" + result + ")" and + result = input.substring(1, input.length() - 1) +} + + +class GenericMacro extends Macro { + string ctrlExpr; + + GenericMacro() { ctrlExpr = getBody().regexpCapture(genericRegexp(), 1).trim() } + + string getAParameter() { result = this.(FunctionLikeMacro).getAParameter() } + + string getControllingExprString() { + if exists(string s | s = deparenthesize(ctrlExpr)) + then result = deparenthesize(ctrlExpr).trim() + else result = ctrlExpr + } + + /** + * Whether the controlling expression of the `_Generic` expr in this macro's controlling + * expression refers to one of this macro's parameters. + */ + predicate hasControllingExprFromMacroParameter() { + getControllingExprString().matches(getAParameter()) + } +} + +class GenericMacroString extends string { + GenericMacroString() { this = any(Macro m).getBody() and this.matches("%_Generic%") } +} + +import MatchingParenthesis + +class ParsedGenericMacro extends Macro { + ParsedRoot macroBody; + Parsed genericBody; + string beforeGenericBody; + string afterGenericBody; + + ParsedGenericMacro() { + macroBody.getInputString() = this.getBody() and + exists(ParsedText genericText | + genericText.getText().matches("%_Generic%") and + genericBody = genericText.getParent().getChild(genericText.getChildIdx() + 1) and + genericBody.getRoot() = macroBody + ) and + beforeGenericBody = + textFrom(macroBody.getStartToken(), genericBody.getStartToken().getPrevious()) and + ( + if exists(genericBody.getEndToken().getNext()) + then afterGenericBody = textFrom(genericBody.getEndToken().getNext(), macroBody.getEndToken()) + else afterGenericBody = "" + ) + } + + string getAParameter() { + result = this.(FunctionLikeMacro).getAParameter() + } + + int getAParsedGenericCommaSeparatorOffset() { + exists(ParsedText text | + text.getParent() = genericBody and + result = text.getStartToken().getStartPos() + text.getText().indexOf(",") + ) + } + + int getAParsedGenericColonSeparatorOffset() { + exists(ParsedText text | + text.getParent() = genericBody and + result = text.getStartToken().getStartPos() + text.getText().indexOf(":") + ) + } + + int getParsedGenericCommaSeparatorOffset(int i) { + result = rank[i](int index | index = getAParsedGenericCommaSeparatorOffset()) + } + + bindingset[start, end] + int getParsedGenericColon(int start, int end) { + result = + min(int offset | + offset = getAParsedGenericColonSeparatorOffset() and + offset >= start and + offset <= end + ) + } + + predicate hasParsedFullSelectionRange(int idx, int start, int end) { + idx = 1 and + start = genericBody.getStartToken().getEndPos() and + end = getParsedGenericCommaSeparatorOffset(idx) + or + not exists(getParsedGenericCommaSeparatorOffset(idx)) and + start = getParsedGenericCommaSeparatorOffset(idx - 1) and + end = genericBody.getEndToken().getStartPos() + or + start = getParsedGenericCommaSeparatorOffset(idx - 1) and + end = getParsedGenericCommaSeparatorOffset(idx) + } + + string getSelectionString(int idx) { + exists(int start, int rawStart, int end | + hasParsedFullSelectionRange(idx, rawStart, end) and + ( + if exists(getParsedGenericColon(rawStart, end)) + then start = getParsedGenericColon(rawStart, end) + else start = rawStart + ) and + result = genericBody.getInputString().substring(start, end) + ) + } + + string getControllingExprString() { + result = getSelectionString(1) + } + + bindingset[str, word] + private int countWordInString(string word, string str) { + result = + max(int occurrence | + exists(str.regexpFind("\\b" + word + "\\b", occurrence, _)) or occurrence = -1 + | + occurrence + 1 + ) + } + + int expansionsOutsideExpr(string parameter) { + parameter = getAParameter() and + result = + countWordInString(parameter, beforeGenericBody) + + countWordInString(parameter, afterGenericBody) + } + + int expansionsInsideSelection(string parameter, int idx) { + parameter = getAParameter() and + result = countWordInString(parameter, getSelectionString(idx)) + } + + int expansionsInsideControllingExpr(string parameter) { + result = expansionsInsideSelection(parameter, 1) + } + + int expansionsInsideAssociation(string parameter, int idx) { + not idx = 0 and + result = expansionsInsideSelection(parameter, idx + 1) + } +} \ No newline at end of file diff --git a/c/common/src/codingstandards/c/OutOfBounds.qll b/c/common/src/codingstandards/c/OutOfBounds.qll index 220cf5a0a0..bb7d1bd124 100644 --- a/c/common/src/codingstandards/c/OutOfBounds.qll +++ b/c/common/src/codingstandards/c/OutOfBounds.qll @@ -5,7 +5,7 @@ */ import cpp -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers import codingstandards.c.Variable import codingstandards.cpp.Allocations import codingstandards.cpp.Overflow diff --git a/c/common/src/codingstandards/c/UndefinedBehavior.qll b/c/common/src/codingstandards/c/UndefinedBehavior.qll index 6a72cb6eb7..47461aa613 100644 --- a/c/common/src/codingstandards/c/UndefinedBehavior.qll +++ b/c/common/src/codingstandards/c/UndefinedBehavior.qll @@ -1,5 +1,5 @@ import cpp -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers import codingstandards.cpp.UndefinedBehavior /** diff --git a/c/misra/src/codingstandards/c/misra/EssentialTypes.qll b/c/misra/src/codingstandards/c/misra/EssentialTypes.qll index 02b8498ecb..afdbefdb7d 100644 --- a/c/misra/src/codingstandards/c/misra/EssentialTypes.qll +++ b/c/misra/src/codingstandards/c/misra/EssentialTypes.qll @@ -164,14 +164,14 @@ EssentialTypeCategory getEssentialTypeCategory(Type type) { */ pragma[nomagic] Type getEssentialType(Expr e) { - if e.hasExplicitConversion() - then - if e.getConversion() instanceof ParenthesisExpr - then - if e.getConversion().(ParenthesisExpr).hasExplicitConversion() - then result = e.getConversion().(ParenthesisExpr).getConversion().getType() - else result = e.getConversion().(ParenthesisExpr).getExpr().(EssentialExpr).getEssentialType() - else result = e.getConversion().getType() + if e.hasConversion() + then result = getEssentialTypeOfConversion(e.getFullyConverted()) + else result = e.(EssentialExpr).getEssentialType() +} + +Type getEssentialTypeOfConversion(Expr e) { + if e.(Conversion).isImplicit() or e instanceof ParenthesisExpr or e instanceof C11GenericExpr + then result = getEssentialTypeOfConversion(e.(Conversion).getExpr()) else result = e.(EssentialExpr).getEssentialType() } @@ -446,7 +446,7 @@ class EssentialLiteral extends EssentialExpr, Literal { if underlyingStandardType.(IntType).isSigned() then result = stlr(this) else result = utlr(this) - else result = underlyingStandardType + else result = getStandardType() ) ) } diff --git a/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql b/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql index 36157e130e..7678fc1d23 100644 --- a/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql +++ b/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql @@ -14,7 +14,7 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers from CStyleCast cast, Type type, Type newType where diff --git a/c/misra/src/rules/RULE-11-2/ConversionBetweenIncompleteTypePointerAndOtherType.ql b/c/misra/src/rules/RULE-11-2/ConversionBetweenIncompleteTypePointerAndOtherType.ql index 6c552b0f39..5c16dc1afb 100644 --- a/c/misra/src/rules/RULE-11-2/ConversionBetweenIncompleteTypePointerAndOtherType.ql +++ b/c/misra/src/rules/RULE-11-2/ConversionBetweenIncompleteTypePointerAndOtherType.ql @@ -14,7 +14,7 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers import codingstandards.cpp.Type from Cast cast, Type type, Type newType diff --git a/c/misra/src/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.ql b/c/misra/src/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.ql index 8292bd3b6f..3a6fb28c2a 100644 --- a/c/misra/src/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.ql +++ b/c/misra/src/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.ql @@ -15,7 +15,7 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers from CStyleCast cast, Type baseTypeFrom, Type baseTypeTo where diff --git a/c/misra/src/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.ql b/c/misra/src/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.ql index 8877d04323..336f5d4643 100644 --- a/c/misra/src/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.ql +++ b/c/misra/src/rules/RULE-11-4/ConversionBetweenPointerToObjectAndIntegerType.ql @@ -15,7 +15,7 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.Macro -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers MacroInvocation getAMacroInvocation(CStyleCast cast) { result.getAnExpandedElement() = cast } diff --git a/c/misra/src/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.ql b/c/misra/src/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.ql index 0363c28c19..b316b39a56 100644 --- a/c/misra/src/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.ql +++ b/c/misra/src/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.ql @@ -14,7 +14,7 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers from Cast cast, VoidPointerType type, PointerToObjectType newType where diff --git a/c/misra/src/rules/RULE-11-6/CastBetweenPointerToVoidAndArithmeticType.ql b/c/misra/src/rules/RULE-11-6/CastBetweenPointerToVoidAndArithmeticType.ql index cc0adf0517..2293ede61e 100644 --- a/c/misra/src/rules/RULE-11-6/CastBetweenPointerToVoidAndArithmeticType.ql +++ b/c/misra/src/rules/RULE-11-6/CastBetweenPointerToVoidAndArithmeticType.ql @@ -14,7 +14,7 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers from CStyleCast cast, Type typeFrom, Type typeTo where diff --git a/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql b/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql index e499ea6485..82ac620aa7 100644 --- a/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql +++ b/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql @@ -14,7 +14,7 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers class MisraNonIntegerArithmeticType extends Type { MisraNonIntegerArithmeticType() { diff --git a/c/misra/src/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.ql b/c/misra/src/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.ql index cb18ed0d1d..28b256e85c 100644 --- a/c/misra/src/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.ql +++ b/c/misra/src/rules/RULE-11-9/MacroNullNotUsedAsIntegerNullPointerConstant.ql @@ -13,7 +13,7 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers import codingstandards.cpp.Type from Zero zero, Expr e, string type diff --git a/c/misra/src/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.ql b/c/misra/src/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.ql index 3a99ebd842..dc1433d5e4 100644 --- a/c/misra/src/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.ql +++ b/c/misra/src/rules/RULE-18-10/PointersToVariablyModifiedArrayTypesUsed.ql @@ -16,7 +16,7 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.VariablyModifiedTypes +import codingstandards.cpp.types.VariablyModifiedTypes from VmtDeclarationEntry v, string declstr, string adjuststr, string relationstr where diff --git a/c/misra/src/rules/RULE-2-4/UnusedTagDeclaration.ql b/c/misra/src/rules/RULE-2-4/UnusedTagDeclaration.ql index 08fe2568e9..88b0a5b05a 100644 --- a/c/misra/src/rules/RULE-2-4/UnusedTagDeclaration.ql +++ b/c/misra/src/rules/RULE-2-4/UnusedTagDeclaration.ql @@ -15,7 +15,7 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.TypeUses +import codingstandards.cpp.types.Uses from UserType s where diff --git a/c/misra/src/rules/RULE-2-8/UnusedObjectDefinition.ql b/c/misra/src/rules/RULE-2-8/UnusedObjectDefinition.ql index 2230a74592..13355b7f74 100644 --- a/c/misra/src/rules/RULE-2-8/UnusedObjectDefinition.ql +++ b/c/misra/src/rules/RULE-2-8/UnusedObjectDefinition.ql @@ -20,5 +20,5 @@ from ReportDeadObject report where not isExcluded(report.getPrimaryElement(), DeadCode2Package::unusedObjectDefinitionQuery()) and not report.hasAttrUnused() -select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocation(), +select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocatable(), report.getOptionalPlaceholderMessage() diff --git a/c/misra/src/rules/RULE-2-8/UnusedObjectDefinitionStrict.ql b/c/misra/src/rules/RULE-2-8/UnusedObjectDefinitionStrict.ql index cc117763ee..4eb1ad9773 100644 --- a/c/misra/src/rules/RULE-2-8/UnusedObjectDefinitionStrict.ql +++ b/c/misra/src/rules/RULE-2-8/UnusedObjectDefinitionStrict.ql @@ -22,5 +22,5 @@ from ReportDeadObject report where not isExcluded(report.getPrimaryElement(), DeadCode2Package::unusedObjectDefinitionStrictQuery()) and report.hasAttrUnused() -select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocation(), +select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocatable(), report.getOptionalPlaceholderMessage() diff --git a/c/misra/src/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.ql b/c/misra/src/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.ql index f5d8057b3a..28dce7b638 100644 --- a/c/misra/src/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.ql +++ b/c/misra/src/rules/RULE-21-15/MemcpyMemmoveMemcmpArgNotPointersToCompatibleTypes.ql @@ -13,7 +13,7 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers class MemCmpMoveCpy extends Function { // Couldn't extend BuiltInFunction because it misses `memcmp` diff --git a/c/misra/src/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql b/c/misra/src/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql new file mode 100644 index 0000000000..0dc0e5273a --- /dev/null +++ b/c/misra/src/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql @@ -0,0 +1,26 @@ +/** + * @id c/misra/generic-selection-doesnt-depend-on-macro-argument + * @name RULE-23-1: A generic selection should depend on the type of a macro argument + * @description A generic selection should depend on the type of a macro argument. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-23-1 + * correctness + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Generic + +from ParsedGenericMacro macro, string ctrlExpr +where + not isExcluded(macro, GenericsPackage::genericSelectionDoesntDependOnMacroArgumentQuery()) and + ctrlExpr = macro.getControllingExprString().trim() and + not macro.expansionsInsideControllingExpr(_) > 0 +select macro, + "Generic macro " + macro.getName() + " uses controlling expr " + ctrlExpr + + ", which doesn't match any macro parameter." diff --git a/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql b/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql new file mode 100644 index 0000000000..ef2a2e75c5 --- /dev/null +++ b/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/generic-selection-not-expanded-from-a-macro + * @name RULE-23-1: A generic selection should only be expanded from a macro + * @description A generic selection should only be expanded from a macro. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-23-1 + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +from C11GenericExpr generic, Expr ctrlExpr +where + not isExcluded(generic, GenericsPackage::genericSelectionNotExpandedFromAMacroQuery()) and + ctrlExpr = generic.getControllingExpr() and + not exists(MacroInvocation mi | + mi.getAGeneratedElement() = generic.getExpr() + ) +select generic, "Generic expression with controlling expression $@ is not expanded froma macro", +ctrlExpr, ctrlExpr.toString() diff --git a/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql b/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql new file mode 100644 index 0000000000..3ec53a08bf --- /dev/null +++ b/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql @@ -0,0 +1,87 @@ +/** + * @id c/misra/generic-selection-not-from-macro-with-side-effects + * @name RULE-23-2: A generic selection shall not contain side-effects if it is not expanded from a macro + * @description A generic selection that is not expanded from a macro shall not contain potential + * side effects in the controlling expression. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-23-2 + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Generic +import codingstandards.cpp.SideEffect +import codingstandards.cpp.sideeffect.DefaultEffects +import codingstandards.cpp.alertreporting.DeduplicateMacroResults + +class GenericWithNonMacroSideEffect extends C11GenericExpr { + SideEffect sideEffect; + + GenericWithNonMacroSideEffect() { + not exists(MacroInvocation mi | + mi.getAGeneratedElement() = getExpr() and + mi.getMacro().(GenericMacro).hasControllingExprFromMacroParameter() + ) and + sideEffect = getASideEffect(getControllingExpr()) + } + + SideEffect getASideEffect() { result = sideEffect } +} + +module GenericSideEffectConfig implements DeduplicateMacroConfigSig { + string describe(GenericWithNonMacroSideEffect e) { + result = "side effect '" + e.getASideEffect() + "'" + } +} + +module GenericSideEffectReportConfig implements MacroReportConfigSig { + /* Create a message to describe this macro, with a string describing its `ResultElement`. */ + bindingset[description] + string getMessageSameResultInAllExpansions(Macro m, string description) { + result = + "Generic selection macro " + m.getName() + " contains a " + description + + ", which is not from macro invocation arguments." + } + + /* Create a message to describe this macro, using '$@' to describe an example `ResultElement`. */ + string getMessageVariedResultInAllExpansions(Macro m) { + result = + "Generic selection in macro " + m.getName() + + " contains an invocation-dependent side effect which is not from macro invocation arguments, for example $@." + } + + /** + * Create a message to describe this macro expansion which produces a `ResultElement`, using '$@' + * to describe the relevant macro. + */ + string getMessageResultInIsolatedExpansion(GenericWithNonMacroSideEffect element) { + // A result in an isolated expansion indicates that the side effect is not always present when + // macro is expanded, and therefore the side-effect is not in the macro definition but rather + // originates in one of the macro arguments. + none() + } + + /** + * Create a message to describe a `ResultElement` which is not generated by a macro expansion. + */ + string getMessageNotInMacro( + GenericWithNonMacroSideEffect element, Locatable optLoc1, string optStr1 + ) { + none() + } +} + +import DeduplicateMacroResults as Deduplicate +import Deduplicate::Report as Report + +from Report::ReportResult res +where + not isExcluded(res.getPrimaryElement(), + GenericsPackage::genericSelectionNotFromMacroWithSideEffectsQuery()) +select res.getPrimaryElement(), res.getMessage(), res.getOptionalPlaceholderLocatable(), + res.getOptionalPlaceholderMessage() \ No newline at end of file diff --git a/c/misra/src/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql b/c/misra/src/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql new file mode 100644 index 0000000000..f3a0227022 --- /dev/null +++ b/c/misra/src/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql @@ -0,0 +1,35 @@ +/** + * @id c/misra/generic-without-non-default-association + * @name RULE-23-3: A generic selection should contain at least one non-default association + * @description A generic selection should contain at least one non-default association. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-23-3 + * correctness + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.AlertReporting + +class InvalidGeneric extends C11GenericExpr { + InvalidGeneric() { + not exists(Type t | + t = getAnAssociationType() and + not t instanceof VoidType + ) + } +} + +from C11GenericExpr generic, Element primaryElement +where + not isExcluded(primaryElement, GenericsPackage::genericWithoutNonDefaultAssociationQuery()) and + not exists(Type t | + t = generic.getAnAssociationType() and + not t instanceof VoidType + ) and primaryElement = MacroUnwrapper::unwrapElement(generic) +select primaryElement, "Generic selection contains no non-default association." diff --git a/c/misra/src/rules/RULE-23-4/GenericAssociationWithUnselectableType.ql b/c/misra/src/rules/RULE-23-4/GenericAssociationWithUnselectableType.ql new file mode 100644 index 0000000000..e8ed88f757 --- /dev/null +++ b/c/misra/src/rules/RULE-23-4/GenericAssociationWithUnselectableType.ql @@ -0,0 +1,111 @@ +/** + * @id c/misra/generic-association-with-unselectable-type + * @name RULE-23-4: A generic association shall list an appropriate type + * @description Generic selections undergo lvalue conversion before type comparison, leading to + * certain types being impossible to select. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-23-4 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.types.LvalueConversion +import codingstandards.cpp.types.Graph +import codingstandards.cpp.alertreporting.DeduplicateMacroResults + +/** + * Check if a type contains an unmatchable anonymous struct or union. + * + * Anonymous structs and unions are only equal to themselves. So any anonymous struct, or compound + * type containing an anonymous struct, is unmatchable. + * + * However, there is an exception if the anonymous struct is behind a typedef. All uses of that + * typedef will resolve to the same anonymous struct, and so the typedef is matchable. + */ +predicate containsAnonymousType(Type t) { + t.(Struct).isAnonymous() + or + not t instanceof TypedefType and + exists(Type next | typeGraph(t, next) | containsAnonymousType(next)) +} + +predicate invalidType(Type t, string reason) { + containsAnonymousType(t) and + reason = "containing an anonymous struct or union type" + or + exists(performLvalueConversion(t, reason)) +} + +class InvalidSelection extends Expr { + Type selectionType; + int idx; + C11GenericExpr generic; + string reason; + + InvalidSelection() { + this = generic.getAssociationExpr(idx) and + selectionType = generic.getAssociationType(idx) and + invalidType(selectionType, reason) + } + + Type getSelectionType() { result = selectionType } + + string getReason() { result = reason } +} + +module InvalidSelectionConfig implements DeduplicateMacroConfigSig { + string describe(InvalidSelection e) { + result = "'" + e.getSelectionType().toString() + "', due to " + e.getReason() + } +} + +import InvalidSelectionConfig + +module InvalidSelectionReportConfig implements MacroReportConfigSig { + /* Create a message to describe this macro, with a string describing its `ResultElement`. */ + bindingset[description] + string getMessageSameResultInAllExpansions(Macro m, string description) { + result = "Generic in macro " + m.getName() + " has unselectable type " + description + "." + } + + /* Create a message to describe this macro, using '$@' to describe an example `ResultElement`. */ + string getMessageVariedResultInAllExpansions(Macro m) { + result = + "Generic in macro " + m.getName() + + " has an invocation-dependent unselectable type, for example $@." + } + + /** + * Create a message to describe this macro expansion which produces a `ResultElement`, using '$@' + * to describe the relevant macro. + */ + string getMessageResultInIsolatedExpansion(InvalidSelection element) { + result = + "Generic resulting from invocation of macro $@ contains an unselectable type " + + describe(element) + "." + } + + /** + * Create a message to describe a `ResultElement` which is not generated by a macro expansion. + */ + string getMessageNotInMacro(InvalidSelection element, Locatable optLoc1, string optStr1) { + result = "Generic selection uses unselectable type " + describe(element) + "'." and + optLoc1 = element and + optStr1 = "side effect" + } +} + +import DeduplicateMacroResults as Deduplicate +import Deduplicate::Report as Report + +from Report::ReportResult res +where + not isExcluded(res.getPrimaryElement(), + GenericsPackage::genericSelectionNotFromMacroWithSideEffectsQuery()) +select res.getPrimaryElement(), res.getMessage(), res.getOptionalPlaceholderLocatable(), + res.getOptionalPlaceholderMessage() diff --git a/c/misra/src/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.ql b/c/misra/src/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.ql new file mode 100644 index 0000000000..a009ba1b2a --- /dev/null +++ b/c/misra/src/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.ql @@ -0,0 +1,78 @@ +/** + * @id c/misra/dangerous-default-selection-for-pointer-in-generic + * @name RULE-23-5: A generic selection should not depend on implicit pointer type conversion + * @description Pointer types in a generic selection do not undergo pointer conversions and should + * not counterintuitively fall through to the default association. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-23-5 + * correctness + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.AlertReporting +import codingstandards.cpp.types.Compatible +import codingstandards.cpp.types.LvalueConversion +import codingstandards.cpp.types.SimpleAssignment + +predicate typesCompatible(Type t1, Type t2) { + TypeEquivalence::equalTypes(t1, t2) +} + +class TypeFromGeneric extends Type { + TypeFromGeneric() { + exists(C11GenericExpr g | + ( + this = g.getAssociationType(_) or + this = g.getControllingExpr().getFullyConverted().getType() + ) + ) + } +} + +predicate missesOnPointerConversion(Type provided, Type expected) { + // The provided type is not compatible with the expected type: + not typesCompatible(provided, expected) and + // But 6.5.16.1 simple assignment constraints would have been satisfied: + ( + // Check as if the controlling expr is assigned to the expected type: + SimpleAssignment::satisfiesSimplePointerAssignment(expected, provided) + or + // Since developers typically rely on the compiler to catch const/non-const assignment + // errors, don't assume a const-to-non-const generic selection miss was intentional. + SimpleAssignment::satisfiesSimplePointerAssignment(provided, expected) + ) +} + +from + C11GenericExpr generic, Expr controllingExpr, Type providedType, Type missedType, + Type lvalueConverted, Element extraElement, string extraString, string extraElementName +where + not isExcluded(generic, GenericsPackage::dangerousDefaultSelectionForPointerInGenericQuery()) and + controllingExpr = generic.getControllingExpr() and + providedType = generic.getControllingExpr().getFullyConverted().getType() and + // The controlling expression undergoes lvalue conversion: + lvalueConverted = getLvalueConverted(providedType) and + // There is no perfect match + not typesCompatible(lvalueConverted, generic.getAnAssociationType()) and + // There is a default selector. + exists(VoidType default | default = generic.getAnAssociationType()) and + missedType = generic.getAnAssociationType() and + missesOnPointerConversion(lvalueConverted, missedType) and + extraElement = MacroUnwrapper::unwrapElement(generic) and + ( + if extraElement instanceof Macro + then ( + extraString = " in generic macro $@" and extraElementName = extraElement.(Macro).getName() + ) else ( + extraString = "" and extraElementName = "" + ) + ) +select generic, + "Generic matched default selection, as controlling argument type " + lvalueConverted.toString() + + " does not undergo pointer conversion to " + missedType.toString() + extraString + ".", + extraElement, extraElementName diff --git a/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql b/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql new file mode 100644 index 0000000000..c27ce3dc55 --- /dev/null +++ b/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql @@ -0,0 +1,62 @@ +/** + * @id c/misra/generic-expression-with-incorrect-essential-type + * @name RULE-23-6: The controlling expression of a generic selection shall have an essential type that matches its standard type + * @description The controlling expression of a generic selection shall have an essential type that + * matches its standard type. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-23-6 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes +import codingstandards.cpp.Cpp14Literal +import codingstandards.cpp.AlertReporting + +predicate allowedByException(Expr expr, Type essentialType) { + // Constant expressions + exists(expr.getValue()) and + ( + // with essentially signed or unsigned type + getEssentialTypeCategory(essentialType) = EssentiallySignedType() + or + getEssentialTypeCategory(essentialType) = EssentiallyUnsignedType() + ) and + // with lower rank than `int` + essentialType.getSize() < any(IntType t).getSize() and + // and not a character constant + not expr instanceof Cpp14Literal::CharLiteral +} + +from + C11GenericExpr generic, Expr ctrlExpr, Type ctrlType, Type ctrlEssentialType, + Element extraElement, string extraString, string extraMessage +where + not isExcluded(ctrlExpr, GenericsPackage::genericExpressionWithIncorrectEssentialTypeQuery()) and + ctrlExpr = generic.getControllingExpr() and + ctrlType = ctrlExpr.getFullyConverted().getType() and + ctrlEssentialType = getEssentialType(ctrlExpr) and + // Exclude lvalue conversion on const structs + exists(getEssentialTypeCategory(ctrlEssentialType)) and + ( + not ctrlEssentialType = ctrlType + or + getEssentialTypeCategory(ctrlEssentialType) = EssentiallyEnumType() + ) and + not allowedByException(ctrlExpr, ctrlEssentialType) and + extraElement = MacroUnwrapper::unwrapElement(generic) and + ( + if extraElement instanceof Macro + then ( + extraMessage = "macro $@ " and extraString = extraElement.(Macro).getName() + ) else ( + extraMessage = "" and extraString = "" + ) + ) +select generic, + "Controlling expression in generic " + extraMessage + + "has standard type " + ctrlType.toString() + ", which doesn't match its essential type " + + ctrlEssentialType.toString() + ".", extraElement, extraString \ No newline at end of file diff --git a/c/misra/src/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.ql b/c/misra/src/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.ql new file mode 100644 index 0000000000..04952ae960 --- /dev/null +++ b/c/misra/src/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.ql @@ -0,0 +1,60 @@ +/** + * @id c/misra/invalid-generic-macro-argument-evaluation + * @name RULE-23-7: A generic selection that is expanded from a macro should evaluate its argument only once + * @description A generic selection that is expanded from a macro should evaluate its argument only + * once. + * @kind problem + * @precision medium + * @problem.severity warning + * @tags external/misra/id/rule-23-7 + * correctness + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.Generic + +predicate allowedByException(string parameter, ParsedGenericMacro genericMacro) { + genericMacro.expansionsOutsideExpr(parameter) = 0 and + not genericMacro.expansionsInsideAssociation(parameter, _) > 0 and + forall(MacroInvocation mi, C11GenericExpr expr | + mi.getMacro() = genericMacro and + mi.getAGeneratedElement() = expr + | + forall(Expr assoc | assoc = expr.getAnAssociationExpr() | exists(assoc.getValue())) + ) +} + +from ParsedGenericMacro genericMacro, string parameter, string reason +where + not isExcluded(genericMacro, GenericsPackage::invalidGenericMacroArgumentEvaluationQuery()) and + parameter = genericMacro.getAParameter() and + genericMacro.expansionsInsideControllingExpr(parameter) > 0 and + ( + genericMacro.expansionsOutsideExpr(parameter) > 1 and + reason = "expanded multiple times outside the generic selection" + or + genericMacro.expansionsOutsideExpr(parameter) = 1 and + genericMacro.expansionsInsideAssociation(parameter, _) > 0 and + reason = "expanded outside the generic selection and inside the generic selection" + or + genericMacro.expansionsOutsideExpr(parameter) = 0 and + exists(int i | + genericMacro.expansionsInsideAssociation(parameter, i) > 1 and + reason = "expanded in generic selection " + i.toString() + " more than once" + ) + or + genericMacro.expansionsOutsideExpr(parameter) = 0 and + exists(int i | + genericMacro.expansionsInsideAssociation(parameter, i) = 0 and + reason = "not expanded in generic selection " + i.toString() + ) and + not allowedByException(parameter, genericMacro) + ) and + not genericMacro.getBody().matches(["%sizeof%", "%__alignof%", "%typeof%", "%offsetof%"]) +select genericMacro, + "Generic macro " + genericMacro.getName() + " may have unexpected behavior from side effects " + + "in parameter " + parameter + ", as it is " + reason + "." diff --git a/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql b/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql new file mode 100644 index 0000000000..04b12ac436 --- /dev/null +++ b/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql @@ -0,0 +1,93 @@ +/** + * @id c/misra/default-generic-selection-not-first-or-last + * @name RULE-23-8: A default association shall appear as either the first or the last association of a generic + * @description A default association shall appear as either the first or the last association of a + * generic selection + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-23-8 + * maintainability + * external/misra/c/2012/amendment3 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.alertreporting.DeduplicateMacroResults + +class GenericWithMisplacedDefault extends C11GenericExpr { + int defaultIdx; + + GenericWithMisplacedDefault() { + getAssociationType(defaultIdx) instanceof VoidType and + not defaultIdx = 0 and + not defaultIdx = max(int i | exists(getAssociationType(i))) + } + + int getDefaultIdx() { result = defaultIdx } +} + +module GenericWithMisplacedDefaultConfig implements + DeduplicateMacroConfigSig +{ + string describe(GenericWithMisplacedDefault e) { + exists(int i | i = e.getDefaultIdx() + 1 | + i = 1 and result = "1st" + or + i = 2 and result = "2nd" + or + i = 3 and result = "3rd" + or + i > 3 and result = i.toString() + "th" + ) + } +} + +import GenericWithMisplacedDefaultConfig + +module GenericMisplacedDefaultReportConfig implements + MacroReportConfigSig +{ + /* Create a message to describe this macro, with a string describing its `ResultElement`. */ + bindingset[description] + string getMessageSameResultInAllExpansions(Macro m, string description) { + result = + "Generic macro " + m.getName() + " has default as " + description + " association, which is not first or last." + } + + /* Create a message to describe this macro, using '$@' to describe an example `ResultElement`. */ + string getMessageVariedResultInAllExpansions(Macro m) { + result = + "Generic macro " + m.getName() + " has a default association which is not first or last, for example $@." + } + + /** + * Create a message to describe this macro expansion which produces a `ResultElement`, using '$@' + * to describe the relevant macro. + */ + string getMessageResultInIsolatedExpansion(GenericWithMisplacedDefault element) { + result = + "Generic macro $@, in this expansion, has default as " + describe(element) + " association, which is not first or last." + } + + /** + * Create a message to describe a `ResultElement` which is not generated by a macro expansion. + */ + string getMessageNotInMacro(GenericWithMisplacedDefault element, Locatable optLoc1, string optStr1) { + result = + "Generic has default as " + describe(element) + " association, which is not first or last." and + optLoc1 = element and + optStr1 = "" + } +} + +import DeduplicateMacroResults as Deduplicate +import Deduplicate::Report as Report + +from Report::ReportResult res +where + not isExcluded(res.getPrimaryElement(), + GenericsPackage::defaultGenericSelectionNotFirstOrLastQuery()) +select res.getPrimaryElement(), res.getMessage(), res.getOptionalPlaceholderLocatable(), + res.getOptionalPlaceholderMessage() diff --git a/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql b/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql index ddb8cbcdcc..6a2c123907 100644 --- a/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql +++ b/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql @@ -16,7 +16,7 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.Pointers +import codingstandards.cpp.types.Pointers import codingstandards.cpp.SideEffect import codingstandards.cpp.alertreporting.HoldsForAllCopies diff --git a/c/misra/src/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql b/c/misra/src/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql index 8c80c64a40..a0d7c0c9ab 100644 --- a/c/misra/src/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql +++ b/c/misra/src/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql @@ -14,9 +14,9 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.Compatible +import codingstandards.cpp.types.Compatible -from FunctionDeclarationEntry f1, FunctionDeclarationEntry f2, string case +from FunctionDeclarationEntry f1, FunctionDeclarationEntry f2, string case, string pluralDo where not isExcluded(f1, Declarations4Package::declarationsOfAFunctionSameNameAndTypeQuery()) and not isExcluded(f2, Declarations4Package::declarationsOfAFunctionSameNameAndTypeQuery()) and @@ -24,16 +24,16 @@ where f1.getDeclaration() = f2.getDeclaration() and //return type check ( - not typesCompatible(f1.getType(), f2.getType()) and - case = "return type" + not FunctionDeclarationTypeEquivalence::equalReturnTypes(f1, f2) and + case = "return type" and pluralDo = "does" or //parameter type check - parameterTypesIncompatible(f1, f2) and - case = "parameter types" + not FunctionDeclarationTypeEquivalence::equalParameterTypes(f1, f2) and + case = "parameter types" and pluralDo = "do" or //parameter name check - parameterNamesIncompatible(f1, f2) and - case = "parameter names" + parameterNamesUnmatched(f1, f2) and + case = "parameter names" and pluralDo = "do" ) -select f1, "The " + case + " of re-declaration of $@ is not compatible with declaration $@", f1, +select f1, "The " + case + " of re-declaration of $@ " + pluralDo + " not use the same type names as declaration $@", f1, f1.getName(), f2, f2.getName() diff --git a/c/misra/src/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql b/c/misra/src/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql index 421998c582..83c67e2efa 100644 --- a/c/misra/src/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql +++ b/c/misra/src/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql @@ -14,12 +14,18 @@ import cpp import codingstandards.c.misra -import codingstandards.cpp.Compatible +import codingstandards.cpp.types.Compatible -from VariableDeclarationEntry decl1, VariableDeclarationEntry decl2 -where - not isExcluded(decl1, Declarations4Package::declarationsOfAnObjectSameNameAndTypeQuery()) and - not isExcluded(decl2, Declarations4Package::declarationsOfAnObjectSameNameAndTypeQuery()) and +class RelevantType extends Type { + RelevantType() { + exists(VariableDeclarationEntry decl | + (relevantPair(decl, _) or relevantPair(_, decl)) and + decl.getType() = this + ) + } +} + +predicate relevantPair(VariableDeclarationEntry decl1, VariableDeclarationEntry decl2) { not decl1 = decl2 and not decl1.getVariable().getDeclaringType().isAnonymous() and // Declarations are for the same qualified name @@ -34,9 +40,17 @@ where or decl1.getVariable().(Field).getDeclaringType().(Class).getALinkTarget() = decl2.getVariable().(Field).getDeclaringType().(Class).getALinkTarget() - ) and - not typesCompatible(decl1.getType(), decl2.getType()) + ) +} + +from VariableDeclarationEntry decl1, VariableDeclarationEntry decl2 +where + not isExcluded(decl1, Declarations4Package::declarationsOfAnObjectSameNameAndTypeQuery()) and + not isExcluded(decl2, Declarations4Package::declarationsOfAnObjectSameNameAndTypeQuery()) and + relevantPair(decl1, decl2) and + not TypeEquivalence::equalTypes(decl1.getType(), + decl2.getType()) select decl1, "The object $@ of type " + decl1.getType().toString() + - " is not compatible with re-declaration $@ of type " + decl2.getType().toString(), decl1, + " does not use the same type names as re-declaration $@ of type " + decl2.getType().toString(), decl1, decl1.getName(), decl2, decl2.getName() diff --git a/c/misra/src/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.ql b/c/misra/src/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.ql index 63f70d3541..98876ad1bd 100644 --- a/c/misra/src/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.ql +++ b/c/misra/src/rules/RULE-8-4/CompatibleDeclarationFunctionDefined.ql @@ -17,7 +17,7 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.Identifiers -import codingstandards.cpp.Compatible +import codingstandards.cpp.types.Compatible from FunctionDeclarationEntry f1 where @@ -36,15 +36,15 @@ where f1.getName() = f2.getName() and not f2.isDefinition() and f2.getDeclaration() = f1.getDeclaration() and - //return types differ ( - not typesCompatible(f1.getType(), f2.getType()) + //return types differ + not FunctionDeclarationTypeEquivalence::equalReturnTypes(f1, f2) or //parameter types differ - parameterTypesIncompatible(f1, f2) + not FunctionDeclarationTypeEquivalence::equalParameterTypes(f1, f2) or //parameter names differ - parameterNamesIncompatible(f1, f2) + parameterNamesUnmatched(f1, f2) ) ) ) diff --git a/c/misra/src/rules/RULE-8-4/CompatibleDeclarationObjectDefined.ql b/c/misra/src/rules/RULE-8-4/CompatibleDeclarationObjectDefined.ql index 7e5baacd9a..613ce56806 100644 --- a/c/misra/src/rules/RULE-8-4/CompatibleDeclarationObjectDefined.ql +++ b/c/misra/src/rules/RULE-8-4/CompatibleDeclarationObjectDefined.ql @@ -17,7 +17,16 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.Identifiers -import codingstandards.cpp.Compatible +import codingstandards.cpp.types.Compatible + +class RelevantType extends Type { + RelevantType() { + exists(VariableDeclarationEntry decl | + count(VariableDeclarationEntry others | others.getDeclaration() = decl.getDeclaration()) > 1 and + decl.getType() = this + ) + } +} from VariableDeclarationEntry decl1 where @@ -28,6 +37,7 @@ where not exists(VariableDeclarationEntry decl2 | not decl2.isDefinition() and decl1.getDeclaration() = decl2.getDeclaration() and - typesCompatible(decl1.getType(), decl2.getType()) + TypeEquivalence::equalTypes(decl1.getType(), + decl2.getType()) ) select decl1, "No separate compatible declaration found for this definition." diff --git a/c/misra/test/rules/RULE-2-8/UnusedObjectDefinition.expected b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinition.expected index 731aebb1be..9a373a644c 100644 --- a/c/misra/test/rules/RULE-2-8/UnusedObjectDefinition.expected +++ b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinition.expected @@ -1,12 +1,12 @@ -| test.c:6:5:6:6 | definition of g2 | Unused object 'g2'. | test.c:6:5:6:6 | test.c:6:5:6:6 | (ignored) | -| test.c:9:5:9:6 | definition of g3 | Unused object 'g3'. | test.c:9:5:9:6 | test.c:9:5:9:6 | (ignored) | -| test.c:20:7:20:8 | definition of l2 | Unused object 'l2'. | test.c:20:7:20:8 | test.c:20:7:20:8 | (ignored) | -| test.c:27:7:27:8 | definition of l5 | Unused object 'l5'. | test.c:27:7:27:8 | test.c:27:7:27:8 | (ignored) | -| test.c:37:10:37:11 | definition of g5 | Unused object 'g5'. | test.c:37:10:37:11 | test.c:37:10:37:11 | (ignored) | -| test.c:45:9:45:10 | definition of g6 | Unused object 'g6'. | test.c:45:9:45:10 | test.c:45:9:45:10 | (ignored) | -| test.c:51:5:51:6 | definition of g7 | Unused object 'g7'. | test.c:51:5:51:6 | test.c:51:5:51:6 | (ignored) | -| test.c:64:3:64:18 | ONLY_DEF_VAR(x) | Invocation of macro '$@' defines unused object 'l2'. | test.c:60:1:60:34 | test.c:60:1:60:34 | ONLY_DEF_VAR | -| test.c:68:1:71:5 | #define ALSO_DEF_VAR(x) int x = 0; while (1) ; | Macro 'ALSO_DEF_VAR' defines unused object with an invocation-dependent name, for example, '$@'. | test.c:73:16:73:17 | test.c:73:16:73:17 | l1 | -| test.c:77:1:82:3 | #define DEF_UNUSED_INNER_VAR() { int _v = 0; while (1) ; } | Macro 'DEF_UNUSED_INNER_VAR' defines unused object '_v'. | test.c:77:1:82:3 | test.c:77:1:82:3 | (ignored) | -| test.c:119:11:119:13 | definition of g10 | Unused object 'g10'. | test.c:119:11:119:13 | test.c:119:11:119:13 | (ignored) | -| test.c:124:13:124:14 | definition of l2 | Unused object 'l2'. | test.c:124:13:124:14 | test.c:124:13:124:14 | (ignored) | +| test.c:6:5:6:6 | definition of g2 | Unused object 'g2'. | test.c:6:5:6:6 | definition of g2 | (ignored) | +| test.c:9:5:9:6 | definition of g3 | Unused object 'g3'. | test.c:9:5:9:6 | definition of g3 | (ignored) | +| test.c:20:7:20:8 | definition of l2 | Unused object 'l2'. | test.c:20:7:20:8 | definition of l2 | (ignored) | +| test.c:27:7:27:8 | definition of l5 | Unused object 'l5'. | test.c:27:7:27:8 | definition of l5 | (ignored) | +| test.c:37:10:37:11 | definition of g5 | Unused object 'g5'. | test.c:37:10:37:11 | definition of g5 | (ignored) | +| test.c:45:9:45:10 | definition of g6 | Unused object 'g6'. | test.c:45:9:45:10 | definition of g6 | (ignored) | +| test.c:51:5:51:6 | definition of g7 | Unused object 'g7'. | test.c:51:5:51:6 | definition of g7 | (ignored) | +| test.c:64:3:64:18 | ONLY_DEF_VAR(x) | Invocation of macro '$@' defines unused object 'l2'. | test.c:60:1:60:34 | #define ONLY_DEF_VAR(x) int x = 0; | ONLY_DEF_VAR | +| test.c:68:1:71:5 | #define ALSO_DEF_VAR(x) int x = 0; while (1) ; | Macro 'ALSO_DEF_VAR' defines unused object with an invocation-dependent name, for example, '$@'. | test.c:73:16:73:17 | definition of l1 | l1 | +| test.c:77:1:82:3 | #define DEF_UNUSED_INNER_VAR() { int _v = 0; while (1) ; } | Macro 'DEF_UNUSED_INNER_VAR' defines unused object '_v'. | test.c:77:1:82:3 | #define DEF_UNUSED_INNER_VAR() { int _v = 0; while (1) ; } | (ignored) | +| test.c:119:11:119:13 | definition of g10 | Unused object 'g10'. | test.c:119:11:119:13 | definition of g10 | (ignored) | +| test.c:124:13:124:14 | definition of l2 | Unused object 'l2'. | test.c:124:13:124:14 | definition of l2 | (ignored) | diff --git a/c/misra/test/rules/RULE-2-8/UnusedObjectDefinitionStrict.expected b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinitionStrict.expected index cf3c0b64e1..fa191e5d68 100644 --- a/c/misra/test/rules/RULE-2-8/UnusedObjectDefinitionStrict.expected +++ b/c/misra/test/rules/RULE-2-8/UnusedObjectDefinitionStrict.expected @@ -1,4 +1,4 @@ -| test.c:87:29:87:30 | definition of g8 | Unused object 'g8'. | test.c:87:29:87:30 | test.c:87:29:87:30 | (ignored) | -| test.c:92:3:92:30 | ONLY_DEF_ATTR_UNUSED_VAR(x) | Invocation of macro '$@' defines unused object 'l2'. | test.c:88:1:88:70 | test.c:88:1:88:70 | ONLY_DEF_ATTR_UNUSED_VAR | -| test.c:96:1:99:5 | #define ALSO_DEF_ATTR_UNUSED_VAR(x) __attribute__((unused)) int x = 0; while (1) ; | Macro 'ALSO_DEF_ATTR_UNUSED_VAR' defines unused object with an invocation-dependent name, for example, '$@'. | test.c:101:28:101:29 | test.c:101:28:101:29 | l1 | -| test.c:106:1:111:3 | #define DEF_ATTR_UNUSED_INNER_VAR() { __attribute__((unused)) int _v = 0; while (1) ; } | Macro 'DEF_ATTR_UNUSED_INNER_VAR' defines unused object '_v'. | test.c:106:1:111:3 | test.c:106:1:111:3 | (ignored) | +| test.c:87:29:87:30 | definition of g8 | Unused object 'g8'. | test.c:87:29:87:30 | definition of g8 | (ignored) | +| test.c:92:3:92:30 | ONLY_DEF_ATTR_UNUSED_VAR(x) | Invocation of macro '$@' defines unused object 'l2'. | test.c:88:1:88:70 | #define ONLY_DEF_ATTR_UNUSED_VAR(x) __attribute__((unused)) int x = 0; | ONLY_DEF_ATTR_UNUSED_VAR | +| test.c:96:1:99:5 | #define ALSO_DEF_ATTR_UNUSED_VAR(x) __attribute__((unused)) int x = 0; while (1) ; | Macro 'ALSO_DEF_ATTR_UNUSED_VAR' defines unused object with an invocation-dependent name, for example, '$@'. | test.c:101:28:101:29 | definition of l1 | l1 | +| test.c:106:1:111:3 | #define DEF_ATTR_UNUSED_INNER_VAR() { __attribute__((unused)) int _v = 0; while (1) ; } | Macro 'DEF_ATTR_UNUSED_INNER_VAR' defines unused object '_v'. | test.c:106:1:111:3 | #define DEF_ATTR_UNUSED_INNER_VAR() { __attribute__((unused)) int _v = 0; while (1) ; } | (ignored) | diff --git a/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.expected b/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.expected new file mode 100644 index 0000000000..2534e47012 --- /dev/null +++ b/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.expected @@ -0,0 +1,3 @@ +| test.c:2:1:2:30 | #define M1 _Generic(1, int: 1) | Generic macro M1 uses controlling expr 1, which doesn't match any macro parameter. | +| test.c:4:1:4:33 | #define M2(X) _Generic(1, int: X) | Generic macro M2 uses controlling expr 1, which doesn't match any macro parameter. | +| test.c:18:1:18:38 | #define M9(X) g(_Generic((Y), int: 1)) | Generic macro M9 uses controlling expr (Y), which doesn't match any macro parameter. | diff --git a/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.qlref b/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.qlref new file mode 100644 index 0000000000..1ca5f792fa --- /dev/null +++ b/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.qlref @@ -0,0 +1 @@ +rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.expected b/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.expected new file mode 100644 index 0000000000..476a9320b8 --- /dev/null +++ b/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.expected @@ -0,0 +1 @@ +| test.c:21:3:21:21 | _Generic | Generic expression with controlling expression $@ is not expanded froma macro | test.c:21:12:21:12 | 1 | 1 | diff --git a/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.qlref b/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.qlref new file mode 100644 index 0000000000..59fae02b7f --- /dev/null +++ b/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.qlref @@ -0,0 +1 @@ +rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-1/test.c b/c/misra/test/rules/RULE-23-1/test.c new file mode 100644 index 0000000000..f27541dd38 --- /dev/null +++ b/c/misra/test/rules/RULE-23-1/test.c @@ -0,0 +1,25 @@ +// NON_COMPLIANT: +#define M1 _Generic(1, int: 1) +// NON_COMPLIANT: +#define M2(X) _Generic(1, int: X) +// COMPLIANT: +#define M3(X) _Generic((X), int: 1) +// COMPLIANT: +#define M4(X) _Generic((X), int: 1) +// COMPLIANT: +#define M5(X) _Generic((X + X), int: 1) +int f1(int a, int b); +// COMPLIANT: +#define M6(X) _Generic(f(1, (X)), int: 1) +#define M7(X) 1 + _Generic((X), int: 1) +// COMPLIANT: +#define M8(X) g(_Generic((X), int: 1)) +// NON_COMPLIANT: +#define M9(X) g(_Generic((Y), int: 1)) + +void f2() { + _Generic(1, int: 1); // NON_COMPLIANT + M1; // NON_COMPLIANT + M2(1); // NON_COMPLIANT + M3(1); // COMPLIANT +} diff --git a/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.expected b/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.expected new file mode 100644 index 0000000000..1abcb4f2bb --- /dev/null +++ b/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.expected @@ -0,0 +1,3 @@ +| test.c:4:1:4:37 | #define M2(X) _Generic((X)++, int: 1) | Generic selection macro M2 contains a side effect '... ++', which is not from macro invocation arguments. | test.c:4:1:4:37 | #define M2(X) _Generic((X)++, int: 1) | (ignored) | +| test.c:7:1:7:38 | #define M3(X) _Generic(l1++, int: (X)) | Generic selection macro M3 contains a side effect '... ++', which is not from macro invocation arguments. | test.c:7:1:7:38 | #define M3(X) _Generic(l1++, int: (X)) | (ignored) | +| test.c:42:1:44:24 | #define M5(X) static volatile l ## X; _Generic(l ## X, int: 1) | Generic selection in macro M5 contains an invocation-dependent side effect which is not from macro invocation arguments, for example $@. | test.c:47:3:47:7 | _Generic | side effect 'la' | diff --git a/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.qlref b/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.qlref new file mode 100644 index 0000000000..bb3e39a58c --- /dev/null +++ b/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.qlref @@ -0,0 +1 @@ +rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-2/test.c b/c/misra/test/rules/RULE-23-2/test.c new file mode 100644 index 0000000000..6a25e15189 --- /dev/null +++ b/c/misra/test/rules/RULE-23-2/test.c @@ -0,0 +1,49 @@ +#define M1(X) _Generic((X), int: 1) + +// NON_COMPLIANT: +#define M2(X) _Generic((X)++, int: 1) + +// NON_COMPLIANT: +#define M3(X) _Generic(l1++, int: (X)) + +// COMPLIANT: +#define M3_WRAPPER(X) M3(X) + +#define M4(X) _Generic((X)(), int: 1) + +void f1() { + int l1; + + _Generic(1, int: 1); // COMPLIANT + M1(1); // COMPLIANT + _Generic(l1, int: 1); // COMPLIANT + M1(l1); // COMPLIANT + + _Generic(l1++, + int: 1); // COMPLIANT: side effect is not from a macro argument. + M1(l1++); // COMPLIANT + M2(l1); // NON-COMPLIANT: at macro definition + M3(1); // NON-COMPLIANT: at macro definition + M3_WRAPPER(1); // NON-COMPLIANT: at definition of M3 +} + +int g1; +int pure() { return g1; } + +int impure() { return g1++; } + +void f2() { + M1(pure()); // COMPLIANT + M1(impure()); // COMPLIANT + M4(pure); // COMPLIANT + M4(impure); // NON_COMPLIANT[False negative] +} + +#define M5(X) \ + static volatile l##X; \ + _Generic(l##X, int: 1) + +void f3() { + M5(a); // NON-COMPLIANT + M5(b); // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.expected b/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.expected new file mode 100644 index 0000000000..50d6277e84 --- /dev/null +++ b/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.expected @@ -0,0 +1,2 @@ +| test.c:2:1:2:35 | #define M1 _Generic(1, default: 1); | Generic selection contains no non-default association. | +| test.c:14:3:14:25 | _Generic | Generic selection contains no non-default association. | diff --git a/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.qlref b/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.qlref new file mode 100644 index 0000000000..b44de9083e --- /dev/null +++ b/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.qlref @@ -0,0 +1 @@ +rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-3/test.c b/c/misra/test/rules/RULE-23-3/test.c new file mode 100644 index 0000000000..d3f093f242 --- /dev/null +++ b/c/misra/test/rules/RULE-23-3/test.c @@ -0,0 +1,23 @@ +// NON-COMPLIANT +#define M1 _Generic(1, default: 1); +// COMPLIANT +#define M2 _Generic(1, int: 1); +// COMPLIANT +#define M3 _Generic(1, int: 1, default: 1); +// COMPLIANT +#define M4 _Generic(1, int: 1, long: 1); + +void f() { + // Invalid generics: + // _Generic(1); + // _Generic(1, void: 1); + _Generic(1, default: 1); // NON-COMPLIANT + _Generic(1, int: 1); // COMPLIANT + _Generic(1, int: 1, default: 1); // COMPLIANT + _Generic(1, int: 1, long: 1); // COMPLIANT + + M1; + M2; + M3; + M4; +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.expected b/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.expected new file mode 100644 index 0000000000..27030fc768 --- /dev/null +++ b/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.expected @@ -0,0 +1,13 @@ +| test.c:11:18:11:18 | 1 | Generic selection uses unselectable type 'const int', due to qualifiers removed'. | test.c:11:18:11:18 | 1 | side effect | +| test.c:12:21:12:21 | 1 | Generic selection uses unselectable type 'volatile int', due to qualifiers removed'. | test.c:12:21:12:21 | 1 | side effect | +| test.c:13:20:13:20 | 1 | Generic selection uses unselectable type '_Atomic(int)', due to qualifiers removed'. | test.c:13:20:13:20 | 1 | side effect | +| test.c:16:27:16:27 | 1 | Generic selection uses unselectable type 'const volatile int', due to qualifiers removed'. | test.c:16:27:16:27 | 1 | side effect | +| test.c:18:18:18:18 | 1 | Generic selection uses unselectable type '(unnamed class/struct/union)', due to containing an anonymous struct or union type'. | test.c:18:18:18:18 | 1 | side effect | +| test.c:19:20:19:20 | 1 | Generic selection uses unselectable type 'struct *', due to containing an anonymous struct or union type'. | test.c:19:20:19:20 | 1 | side effect | +| test.c:24:17:24:17 | 1 | Generic selection uses unselectable type '(unnamed class/struct/union)', due to containing an anonymous struct or union type'. | test.c:24:17:24:17 | 1 | side effect | +| test.c:25:19:25:19 | 1 | Generic selection uses unselectable type 'union *', due to containing an anonymous struct or union type'. | test.c:25:19:25:19 | 1 | side effect | +| test.c:31:15:31:15 | 1 | Generic selection uses unselectable type 'int[3]', due to array-to-pointer decay'. | test.c:31:15:31:15 | 1 | side effect | +| test.c:40:1:40:53 | #define M1(X) _Generic((X), const int: 1, default: 0) | Generic in macro M1 has unselectable type 'const int', due to qualifiers removed. | test.c:40:1:40:53 | #define M1(X) _Generic((X), const int: 1, default: 0) | (ignored) | +| test.c:42:1:42:46 | #define M2(X) _Generic(1, X[3]: 1, default: 0) | Generic in macro M2 has an invocation-dependent unselectable type, for example $@. | test.c:49:3:49:10 | 1 | 'char[3]', due to array-to-pointer decay | +| test.c:52:3:52:15 | M3(X) | Generic resulting from invocation of macro $@ contains an unselectable type 'const int', due to qualifiers removed. | test.c:44:1:44:43 | #define M3(X) _Generic(1, X: 1, default: 0) | M3 | +| test.c:64:18:64:18 | 1 | Generic selection uses unselectable type 'const_int', due to qualifiers removed'. | test.c:64:18:64:18 | 1 | side effect | diff --git a/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.qlref b/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.qlref new file mode 100644 index 0000000000..1214be7ce2 --- /dev/null +++ b/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.qlref @@ -0,0 +1 @@ +rules/RULE-23-4/GenericAssociationWithUnselectableType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-4/test.c b/c/misra/test/rules/RULE-23-4/test.c new file mode 100644 index 0000000000..135ab4d0f7 --- /dev/null +++ b/c/misra/test/rules/RULE-23-4/test.c @@ -0,0 +1,73 @@ +typedef struct { +} empty_struct_t; +struct empty_struct {}; +typedef union { +} empty_union_t; +union empty_union {}; + +void f() { + _Generic(1, + int: 1, // COMPLIANT + const int: 1, // NON-COMPLIANT + volatile int: 1, // NON-COMPLIANT + _Atomic int: 1, // NON-COMPLIANT + int *: 1, // COMPLIANT + int const *: 1, // COMPLIANT + const volatile int: 1, // NON-COMPLIANT + int volatile const *: 1, // COMPLIANT + struct {}: 1, // NON-COMPLIANT + struct {} *: 1, // NON-COMPLIANT + empty_struct_t: 1, // COMPLIANT + struct empty_struct: 1, // COMPLIANT + empty_struct_t *: 1, // COMPLIANT + struct empty_struct *: 1, // COMPLIANT + union {}: 1, // NON-COMPLIANT + union {} *: 1, // NON-COMPLIANT + empty_union_t: 1, // COMPLIANT + union empty_union: 1, // COMPLIANT + empty_union_t *: 1, // COMPLIANT + union empty_union *: 1, // COMPLIANT + // int[]: 1, // compile error + int[3]: 1, // NON-COMPLIANT + int(*)[3]: 1, // COMPLIANT: pointer to array OK + // int (int*): 1, // compile error + int (*)(int *): 1, // COMPLIANT: function pointers OK + default: 1 // COMPLIANT + ); +} + +// NON-COMPLIANT +#define M1(X) _Generic((X), const int: 1, default: 0) +// NON-COMPLIANT +#define M2(X) _Generic(1, X[3]: 1, default: 0) +// COMPLIANT +#define M3(X) _Generic(1, X: 1, default: 0) + +void f2() { + M1(1); + M2(int); + M2(char); + + M3(int); // COMPLIANT + M3(const int); // NON-COMPLIANT +} + +typedef int int_t; +typedef int *int_ptr; +const typedef int const_int; +const typedef int *const_int_ptr; +typedef long const *long_const_ptr; + +void f3() { + _Generic(1, + int_t: 1, // COMPLIANT + const_int: 1, // NON-COMPLIANT + const_int_ptr: 1, // COMPLIANT + long_const_ptr: 1, // COMPLIANT + const int_ptr: 1, // COMPLIANT + default: 1 // COMPLIANT + ); +} + +// Type written here so it gets added to the database, see LvalueConversion.qll. +char *g; \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.expected b/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.expected new file mode 100644 index 0000000000..994d55968c --- /dev/null +++ b/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.expected @@ -0,0 +1,84 @@ +| test.c:41:3:41:44 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:41:3:41:44 | _Generic | | +| test.c:42:3:42:47 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:42:3:42:47 | _Generic | | +| test.c:43:3:43:53 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:43:3:43:53 | _Generic | | +| test.c:44:3:44:39 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:44:3:44:39 | _Generic | | +| test.c:45:3:45:45 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:45:3:45:45 | _Generic | | +| test.c:46:3:46:54 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:46:3:46:54 | _Generic | | +| test.c:48:3:48:38 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:48:3:48:38 | _Generic | | +| test.c:50:3:50:39 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:50:3:50:39 | _Generic | | +| test.c:51:3:51:45 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:51:3:51:45 | _Generic | | +| test.c:52:3:52:54 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:52:3:52:54 | _Generic | | +| test.c:57:3:57:53 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:57:3:57:53 | _Generic | | +| test.c:59:3:59:38 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to int *. | test.c:59:3:59:38 | _Generic | | +| test.c:61:3:61:53 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile int *. | test.c:61:3:61:53 | _Generic | | +| test.c:62:3:62:39 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to void *. | test.c:62:3:62:39 | _Generic | | +| test.c:63:3:63:54 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile void *. | test.c:63:3:63:54 | _Generic | | +| test.c:69:3:69:38 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to int *. | test.c:69:3:69:38 | _Generic | | +| test.c:70:3:70:44 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const int *. | test.c:70:3:70:44 | _Generic | | +| test.c:71:3:71:47 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to volatile int *. | test.c:71:3:71:47 | _Generic | | +| test.c:73:3:73:39 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to void *. | test.c:73:3:73:39 | _Generic | | +| test.c:74:3:74:45 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const void *. | test.c:74:3:74:45 | _Generic | | +| test.c:75:3:75:54 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const volatile void *. | test.c:75:3:75:54 | _Generic | | +| test.c:77:3:77:38 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to int *. | test.c:77:3:77:38 | _Generic | | +| test.c:78:3:78:44 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const int *. | test.c:78:3:78:44 | _Generic | | +| test.c:79:3:79:47 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to volatile int *. | test.c:79:3:79:47 | _Generic | | +| test.c:80:3:80:53 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const volatile int *. | test.c:80:3:80:53 | _Generic | | +| test.c:82:3:82:45 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const void *. | test.c:82:3:82:45 | _Generic | | +| test.c:83:3:83:54 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const volatile void *. | test.c:83:3:83:54 | _Generic | | +| test.c:85:3:85:38 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to int *. | test.c:85:3:85:38 | _Generic | | +| test.c:86:3:86:44 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const int *. | test.c:86:3:86:44 | _Generic | | +| test.c:87:3:87:53 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const volatile int *. | test.c:87:3:87:53 | _Generic | | +| test.c:88:3:88:39 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to void *. | test.c:88:3:88:39 | _Generic | | +| test.c:90:3:90:54 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const volatile void *. | test.c:90:3:90:54 | _Generic | | +| test.c:94:3:94:38 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to int *. | test.c:94:3:94:38 | _Generic | | +| test.c:95:3:95:44 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const int *. | test.c:95:3:95:44 | _Generic | | +| test.c:96:3:96:47 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to volatile int *. | test.c:96:3:96:47 | _Generic | | +| test.c:97:3:97:53 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const volatile int *. | test.c:97:3:97:53 | _Generic | | +| test.c:98:3:98:39 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to void *. | test.c:98:3:98:39 | _Generic | | +| test.c:99:3:99:45 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const void *. | test.c:99:3:99:45 | _Generic | | +| test.c:119:3:119:45 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:119:3:119:45 | _Generic | | +| test.c:120:3:120:48 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:120:3:120:48 | _Generic | | +| test.c:121:3:121:54 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:121:3:121:54 | _Generic | | +| test.c:122:3:122:40 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:122:3:122:40 | _Generic | | +| test.c:123:3:123:46 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:123:3:123:46 | _Generic | | +| test.c:124:3:124:55 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:124:3:124:55 | _Generic | | +| test.c:126:3:126:39 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:126:3:126:39 | _Generic | | +| test.c:128:3:128:40 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:128:3:128:40 | _Generic | | +| test.c:129:3:129:46 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:129:3:129:46 | _Generic | | +| test.c:130:3:130:55 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:130:3:130:55 | _Generic | | +| test.c:135:3:135:54 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:135:3:135:54 | _Generic | | +| test.c:137:3:137:39 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to int *. | test.c:137:3:137:39 | _Generic | | +| test.c:139:3:139:54 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile int *. | test.c:139:3:139:54 | _Generic | | +| test.c:140:3:140:40 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to void *. | test.c:140:3:140:40 | _Generic | | +| test.c:141:3:141:55 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile void *. | test.c:141:3:141:55 | _Generic | | +| test.c:147:3:147:39 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to int *. | test.c:147:3:147:39 | _Generic | | +| test.c:148:3:148:45 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const int *. | test.c:148:3:148:45 | _Generic | | +| test.c:149:3:149:48 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to volatile int *. | test.c:149:3:149:48 | _Generic | | +| test.c:151:3:151:40 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to void *. | test.c:151:3:151:40 | _Generic | | +| test.c:152:3:152:46 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const void *. | test.c:152:3:152:46 | _Generic | | +| test.c:153:3:153:55 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const volatile void *. | test.c:153:3:153:55 | _Generic | | +| test.c:156:3:156:45 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:156:3:156:45 | _Generic | | +| test.c:157:3:157:48 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:157:3:157:48 | _Generic | | +| test.c:158:3:158:54 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:158:3:158:54 | _Generic | | +| test.c:159:3:159:40 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:159:3:159:40 | _Generic | | +| test.c:160:3:160:46 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:160:3:160:46 | _Generic | | +| test.c:161:3:161:55 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:161:3:161:55 | _Generic | | +| test.c:163:3:163:39 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:163:3:163:39 | _Generic | | +| test.c:165:3:165:40 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:165:3:165:40 | _Generic | | +| test.c:166:3:166:46 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:166:3:166:46 | _Generic | | +| test.c:167:3:167:55 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:167:3:167:55 | _Generic | | +| test.c:172:3:172:54 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:172:3:172:54 | _Generic | | +| test.c:180:3:180:48 | _Generic | Generic matched default selection, as controlling argument type int(*)[3] does not undergo pointer conversion to int(*const)[3]. | test.c:180:3:180:48 | _Generic | | +| test.c:188:3:191:18 | _Generic | Generic matched default selection, as controlling argument type int(*)[3] does not undergo pointer conversion to int(*const)[3]. | test.c:188:3:191:18 | _Generic | | +| test.c:200:3:200:47 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to int *. | test.c:200:3:200:47 | _Generic | | +| test.c:201:3:201:47 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:201:3:201:47 | _Generic | | +| test.c:215:3:215:44 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:215:3:215:44 | _Generic | | +| test.c:216:3:216:46 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int_t *. | test.c:216:3:216:46 | _Generic | | +| test.c:217:3:217:42 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to c_int_t *. | test.c:217:3:217:42 | _Generic | | +| test.c:221:3:221:45 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to const int *. | test.c:221:3:221:45 | _Generic | | +| test.c:222:3:222:47 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to const int_t *. | test.c:222:3:222:47 | _Generic | | +| test.c:223:3:223:43 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to c_int_t *. | test.c:223:3:223:43 | _Generic | | +| test.c:225:3:225:38 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:225:3:225:38 | _Generic | | +| test.c:226:3:226:40 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int_t *. | test.c:226:3:226:40 | _Generic | | +| test.c:231:3:231:39 | _Generic | Generic matched default selection, as controlling argument type c_int_t * does not undergo pointer conversion to int *. | test.c:231:3:231:39 | _Generic | | +| test.c:232:3:232:41 | _Generic | Generic matched default selection, as controlling argument type c_int_t * does not undergo pointer conversion to int_t *. | test.c:232:3:232:41 | _Generic | | diff --git a/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.qlref b/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.qlref new file mode 100644 index 0000000000..c6b02b6273 --- /dev/null +++ b/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.qlref @@ -0,0 +1 @@ +rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-5/test.c b/c/misra/test/rules/RULE-23-5/test.c new file mode 100644 index 0000000000..d9e140f0af --- /dev/null +++ b/c/misra/test/rules/RULE-23-5/test.c @@ -0,0 +1,236 @@ +void f1(); + +void f2() { + int l1; + int *l2; + const int *l3; + volatile int *l4; + volatile const int *l5; + void *l6; + const void *l7; + volatile void *l8; + const volatile void *l9; + + // No violation for missing pointer/integral conversions: + _Generic(l1, // COMPLIANT + int *: f1, + const int *: f1, + volatile int *: f1, + void *: f1, + const void *: f1, + default: f1); // COMPLIANT + _Generic(l2, int: f1, default: f1); // COMPLIANT + _Generic(l3, int: f1, default: f1); // COMPLIANT + _Generic(l4, int: f1, default: f1); // COMPLIANT + _Generic(l5, int: f1, default: f1); // COMPLIANT + + // Compliant, default case is not matched + _Generic(l1, int: f1); // COMPLIANT + _Generic(l2, int *: f1); // COMPLIANT + _Generic(l3, const int *: f1); // COMPLIANT + _Generic(l4, volatile int *: f1); // COMPLIANT + _Generic(l5, volatile const int *: f1); // COMPLIANT + _Generic(l6, void *: f1); // COMPLIANT + _Generic(l7, const void *: f1); // COMPLIANT + _Generic(l8, volatile void *: f1); // COMPLIANT + _Generic(l9, const volatile void *: f1); // COMPLIANT + + // Violation, match default case due to lack of pointer to pointer + // conversions: + _Generic(l2, int *: f1, default: f1); // COMPLIANT + _Generic(l2, const int *: f1, default: f1); // NON-COMPLIANT + _Generic(l2, volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l2, const volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l2, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l2, const void *: f1, default: f1); // NON-COMPLIANT + _Generic(l2, const volatile void *: f1, default: f1); // NON-COMPLIANT + + _Generic(l3, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l3, const int *: f1, default: f1); // COMPLIANT + _Generic(l3, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l3, const void *: f1, default: f1); // NON-COMPLIANT + _Generic(l3, const volatile void *: f1, default: f1); // NON-COMPLIANT + // Obviously not volatile: + _Generic(l3, volatile int *: f1, default: f1); // COMPLIANT + // Debatable, but volatile const int* is assignable to const int* so its + // considered risky + _Generic(l3, const volatile int *: f1, default: f1); // NON-COMPLIANT + + _Generic(l4, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l4, volatile int *: f1, default: f1); // COMPLIANT + _Generic(l4, const volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l4, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l4, const volatile void *: f1, default: f1); // NON-COMPLIANT + // Debatable, but volatile int* isn't assignable to const int* or vice versa. + _Generic(l4, const int *: f1, default: f1); // COMPLIANT + // Debatable, but volatile int* isn't assignable to const void* or vice versa. + _Generic(l4, const void *: f1, default: f1); // COMPLIANT + + _Generic(l5, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l5, const int *: f1, default: f1); // NON-COMPLIANT + _Generic(l5, volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l5, const volatile int *: f1, default: f1); // COMPLIANT + _Generic(l5, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l5, const void *: f1, default: f1); // NON-COMPLIANT + _Generic(l5, const volatile void *: f1, default: f1); // NON-COMPLIANT + + _Generic(l6, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l6, const int *: f1, default: f1); // NON-COMPLIANT + _Generic(l6, volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l6, const volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l6, void *: f1, default: f1); // COMPLIANT + _Generic(l6, const void *: f1, default: f1); // NON-COMPLIANT + _Generic(l6, const volatile void *: f1, default: f1); // NON-COMPLIANT + + _Generic(l7, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l7, const int *: f1, default: f1); // NON-COMPLIANT + _Generic(l7, const volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l7, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l7, const void *: f1, default: f1); // COMPLIANT + _Generic(l7, const volatile void *: f1, default: f1); // NON-COMPLIANT + // Debatable, but const void* isn't assignable to volatile int* or vice versa. + _Generic(l7, volatile int *: f1, default: f1); // COMPLIANT + + _Generic(l9, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l9, const int *: f1, default: f1); // NON-COMPLIANT + _Generic(l9, volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l9, const volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l9, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l9, const void *: f1, default: f1); // NON-COMPLIANT + _Generic(l9, const volatile void *: f1, default: f1); // COMPLIANT + + /** + * Edge case 1: The controlling expression undergoes lvalue conversion, so + * arrays become pointers and qualifiers on pointers are stripped. + */ + int l10[3]; + const int l11[3]; + volatile int l12[3]; + const volatile int l13[3]; + int *const l14; + const int *const l15; + + _Generic(l10, int *: f1, default: f1); // COMPLIANT + _Generic(l11, const int *: f1, default: f1); // COMPLIANT + _Generic(l12, volatile int *: f1, default: f1); // COMPLIANT + _Generic(l13, const volatile int *: f1, default: f1); // COMPLIANT + + _Generic(l10, int *: f1, default: f1); // COMPLIANT + _Generic(l10, const int *: f1, default: f1); // NON-COMPLIANT + _Generic(l10, volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l10, const volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l10, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l10, const void *: f1, default: f1); // NON-COMPLIANT + _Generic(l10, const volatile void *: f1, default: f1); // NON-COMPLIANT + + _Generic(l11, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l11, const int *: f1, default: f1); // COMPLIANT + _Generic(l11, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l11, const void *: f1, default: f1); // NON-COMPLIANT + _Generic(l11, const volatile void *: f1, default: f1); // NON-COMPLIANT + // Obviously not volatile: + _Generic(l11, volatile int *: f1, default: f1); // COMPLIANT + // Debatable, but volatile const int* is assignable to const int* so its + // considered risky + _Generic(l11, const volatile int *: f1, default: f1); // NON-COMPLIANT + + _Generic(l12, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l12, volatile int *: f1, default: f1); // COMPLIANT + _Generic(l12, const volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l12, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l12, const volatile void *: f1, default: f1); // NON-COMPLIANT + // Debatab12e, but volatile int* isn't assignable to const int* or vice versa. + _Generic(l12, const int *: f1, default: f1); // COMPLIANT + // Debatable, but volatile int* isn't assignable to const void* or vice versa. + _Generic(l12, const void *: f1, default: f1); // COMPLIANT + + _Generic(l13, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l13, const int *: f1, default: f1); // NON-COMPLIANT + _Generic(l13, volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l13, const volatile int *: f1, default: f1); // COMPLIANT + _Generic(l13, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l13, const void *: f1, default: f1); // NON-COMPLIANT + _Generic(l13, const volatile void *: f1, default: f1); // NON-COMPLIANT + + _Generic(l14, int *: f1, default: f1); // COMPLIANT + _Generic(l14, const int *: f1, default: f1); // NON-COMPLIANT + _Generic(l14, volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l14, const volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l14, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l14, const void *: f1, default: f1); // NON-COMPLIANT + _Generic(l14, const volatile void *: f1, default: f1); // NON-COMPLIANT + + _Generic(l15, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l15, const int *: f1, default: f1); // COMPLIANT + _Generic(l15, void *: f1, default: f1); // NON-COMPLIANT + _Generic(l15, const void *: f1, default: f1); // NON-COMPLIANT + _Generic(l15, const volatile void *: f1, default: f1); // NON-COMPLIANT + // Obviously not volatile: + _Generic(l15, volatile int *: f1, default: f1); // COMPLIANT + // Debatable, but volatile const int* is assignable to const int* so its + // considered risky + _Generic(l15, const volatile int *: f1, default: f1); // NON-COMPLIANT + + /** + * Edge case 2: Types don't have to be identical to be compatible. + */ + int(*l16)[3]; + + // This is a risky conversion that should be reported: + _Generic(l16, int(*const)[3]: f1, default: f1); // NON-COMPLIANT + // However, in this one, there is a match on the second selector, because it + // it is an array type with a compatible element type, and sizes only have to + // match if both arrays have a constant size. Therefore, the default selector + // is not chosen and this is not a violation. + _Generic(l16, int(*const)[3]: f1, int(*)[]: f1, default: f1); // COMPLIANT + // In this case, the second selector is not a compatible type because the + // array has a constant size that doesn't match, and this should be reported. + _Generic(l16, + int(*const)[3]: f1, + int(*)[4]: f1, + default: f1); // NON-COMPLIANT + + /** + * Edge case 3: Conversion on _Generic, make sure we use the fully converted + * type when considering compliance. + */ + int *l17; + void *l18; + _Generic((void *)l17, void *: f1, default: f1); // COMPLIANT + _Generic((void *)l17, int *: f1, default: f1); // NON-COMPLIANT + _Generic((int *)l18, void *: f1, default: f1); // NON-COMPLIANT + _Generic((int *)l18, int *: f1, default: f1); // COMPLIANT + + /** + * Edge case 4: Typedefs must be resolved properly. + */ + typedef int int_t; + const typedef int c_int_t; + int_t *l19; + c_int_t *l20; + volatile c_int_t *l21; + + _Generic(l2, int *: f1, default: f1); // COMPLIANT + _Generic(l2, int_t *: f1, default: f1); // COMPLIANT + _Generic(l2, const int *: f1, default: f1); // NON-COMPLIANT + _Generic(l2, const int_t *: f1, default: f1); // NON-COMPLIANT + _Generic(l2, c_int_t *: f1, default: f1); // NON-COMPLIANT + + _Generic(l19, int *: f1, default: f1); // COMPLIANT + _Generic(l19, int_t *: f1, default: f1); // COMPLIANT + _Generic(l19, const int *: f1, default: f1); // NON-COMPLIANT + _Generic(l19, const int_t *: f1, default: f1); // NON-COMPLIANT + _Generic(l19, c_int_t *: f1, default: f1); // NON-COMPLIANT + + _Generic(l3, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l3, int_t *: f1, default: f1); // NON-COMPLIANT + _Generic(l3, const int *: f1, default: f1); // COMPLIANT + _Generic(l3, const int_t *: f1, default: f1); // COMPLIANT + _Generic(l3, c_int_t *: f1, default: f1); // COMPLIANT + + _Generic(l20, int *: f1, default: f1); // NON-COMPLIANT + _Generic(l20, int_t *: f1, default: f1); // NON-COMPLIANT + _Generic(l20, const int *: f1, default: f1); // COMPLIANT + _Generic(l20, const int_t *: f1, default: f1); // COMPLIANT + _Generic(l20, c_int_t *: f1, default: f1); // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.expected b/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.expected new file mode 100644 index 0000000000..1cdcc82698 --- /dev/null +++ b/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.expected @@ -0,0 +1,4 @@ +| test.c:11:3:11:8 | _Generic | Controlling expression in generic macro $@ has standard type (unnamed enum), which doesn't match its essential type (unnamed enum). | test.c:6:1:6:71 | #define M1(X) _Generic((X), int: 1, unsigned int: 1, short: 2, long: 3) | M1 | +| test.c:15:3:15:13 | _Generic | Controlling expression in generic macro $@ has standard type int, which doesn't match its essential type short. | test.c:6:1:6:71 | #define M1(X) _Generic((X), int: 1, unsigned int: 1, short: 2, long: 3) | M1 | +| test.c:18:3:18:23 | _Generic | Controlling expression in generic has standard type int, which doesn't match its essential type char. | test.c:18:3:18:23 | _Generic | | +| test.c:19:3:19:53 | _Generic | Controlling expression in generic has standard type int, which doesn't match its essential type short. | test.c:19:3:19:53 | _Generic | | diff --git a/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.qlref b/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.qlref new file mode 100644 index 0000000000..b91bbefec6 --- /dev/null +++ b/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.qlref @@ -0,0 +1 @@ +rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-6/test.c b/c/misra/test/rules/RULE-23-6/test.c new file mode 100644 index 0000000000..7d59c1e199 --- /dev/null +++ b/c/misra/test/rules/RULE-23-6/test.c @@ -0,0 +1,33 @@ +short l1; +int l2; +long l3; +enum { E1 } l4; + +#define M1(X) _Generic((X), int: 1, unsigned int: 1, short: 2, long: 3) +void f1() { + M1(l1); // COMPLIANT + M1(l2); // COMPLIANT + M1(l3); // COMPLIANT + M1(l4); // NON-COMPLIANT + + M1(1); // COMPLIANT + M1(1u); // COMPLIANT + M1(l1 + l1); // NON-COMPLIANT + M1((int)(l1 + l1)); // COMPLIANT + M1('c'); // NON-COMPLIANT[false negative] + _Generic('c', int: 1); // NON-COMPLIANT + _Generic(_Generic(0, default: l1 + l1), default: 1); // NON-COMPLIANT + _Generic(((short)_Generic(0, default: (l1 + l1))), default: 1); // COMPLIANT +} + +void f2() { + // Edge case: lvalue conversion of a const struct yields an implicit + // conversion to a non-const struct which is ignored by EssentialTypes.qll, + // meaning the essential type does not match the static type. However, we + // shouldn't report an issue here as the static/essential types are not one + // of the essential type categories. + struct S1 { + int m1; + }; + _Generic((const struct S1){.m1 = 0}, default: 1); +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.expected b/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.expected new file mode 100644 index 0000000000..57eecd6be8 --- /dev/null +++ b/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.expected @@ -0,0 +1,12 @@ +| test.c:9:1:9:51 | #define M3(X) _Generic((X), int: f1(X), default: 0) | Generic macro M3 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 2. | +| test.c:10:1:10:61 | #define M4(X) (X) + _Generic((X), int: f1(X), default: f1(X)) | Generic macro M4 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | +| test.c:11:1:11:61 | #define M5(X) _Generic((X), int: f1(X), default: f1(X)) + (X) | Generic macro M5 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | +| test.c:12:1:12:63 | #define M6(X) _Generic((X), int: f1((X) + (X)), default: f1(X)) | Generic macro M6 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 1 more than once. | +| test.c:21:1:21:36 | #define M9(X) _Generic((X), int: f1) | Generic macro M9 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 1. | +| test.c:23:1:23:40 | #define M10(X) _Generic((X), int: f1(1)) | Generic macro M10 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 1. | +| test.c:32:1:32:58 | #define M12(X) _Generic((X) + (X), int: f1(X), default: 1) | Generic macro M12 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 2. | +| test.c:33:1:33:68 | #define M13(X) _Generic((X) + (X), int: f1(X), default: f1(X)) + (X) | Generic macro M13 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | +| test.c:43:1:43:77 | #define M17(X,Y) _Generic((X) + (Y), int: f2((X), (Y)), default: f2((X), 1)) | Generic macro M17 may have unexpected behavior from side effects in parameter Y, as it is not expanded in generic selection 2. | +| test.c:67:1:67:78 | #define M26(X) _Generic((X), int: IGNORE_2ND(X, X), default: IGNORE_2ND(X, X)) | Generic macro M26 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 1 more than once. | +| test.c:67:1:67:78 | #define M26(X) _Generic((X), int: IGNORE_2ND(X, X), default: IGNORE_2ND(X, X)) | Generic macro M26 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 2 more than once. | +| test.c:68:1:68:75 | #define M27(X) _Generic((X), int: f1(IGNORE(X)), default: f1(IGNORE(X)))(X) | Generic macro M27 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | diff --git a/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.qlref b/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.qlref new file mode 100644 index 0000000000..3156bdce91 --- /dev/null +++ b/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.qlref @@ -0,0 +1 @@ +rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-7/test.c b/c/misra/test/rules/RULE-23-7/test.c new file mode 100644 index 0000000000..41a5d6ab6c --- /dev/null +++ b/c/misra/test/rules/RULE-23-7/test.c @@ -0,0 +1,71 @@ +int f1(int p1); +int f2(int p1, int p2); + +// COMPLIANT -- standard correct cases: +#define M1(X) _Generic((X), int: f1, default: f1)(X) +#define M2(X) _Generic((X), int: f1(X), default: f1(X)) + +// NON-COMPLIANT -- standard incorrect cases: +#define M3(X) _Generic((X), int: f1(X), default: 0) +#define M4(X) (X) + _Generic((X), int: f1(X), default: f1(X)) +#define M5(X) _Generic((X), int: f1(X), default: f1(X)) + (X) +#define M6(X) _Generic((X), int: f1((X) + (X)), default: f1(X)) + +// Compliant by exception +// COMPLIANT +#define M7(X) _Generic((X), int: 1, default: 0) +// NON-COMPLIANT[FALSE NEGATIVE] -- Without an expansion, we can't tell if this +// macro has only constant expressions or not. +#define M8(X) _Generic((X), int: f1(1)) +// NON-COMPLIANT -- If the macro is expanded we can detect constant expressions +#define M9(X) _Generic((X), int: f1) +// NON-COMPLIANT -- If the macro is expanded we can detect constant expressions +#define M10(X) _Generic((X), int: f1(1)) +void f3() { + M9(1); + M10(1); +} + +// COMPLIANT -- multiple uses in the controlling expression is OK: +#define M11(X) _Generic((X) + (X), int: f1(X), default: f1(X)) +// NON-COMPLIANT -- the rule should still be enforced otherwise: +#define M12(X) _Generic((X) + (X), int: f1(X), default: 1) +#define M13(X) _Generic((X) + (X), int: f1(X), default: f1(X)) + (X) + +// COMPLIANT -- the argument is not used in the controlling expression: +#define M14(X) _Generic(1, int: f1((X) + (X)), default: f1(X)) +#define M15(X) _Generic(1, int: f1(X), default: f1(X)) + (X) + +// Test cases with more than one argument: +// COMPLIANT -- Y is not used in the controlling expression: +#define M16(X, Y) _Generic((X), int: f2((X), (Y)), default: f2((X), 1)) +// NON-COMPLIANT -- Y is used in the controlling expression +#define M17(X, Y) _Generic((X) + (Y), int: f2((X), (Y)), default: f2((X), 1)) +// COMPLIANT -- Y is used in the controlling expression correctly +#define M18(X, Y) _Generic((X) + (Y), int: f2((X), (Y)), default: f2((X), (Y))) + +// Test unevaluated contexts: +// COMPLIANT -- sizeof is not evaluated: +#define M19(X) _Generic((X), int[sizeof(X)]: f1, default: f1)(X) +#define M20(X) _Generic((X), int: f1(sizeof(X)), default: f1)(X) +#define M21(X) _Generic((X), int: f1(X), default: f1(X)) + sizeof(X) +// NON-COMPLIANT[FALSE NEGATIVE] -- sizeof plus evaluated context +#define M22(X) _Generic((X), int: f1(sizeof(X) + X), default: f1(X))(X) +// NON-COMPLIANT[FALSE NEGATIVE] -- array type sizes may be evaluated +#define M23(X) _Generic((X), int[X]: f1, default: f1)(X) +// COMPLIANT -- alignof, typeof are not evaluated: +#define M24(X) _Generic((X), int[X]: f1, default: f1)(X) + +// Nested macros: +#define ONCE(X) (X) +#define TWICE(X) (X) + (X) +#define IGNORE(X) (1) +#define IGNORE_2ND(X, Y) (X) +// COMPLIANT +#define M25(X) _Generic((X), int: ONCE(f1(X)), default: ONCE(f1(X)) +// COMPLIANT[FALSE POSITIVE] +#define M26(X) _Generic((X), int: IGNORE_2ND(X, X), default: IGNORE_2ND(X, X)) +#define M27(X) _Generic((X), int: f1(IGNORE(X)), default: f1(IGNORE(X)))(X) +// NON-COMPLIANT[FASE NEGATIVE] +#define M28(X) _Generic((X), int: f1(IGNORE(X)), default: f1(IGNORE(X))) +#define M29(X) _Generic((X), int: TWICE(f1(X)), default: TWICE(f1(X))) \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.expected b/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.expected new file mode 100644 index 0000000000..5951834d00 --- /dev/null +++ b/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.expected @@ -0,0 +1,4 @@ +| test.c:11:1:11:64 | #define M4(X) _Generic((X), int: 1, default: 0, unsigned int: 2) | Generic macro M4 has default as 2nd association, which is not first or last. | test.c:11:1:11:64 | #define M4(X) _Generic((X), int: 1, default: 0, unsigned int: 2) | (ignored) | +| test.c:17:1:17:60 | #define M5(__VA_ARGS__...) _Generic(0, __VA_ARGS__, default: 0, int: 1) | Generic macro M5 has a default association which is not first or last, for example $@. | test.c:28:5:28:23 | _Generic | 2nd | +| test.c:34:5:34:27 | M6(__VA_ARGS__...) | Generic macro $@, in this expansion, has default as 2nd association, which is not first or last. | test.c:19:1:19:48 | #define M6(__VA_ARGS__...) _Generic(0, __VA_ARGS__, int: 1) | M6 | +| test.c:44:5:44:52 | _Generic | Generic has default as 2nd association, which is not first or last. | test.c:44:5:44:52 | _Generic | | diff --git a/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.qlref b/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.qlref new file mode 100644 index 0000000000..06fe786e7d --- /dev/null +++ b/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.qlref @@ -0,0 +1 @@ +rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-8/test.c b/c/misra/test/rules/RULE-23-8/test.c new file mode 100644 index 0000000000..fdd6e66044 --- /dev/null +++ b/c/misra/test/rules/RULE-23-8/test.c @@ -0,0 +1,49 @@ +/** + * Cases where the macro itself is always compliant or non compliant: + */ +// COMPLIANT +#define M1(X) _Generic((X), int: 1, unsigned int: 2) +// COMPLIANT +#define M2(X) _Generic((X), int: 1, unsigned int: 2, default: 0) +// COMPLIANT +#define M3(X) _Generic((X), default: 0, int: 1, unsigned int: 2) +// NON-COMPLIANT +#define M4(X) _Generic((X), int: 1, default: 0, unsigned int: 2) + +/** + * Macros that are compliant or not based on use: + */ +// NON-COMPLIANT: because every use is non compliant +#define M5(...) _Generic(0, __VA_ARGS__, default: 0, int: 1) +// COMPLIANT: because some uses are compliant +#define M6(...) _Generic(0, __VA_ARGS__, int: 1) + +void f1() { + M1(0); // COMPLIANT + M2(0); // COMPLIANT + M3(0); // COMPLIANT + M4(0); // COMPLIANT: the macro invocation is compliant, the macro definition + // is not. + + // COMPLIANT: all invocations of M5 are non compliant so the macro is reported + // instead. + M5(unsigned int : 1); + M5(unsigned int : 1, long : 2); + + // Some invocations of M6() will be compliant, so we'll report the issue at + // each invocation. + M6(default : 0); // COMPLIANT + M6(default : 0, long : 1); // COMPLIANT + M6(long : 1, default : 0); // NON-COMPLIANT +} + +/** + * For completeness, non macro cases, though these are not likely and violate + * RULE-23-1. + */ +void f2() { + _Generic(0, int: 1, unsigned int: 2); // COMPLIANT + _Generic(0, int: 1, unsigned int: 2, default: 0); // COMPLIANT + _Generic(0, default: 0, int: 1, unsigned int: 2); // COMPLIANT + _Generic(0, int: 1, default: 0, unsigned int: 2); // NON-COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.expected b/c/misra/test/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.expected index 08e419ef4f..f2b438aaf1 100644 --- a/c/misra/test/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.expected +++ b/c/misra/test/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.expected @@ -1,12 +1,14 @@ -| function1.c:6:6:6:7 | declaration of f3 | The return type of re-declaration of $@ is not compatible with declaration $@ | function1.c:6:6:6:7 | declaration of f3 | f3 | function1.c:8:4:8:5 | declaration of f3 | f3 | -| function1.c:8:4:8:5 | declaration of f3 | The return type of re-declaration of $@ is not compatible with declaration $@ | function1.c:8:4:8:5 | declaration of f3 | f3 | function1.c:6:6:6:7 | declaration of f3 | f3 | -| function1.c:8:4:8:5 | declaration of f3 | The return type of re-declaration of $@ is not compatible with declaration $@ | function1.c:8:4:8:5 | declaration of f3 | f3 | function2.c:4:6:4:7 | declaration of f3 | f3 | -| function1.c:9:6:9:7 | declaration of f4 | The return type of re-declaration of $@ is not compatible with declaration $@ | function1.c:9:6:9:7 | declaration of f4 | f4 | function2.c:5:5:5:6 | declaration of f4 | f4 | -| function1.c:13:5:13:6 | definition of f6 | The return type of re-declaration of $@ is not compatible with declaration $@ | function1.c:13:5:13:6 | definition of f6 | f6 | function2.c:9:6:9:7 | definition of f6 | f6 | -| function1.c:21:3:21:5 | definition of f21 | The parameter types of re-declaration of $@ is not compatible with declaration $@ | function1.c:21:3:21:5 | definition of f21 | f21 | function2.c:17:10:17:12 | declaration of f21 | f21 | -| function1.c:25:6:25:8 | definition of f22 | The parameter names of re-declaration of $@ is not compatible with declaration $@ | function1.c:25:6:25:8 | definition of f22 | f22 | function2.c:19:13:19:15 | declaration of f22 | f22 | -| function2.c:4:6:4:7 | declaration of f3 | The return type of re-declaration of $@ is not compatible with declaration $@ | function2.c:4:6:4:7 | declaration of f3 | f3 | function1.c:8:4:8:5 | declaration of f3 | f3 | -| function2.c:5:5:5:6 | declaration of f4 | The return type of re-declaration of $@ is not compatible with declaration $@ | function2.c:5:5:5:6 | declaration of f4 | f4 | function1.c:9:6:9:7 | declaration of f4 | f4 | -| function2.c:9:6:9:7 | definition of f6 | The return type of re-declaration of $@ is not compatible with declaration $@ | function2.c:9:6:9:7 | definition of f6 | f6 | function1.c:13:5:13:6 | definition of f6 | f6 | -| function2.c:17:10:17:12 | declaration of f21 | The parameter types of re-declaration of $@ is not compatible with declaration $@ | function2.c:17:10:17:12 | declaration of f21 | f21 | function1.c:21:3:21:5 | definition of f21 | f21 | -| function2.c:19:13:19:15 | declaration of f22 | The parameter names of re-declaration of $@ is not compatible with declaration $@ | function2.c:19:13:19:15 | declaration of f22 | f22 | function1.c:25:6:25:8 | definition of f22 | f22 | +| function1.c:6:6:6:7 | declaration of f3 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function1.c:6:6:6:7 | declaration of f3 | f3 | function1.c:8:4:8:5 | declaration of f3 | f3 | +| function1.c:8:4:8:5 | declaration of f3 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function1.c:8:4:8:5 | declaration of f3 | f3 | function1.c:6:6:6:7 | declaration of f3 | f3 | +| function1.c:8:4:8:5 | declaration of f3 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function1.c:8:4:8:5 | declaration of f3 | f3 | function2.c:4:6:4:7 | declaration of f3 | f3 | +| function1.c:9:6:9:7 | declaration of f4 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function1.c:9:6:9:7 | declaration of f4 | f4 | function2.c:5:5:5:6 | declaration of f4 | f4 | +| function1.c:13:5:13:6 | definition of f6 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function1.c:13:5:13:6 | definition of f6 | f6 | function2.c:9:6:9:7 | definition of f6 | f6 | +| function1.c:15:5:15:7 | declaration of f20 | The parameter types of re-declaration of $@ do not use the same type names as declaration $@ | function1.c:15:5:15:7 | declaration of f20 | f20 | function2.c:11:5:11:7 | declaration of f20 | f20 | +| function1.c:21:3:21:5 | definition of f21 | The parameter types of re-declaration of $@ do not use the same type names as declaration $@ | function1.c:21:3:21:5 | definition of f21 | f21 | function2.c:17:10:17:12 | declaration of f21 | f21 | +| function1.c:25:6:25:8 | definition of f22 | The parameter names of re-declaration of $@ do not use the same type names as declaration $@ | function1.c:25:6:25:8 | definition of f22 | f22 | function2.c:19:13:19:15 | declaration of f22 | f22 | +| function2.c:4:6:4:7 | declaration of f3 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function2.c:4:6:4:7 | declaration of f3 | f3 | function1.c:8:4:8:5 | declaration of f3 | f3 | +| function2.c:5:5:5:6 | declaration of f4 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function2.c:5:5:5:6 | declaration of f4 | f4 | function1.c:9:6:9:7 | declaration of f4 | f4 | +| function2.c:9:6:9:7 | definition of f6 | The return type of re-declaration of $@ does not use the same type names as declaration $@ | function2.c:9:6:9:7 | definition of f6 | f6 | function1.c:13:5:13:6 | definition of f6 | f6 | +| function2.c:11:5:11:7 | declaration of f20 | The parameter types of re-declaration of $@ do not use the same type names as declaration $@ | function2.c:11:5:11:7 | declaration of f20 | f20 | function1.c:15:5:15:7 | declaration of f20 | f20 | +| function2.c:17:10:17:12 | declaration of f21 | The parameter types of re-declaration of $@ do not use the same type names as declaration $@ | function2.c:17:10:17:12 | declaration of f21 | f21 | function1.c:21:3:21:5 | definition of f21 | f21 | +| function2.c:19:13:19:15 | declaration of f22 | The parameter names of re-declaration of $@ do not use the same type names as declaration $@ | function2.c:19:13:19:15 | declaration of f22 | f22 | function1.c:25:6:25:8 | definition of f22 | f22 | diff --git a/c/misra/test/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.expected b/c/misra/test/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.expected index c63681c7be..8b8e7f8a48 100644 --- a/c/misra/test/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.expected +++ b/c/misra/test/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.expected @@ -1,22 +1,22 @@ -| object1.c:5:6:5:7 | definition of a3 | The object $@ of type long is not compatible with re-declaration $@ of type LL | object1.c:5:6:5:7 | definition of a3 | a3 | object2.c:11:11:11:12 | declaration of a3 | a3 | -| object1.c:6:6:6:7 | definition of a4 | The object $@ of type long is not compatible with re-declaration $@ of type int | object1.c:6:6:6:7 | definition of a4 | a4 | object2.c:13:12:13:13 | declaration of a4 | a4 | -| object1.c:7:5:7:6 | definition of a5 | The object $@ of type int is not compatible with re-declaration $@ of type long | object1.c:7:5:7:6 | definition of a5 | a5 | object2.c:15:13:15:14 | declaration of a5 | a5 | -| object1.c:8:6:8:7 | definition of a6 | The object $@ of type long is not compatible with re-declaration $@ of type int | object1.c:8:6:8:7 | definition of a6 | a6 | object2.c:19:1:19:3 | declaration of a6 | a6 | -| object1.c:9:5:9:6 | definition of a7 | The object $@ of type int is not compatible with re-declaration $@ of type LL | object1.c:9:5:9:6 | definition of a7 | a7 | object2.c:21:11:21:12 | declaration of a7 | a7 | -| object1.c:15:5:15:7 | definition of a10 | The object $@ of type int[100] is not compatible with re-declaration $@ of type LI[100] | object1.c:15:5:15:7 | definition of a10 | a10 | object2.c:24:4:24:6 | definition of a10 | a10 | -| object1.c:16:5:16:7 | definition of a11 | The object $@ of type int[100] is not compatible with re-declaration $@ of type int[101] | object1.c:16:5:16:7 | definition of a11 | a11 | object2.c:25:12:25:14 | declaration of a11 | a11 | -| object1.c:19:12:19:14 | definition of a13 | The object $@ of type int *const is not compatible with re-declaration $@ of type int * | object1.c:19:12:19:14 | definition of a13 | a13 | object2.c:28:13:28:15 | declaration of a13 | a13 | -| object1.c:23:10:23:13 | definition of size | The object $@ of type size_t is not compatible with re-declaration $@ of type unsigned char | object1.c:23:10:23:13 | definition of size | size | object2.c:32:17:32:20 | definition of size | size | -| object1.c:24:3:24:4 | definition of s0 | The object $@ of type NamedStruct0 is not compatible with re-declaration $@ of type NamedStruct0 | object1.c:24:3:24:4 | definition of s0 | s0 | object2.c:33:3:33:4 | definition of s0 | s0 | -| object1.c:29:3:29:4 | definition of s1 | The object $@ of type NamedStruct1 is not compatible with re-declaration $@ of type NamedStruct1 | object1.c:29:3:29:4 | definition of s1 | s1 | object2.c:38:3:38:4 | definition of s1 | s1 | -| object2.c:11:11:11:12 | declaration of a3 | The object $@ of type LL is not compatible with re-declaration $@ of type long | object2.c:11:11:11:12 | declaration of a3 | a3 | object1.c:5:6:5:7 | definition of a3 | a3 | -| object2.c:13:12:13:13 | declaration of a4 | The object $@ of type int is not compatible with re-declaration $@ of type long | object2.c:13:12:13:13 | declaration of a4 | a4 | object1.c:6:6:6:7 | definition of a4 | a4 | -| object2.c:15:13:15:14 | declaration of a5 | The object $@ of type long is not compatible with re-declaration $@ of type int | object2.c:15:13:15:14 | declaration of a5 | a5 | object1.c:7:5:7:6 | definition of a5 | a5 | -| object2.c:19:1:19:3 | declaration of a6 | The object $@ of type int is not compatible with re-declaration $@ of type long | object2.c:19:1:19:3 | declaration of a6 | a6 | object1.c:8:6:8:7 | definition of a6 | a6 | -| object2.c:21:11:21:12 | declaration of a7 | The object $@ of type LL is not compatible with re-declaration $@ of type int | object2.c:21:11:21:12 | declaration of a7 | a7 | object1.c:9:5:9:6 | definition of a7 | a7 | -| object2.c:24:4:24:6 | definition of a10 | The object $@ of type LI[100] is not compatible with re-declaration $@ of type int[100] | object2.c:24:4:24:6 | definition of a10 | a10 | object1.c:15:5:15:7 | definition of a10 | a10 | -| object2.c:25:12:25:14 | declaration of a11 | The object $@ of type int[101] is not compatible with re-declaration $@ of type int[100] | object2.c:25:12:25:14 | declaration of a11 | a11 | object1.c:16:5:16:7 | definition of a11 | a11 | -| object2.c:28:13:28:15 | declaration of a13 | The object $@ of type int * is not compatible with re-declaration $@ of type int *const | object2.c:28:13:28:15 | declaration of a13 | a13 | object1.c:19:12:19:14 | definition of a13 | a13 | -| object2.c:32:17:32:20 | definition of size | The object $@ of type unsigned char is not compatible with re-declaration $@ of type size_t | object2.c:32:17:32:20 | definition of size | size | object1.c:23:10:23:13 | definition of size | size | -| object2.c:33:3:33:4 | definition of s0 | The object $@ of type NamedStruct0 is not compatible with re-declaration $@ of type NamedStruct0 | object2.c:33:3:33:4 | definition of s0 | s0 | object1.c:24:3:24:4 | definition of s0 | s0 | -| object2.c:38:3:38:4 | definition of s1 | The object $@ of type NamedStruct1 is not compatible with re-declaration $@ of type NamedStruct1 | object2.c:38:3:38:4 | definition of s1 | s1 | object1.c:29:3:29:4 | definition of s1 | s1 | +| object1.c:5:6:5:7 | definition of a3 | The object $@ of type long does not use the same type names as re-declaration $@ of type LL | object1.c:5:6:5:7 | definition of a3 | a3 | object2.c:11:11:11:12 | declaration of a3 | a3 | +| object1.c:6:6:6:7 | definition of a4 | The object $@ of type long does not use the same type names as re-declaration $@ of type int | object1.c:6:6:6:7 | definition of a4 | a4 | object2.c:13:12:13:13 | declaration of a4 | a4 | +| object1.c:7:5:7:6 | definition of a5 | The object $@ of type int does not use the same type names as re-declaration $@ of type long | object1.c:7:5:7:6 | definition of a5 | a5 | object2.c:15:13:15:14 | declaration of a5 | a5 | +| object1.c:8:6:8:7 | definition of a6 | The object $@ of type long does not use the same type names as re-declaration $@ of type int | object1.c:8:6:8:7 | definition of a6 | a6 | object2.c:19:1:19:3 | declaration of a6 | a6 | +| object1.c:9:5:9:6 | definition of a7 | The object $@ of type int does not use the same type names as re-declaration $@ of type LL | object1.c:9:5:9:6 | definition of a7 | a7 | object2.c:21:11:21:12 | declaration of a7 | a7 | +| object1.c:15:5:15:7 | definition of a10 | The object $@ of type int[100] does not use the same type names as re-declaration $@ of type LI[100] | object1.c:15:5:15:7 | definition of a10 | a10 | object2.c:24:4:24:6 | definition of a10 | a10 | +| object1.c:16:5:16:7 | definition of a11 | The object $@ of type int[100] does not use the same type names as re-declaration $@ of type int[101] | object1.c:16:5:16:7 | definition of a11 | a11 | object2.c:25:12:25:14 | declaration of a11 | a11 | +| object1.c:19:12:19:14 | definition of a13 | The object $@ of type int *const does not use the same type names as re-declaration $@ of type int * | object1.c:19:12:19:14 | definition of a13 | a13 | object2.c:28:13:28:15 | declaration of a13 | a13 | +| object1.c:23:10:23:13 | definition of size | The object $@ of type size_t does not use the same type names as re-declaration $@ of type unsigned char | object1.c:23:10:23:13 | definition of size | size | object2.c:32:17:32:20 | definition of size | size | +| object1.c:24:3:24:4 | definition of s0 | The object $@ of type NamedStruct0 does not use the same type names as re-declaration $@ of type NamedStruct0 | object1.c:24:3:24:4 | definition of s0 | s0 | object2.c:33:3:33:4 | definition of s0 | s0 | +| object1.c:29:3:29:4 | definition of s1 | The object $@ of type NamedStruct1 does not use the same type names as re-declaration $@ of type NamedStruct1 | object1.c:29:3:29:4 | definition of s1 | s1 | object2.c:38:3:38:4 | definition of s1 | s1 | +| object2.c:11:11:11:12 | declaration of a3 | The object $@ of type LL does not use the same type names as re-declaration $@ of type long | object2.c:11:11:11:12 | declaration of a3 | a3 | object1.c:5:6:5:7 | definition of a3 | a3 | +| object2.c:13:12:13:13 | declaration of a4 | The object $@ of type int does not use the same type names as re-declaration $@ of type long | object2.c:13:12:13:13 | declaration of a4 | a4 | object1.c:6:6:6:7 | definition of a4 | a4 | +| object2.c:15:13:15:14 | declaration of a5 | The object $@ of type long does not use the same type names as re-declaration $@ of type int | object2.c:15:13:15:14 | declaration of a5 | a5 | object1.c:7:5:7:6 | definition of a5 | a5 | +| object2.c:19:1:19:3 | declaration of a6 | The object $@ of type int does not use the same type names as re-declaration $@ of type long | object2.c:19:1:19:3 | declaration of a6 | a6 | object1.c:8:6:8:7 | definition of a6 | a6 | +| object2.c:21:11:21:12 | declaration of a7 | The object $@ of type LL does not use the same type names as re-declaration $@ of type int | object2.c:21:11:21:12 | declaration of a7 | a7 | object1.c:9:5:9:6 | definition of a7 | a7 | +| object2.c:24:4:24:6 | definition of a10 | The object $@ of type LI[100] does not use the same type names as re-declaration $@ of type int[100] | object2.c:24:4:24:6 | definition of a10 | a10 | object1.c:15:5:15:7 | definition of a10 | a10 | +| object2.c:25:12:25:14 | declaration of a11 | The object $@ of type int[101] does not use the same type names as re-declaration $@ of type int[100] | object2.c:25:12:25:14 | declaration of a11 | a11 | object1.c:16:5:16:7 | definition of a11 | a11 | +| object2.c:28:13:28:15 | declaration of a13 | The object $@ of type int * does not use the same type names as re-declaration $@ of type int *const | object2.c:28:13:28:15 | declaration of a13 | a13 | object1.c:19:12:19:14 | definition of a13 | a13 | +| object2.c:32:17:32:20 | definition of size | The object $@ of type unsigned char does not use the same type names as re-declaration $@ of type size_t | object2.c:32:17:32:20 | definition of size | size | object1.c:23:10:23:13 | definition of size | size | +| object2.c:33:3:33:4 | definition of s0 | The object $@ of type NamedStruct0 does not use the same type names as re-declaration $@ of type NamedStruct0 | object2.c:33:3:33:4 | definition of s0 | s0 | object1.c:24:3:24:4 | definition of s0 | s0 | +| object2.c:38:3:38:4 | definition of s1 | The object $@ of type NamedStruct1 does not use the same type names as re-declaration $@ of type NamedStruct1 | object2.c:38:3:38:4 | definition of s1 | s1 | object1.c:29:3:29:4 | definition of s1 | s1 | diff --git a/c/misra/test/rules/RULE-8-3/function1.c b/c/misra/test/rules/RULE-8-3/function1.c index 2072748047..7fcf775167 100644 --- a/c/misra/test/rules/RULE-8-3/function1.c +++ b/c/misra/test/rules/RULE-8-3/function1.c @@ -12,7 +12,7 @@ long f5(int f5a) { return 0; } // COMPLIANT int f6(int f6a) { return 0; } // NON_COMPLIANT -int f20(int f20a); // COMPLIANT - overloaded function +int f20(int f20a); // NON_COMPLIANT typedef int wi; typedef int hi; diff --git a/c/misra/test/rules/RULE-8-3/function2.c b/c/misra/test/rules/RULE-8-3/function2.c index 979e002466..b33dc73c1c 100644 --- a/c/misra/test/rules/RULE-8-3/function2.c +++ b/c/misra/test/rules/RULE-8-3/function2.c @@ -8,7 +8,7 @@ long f5(int f5a) { return 0; } // COMPLIANT long f6(int f6a) { return 0; } // NON_COMPLIANT -int f20(int f20a, int f20b); // COMPLIANT -- overloaded function +int f20(int f20a, int f20b); // NON_COMPLIANT typedef int wi; typedef int hi; diff --git a/change_notes/2025-02-25-move-type-related-libraries.md b/change_notes/2025-02-25-move-type-related-libraries.md new file mode 100644 index 0000000000..9f4fbd0bf2 --- /dev/null +++ b/change_notes/2025-02-25-move-type-related-libraries.md @@ -0,0 +1,2 @@ + - All rules using `Type.qll`, `TypeUses.qll`, `Pointers.qll`, `TrivialType.qll`, `VariablyModifiedTypes.qll`: + - Files moved into `cpp/common/types` directory. No external changes in behavior expected. \ No newline at end of file diff --git a/change_notes/2025-02-25-update-macro-deduplication-library.md b/change_notes/2025-02-25-update-macro-deduplication-library.md new file mode 100644 index 0000000000..90b3ef51af --- /dev/null +++ b/change_notes/2025-02-25-update-macro-deduplication-library.md @@ -0,0 +1,4 @@ +- `RULE-2-8` - `UnusedObjectDefinition.ql`, `UnusedObjectDefinitionStrict.ql`: + - Refactor to allow additional parameters in non-macro results for library `DeduplicateMacroResults.qll`. + - Refactor to replace `Location` with `Locatable` in API of library `DeduplicationMacroResults.qll`. + - No observable difference in behavior expected. diff --git a/change_notes/2025-03-04-essential-types-with-explicit-conversions.md b/change_notes/2025-03-04-essential-types-with-explicit-conversions.md new file mode 100644 index 0000000000..aa32044087 --- /dev/null +++ b/change_notes/2025-03-04-essential-types-with-explicit-conversions.md @@ -0,0 +1,2 @@ + - `EssentialType` - for all queries related to essential types: + - Updated the way essential types of expressions with "conversions" (including explicit casts, parenthesis, and implicit conversions such as array-to-pointer conversions) are handled, to get proper essential types when parenthesis, casts, and generics interact. \ No newline at end of file diff --git a/change_notes/2025-03-04-more-accurate-type-comparisons.md b/change_notes/2025-03-04-more-accurate-type-comparisons.md new file mode 100644 index 0000000000..942d76f7af --- /dev/null +++ b/change_notes/2025-03-04-more-accurate-type-comparisons.md @@ -0,0 +1,6 @@ + - `RULE-8-3` - `DeclarationsOfAFunctionSameNameAndType.ql`, `DeclarationsOfAnObjectSameNameAndType.ql`: + - New shared module used to fix false positives for compound types referring to the same basic integer types under a different name, e.g., query will not report for `signed[4]` used in place of `int[4]` as per MISRA spec. + - Now query will report incompatibilities for two functions of the same name with a different number of parameters. + - Query result string updated to not use the word "Compatible," which is confusing, as it may falsely appear that the query is testing for compatibility as defined by C17. + - `RULE-8-4`, `DCL-40C` - `CompatibleDeclarationFunctionDefined.ql`, `CompatibleDeclarationObjectDefined.ql`, `IncomptatibleFunctionDeclarations.ql`: + - New shared module used to fix false positives by updating "compatible" type checks to more closely match the C17 standard. For instance, `int[3]` and `int[]` are compatible declarations (while `int[3]` and `int[4]` are not), and typedefs are now resolved as well. Some false positives may still occur regarding structs from different compilation units. \ No newline at end of file diff --git a/cpp/autosar/src/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.ql b/cpp/autosar/src/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.ql index 9123e7de2f..0a59b423d0 100644 --- a/cpp/autosar/src/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.ql +++ b/cpp/autosar/src/rules/A0-4-1/FloatingPointImplementationShallComplyWithIeeeStandard.ql @@ -18,7 +18,7 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.TypeUses +import codingstandards.cpp.types.Uses class NumericLimits extends Class { NumericLimits() { this.hasQualifiedName("std", ["numeric_limits", "__libcpp_numeric_limits"]) } diff --git a/cpp/autosar/src/rules/A12-8-4/MoveConstructorUsesCopySemantics.ql b/cpp/autosar/src/rules/A12-8-4/MoveConstructorUsesCopySemantics.ql index 4996afd34e..a71d49d844 100644 --- a/cpp/autosar/src/rules/A12-8-4/MoveConstructorUsesCopySemantics.ql +++ b/cpp/autosar/src/rules/A12-8-4/MoveConstructorUsesCopySemantics.ql @@ -16,7 +16,7 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType /** * A literal with no values. diff --git a/cpp/autosar/src/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.ql b/cpp/autosar/src/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.ql index 7f9ced9909..61b00ba852 100644 --- a/cpp/autosar/src/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.ql +++ b/cpp/autosar/src/rules/A14-5-2/NonTemplateMemberDefinedInTemplate.ql @@ -15,7 +15,7 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.TypeUses +import codingstandards.cpp.types.Uses import codingstandards.cpp.Operator predicate templateDefinitionMentionsTypeParameter(Declaration d, TemplateParameter tp) { diff --git a/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql b/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql index a07dbd43f7..af3a00fadb 100644 --- a/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql +++ b/cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql @@ -15,7 +15,7 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType import codingstandards.cpp.SideEffect import semmle.code.cpp.controlflow.SSA import codingstandards.cpp.Expr diff --git a/cpp/autosar/src/rules/A8-4-7/TriviallyCopyableSmallType.qll b/cpp/autosar/src/rules/A8-4-7/TriviallyCopyableSmallType.qll index be7cd76bd2..09c6cdae69 100644 --- a/cpp/autosar/src/rules/A8-4-7/TriviallyCopyableSmallType.qll +++ b/cpp/autosar/src/rules/A8-4-7/TriviallyCopyableSmallType.qll @@ -1,6 +1,6 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType /** * Get the largest word size, in bytes. Some projects may have multiple different diff --git a/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll b/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll index c0a32baba9..8985f7254e 100644 --- a/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll +++ b/cpp/autosar/src/rules/M0-1-4/SingleUsePODVariable.qll @@ -1,7 +1,7 @@ /** A module providing predicates that support identifying single use non volatile POD variables. */ import cpp -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType import codingstandards.cpp.deadcode.UnusedVariables /** diff --git a/cpp/cert/src/rules/MEM53-CPP/ManuallyManagedLifetime.qll b/cpp/cert/src/rules/MEM53-CPP/ManuallyManagedLifetime.qll index 4392b598f7..0eaf9f8dfa 100644 --- a/cpp/cert/src/rules/MEM53-CPP/ManuallyManagedLifetime.qll +++ b/cpp/cert/src/rules/MEM53-CPP/ManuallyManagedLifetime.qll @@ -1,6 +1,6 @@ import codingstandards.cpp.cert import codingstandards.cpp.Conversion -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType import ManuallyManagedLifetime import semmle.code.cpp.controlflow.Dominance import semmle.code.cpp.dataflow.TaintTracking diff --git a/cpp/cert/src/rules/MEM53-CPP/MissingConstructorCallForManuallyManagedObject.ql b/cpp/cert/src/rules/MEM53-CPP/MissingConstructorCallForManuallyManagedObject.ql index 6e3121e46d..5398aa04e1 100644 --- a/cpp/cert/src/rules/MEM53-CPP/MissingConstructorCallForManuallyManagedObject.ql +++ b/cpp/cert/src/rules/MEM53-CPP/MissingConstructorCallForManuallyManagedObject.ql @@ -12,7 +12,7 @@ import cpp import codingstandards.cpp.cert -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType import ManuallyManagedLifetime import semmle.code.cpp.dataflow.TaintTracking import AllocToStaticCastFlow::PathGraph diff --git a/cpp/common/src/codingstandards/cpp/Compatible.qll b/cpp/common/src/codingstandards/cpp/Compatible.qll deleted file mode 100644 index 0f6e2108ff..0000000000 --- a/cpp/common/src/codingstandards/cpp/Compatible.qll +++ /dev/null @@ -1,30 +0,0 @@ -import cpp - -pragma[noinline] -pragma[nomagic] -predicate typesCompatible(Type t1, Type t2) { - t1 = t2 - or - //signed int is same as int ect - t1.(IntegralType).getCanonicalArithmeticType() = t2.(IntegralType).getCanonicalArithmeticType() -} - -predicate parameterTypesIncompatible(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) { - f1.getDeclaration() = f2.getDeclaration() and - exists(ParameterDeclarationEntry p1, ParameterDeclarationEntry p2, int i | - p1 = f1.getParameterDeclarationEntry(i) and - p2 = f2.getParameterDeclarationEntry(i) - | - not typesCompatible(p1.getType(), p2.getType()) - ) -} - -predicate parameterNamesIncompatible(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) { - f1.getDeclaration() = f2.getDeclaration() and - exists(ParameterDeclarationEntry p1, ParameterDeclarationEntry p2, int i | - p1 = f1.getParameterDeclarationEntry(i) and - p2 = f2.getParameterDeclarationEntry(i) - | - not p1.getName() = p2.getName() - ) -} diff --git a/cpp/common/src/codingstandards/cpp/MatchingParenthesis.qll b/cpp/common/src/codingstandards/cpp/MatchingParenthesis.qll new file mode 100644 index 0000000000..8a28bd0517 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/MatchingParenthesis.qll @@ -0,0 +1,264 @@ +/** + * A library for parsing a string of parentheses and non-parentheses characters. + * + * Simply implement the signature class `InputString` for the set of strings that you wish to parse, + * and then use the `MatchingParenthesis` module which exposes the following classes/predicates: + * - `ParsedRoot`: The root of the parse tree. + * - `ParsedGroup`: Parenthesis groups. The root is also a group, even if not parenthesized. + * - `ParsedText`: All text that is not '(' or ')'. + * - `Tokenized`: A linked list of the tokens in the input string. + * - `textFrom(start, end)`: A function to get the text between two tokens. + * + * The parenthesis AST has functions `getChild(int i)` and `getParent()` to navigate the tree, as + * well as `getRoot()` and `getText()` for `ParsedText` nodes. They also have methods + * `getStartToken()`, `getEndToken()` which are especially useful with the method `textFrom(...)`. + * + * This module can allow for slightly more intelligent interpretation of macro strings, but it has + * limitations. + * - It _only_ handles the parenthesis. + * - It assumes parentheses are matched. + * - It does not handle the case where a parenthesis is inside a string literal. + * - It does not handle the case where a parenthesis is inside a comment. + * + * This module has been moderately optimized, but still it is best to be selective with the set of + * strings you attempt to parse with it. + */ + +import codeql.util.Option + +signature class InputString extends string; + +module MatchingParenthesis { + newtype TTokenType = + TOpenParen() or + TCloseParen() or + TNotParen() + + bindingset[char] + private TTokenType tokenTypeOfChar(string char) { + result = TOpenParen() and char = "(" + or + result = TCloseParen() and char = ")" + } + + private int inputId(Input i) { rank[result](Input inp) = i } + + private newtype TTokenized = + TTokenizerStart(int iid) { iid = inputId(_) } or + TToken(int iid, TTokenized prev, TTokenType token, int occurrence, int endPos) { + exists(string inputStr, int prevEndPos, int prevOccurrence, string char | + iid = inputId(inputStr) and + ( + prev = TTokenizerStart(iid) and prevOccurrence = -1 and prevEndPos = 0 + or + prev = TToken(iid, _, _, prevOccurrence, prevEndPos) + ) and + inputStr.charAt(prevEndPos) = char and + if char = ["(", ")"] + then ( + endPos = prevEndPos + 1 and + token = tokenTypeOfChar(char) and + occurrence = prevOccurrence + 1 + ) else ( + token = TNotParen() and + exists(inputStr.regexpFind("\\(|\\)", prevOccurrence + 1, endPos)) and + occurrence = prevOccurrence + ) + ) + } + + class Tokenized extends TTokenized { + string toString() { + getTokenType() = TOpenParen() and result = "(" + or + getTokenType() = TCloseParen() and result = ")" + or + getTokenType() = TNotParen() and result = "non-parenthesis" + } + + int getInputId() { this = TToken(result, _, _, _, _) } + + TTokenType getTokenType() { this = TToken(_, _, result, _, _) } + + Tokenized getPrevious() { this = TToken(_, result, _, _, _) } + + string getInputString() { + this = TToken(inputId(result), _, _, _, _) or this = TTokenizerStart(inputId(result)) + } + + int getStartPos() { + if exists(getPrevious()) then result = getPrevious().getEndPos() else result = 0 + } + + int getEndPos() { + this = TToken(_, _, _, _, result) + or + this = TTokenizerStart(_) and result = 0 + } + + string getText() { result = textFrom(this, this) } + + Tokenized getNext() { result.getPrevious() = this } + + Tokenized getLast() { + if exists(getNext()) then result = getNext().getLast() else result = this + } + } + + /** + * The root of the parse tree. + */ + class ParsedRoot extends ParsedGroup { + ParsedRoot() { not exists(getParent()) } + + override ParsedRoot getRoot() { result = this } + + override string getDebugText() { result = this.(Tokenized).getInputString() } + } + + /** + * A group of tokens that may be parenthesized. + * + * The `ParseRoot` is the only group that isn't parenthesized. + */ + class ParsedGroup extends Parsed { + ParsedGroup() { isGroup() } + + Parsed getChild(int i) { + result.getParent() = this and + result.getChildIdx() = i + } + } + + /** + * Get the text from the `start` token to the `end` token (inclusive on both ends). + */ + pragma[inline] + string textFrom(Tokenized start, Tokenized end) { + result = start.getInputString().substring(start.getStartPos(), end.getEndPos()) + } + + /** + * All text that is not '(' or ')'. + */ + class ParsedText extends Parsed { + ParsedText() { not isGroup() } + + string getText() { result = textFrom(getStartToken(), getEndToken()) } + } + + /** + * The AST for the input string parsed with matching parenthesis. + */ + class Parsed extends TTokenized { + Option::Option parent; + int childIdx; + boolean isGroup; + + Parsed() { + this.(Tokenized).getTokenType() = TNotParen() and + parseStepAppend(this, parent.asSome(), childIdx) and + isGroup = false + or + this.(Tokenized).getTokenType() = TOpenParen() and + parseStepOpen(this, parent.asSome(), childIdx) and + isGroup = true + or + this = TTokenizerStart(_) and + parent.isNone() and + childIdx = 0 and + isGroup = true + } + + ParsedRoot getRoot() { result = getParent().getRoot() } + + string getInputString() { result = this.(Tokenized).getInputString() } + + /** + * The token that starts this group. + * + * For `ParsedText`, this is the same as the end token. + */ + Tokenized getStartToken() { result = this } + + /** + * The token that endns this group. + * + * For `ParsedText`, this is the same as the start token. If parentheses are not matched, this + * may not have a result. + */ + Tokenized getEndToken() { + this.(Tokenized).getTokenType() = TNotParen() and + result = this + or + this.(Tokenized).getTokenType() = TOpenParen() and + parseStepClose(result, this) + or + this = TTokenizerStart(_) and + result = getStartToken().(Tokenized).getLast() + } + + /** + * The index of this child in the parent group. + */ + int getChildIdx() { result = childIdx } + + ParsedGroup getParent() { result = parent.asSome() } + + predicate isGroup() { isGroup = true } + + string getDebugText() { result = textFrom(getStartToken(), getEndToken()) } + + string toString() { result = this.(Tokenized).toString() } + } + + /** + * Parse open parenthesis and add it to the open group or parse root. Parsing algorithm may not + * behave reliably for mismatched parenthesis. + */ + private predicate parseStepOpen(Tokenized consumeToken, ParsedGroup parent, int childIdx) { + consumeToken.getTokenType() = TOpenParen() and + ( + consumeToken.getPrevious() = parent.getStartToken() and + childIdx = 0 + or + exists(Parsed prevSibling | + prevSibling.getEndToken() = consumeToken.getPrevious() and + childIdx = prevSibling.getChildIdx() + 1 and + parent = prevSibling.getParent() + ) + ) + } + + /** + * Parse raw text that isn't '(' or ')' and add it to the open group or parse root. + */ + private predicate parseStepAppend(Tokenized consumeToken, ParsedGroup parent, int childIdx) { + consumeToken.getTokenType() = TNotParen() and + ( + consumeToken.getPrevious() = parent.getStartToken() and childIdx = 0 + or + exists(Parsed prevSibling | + prevSibling.getEndToken() = consumeToken.getPrevious() and + childIdx = prevSibling.getChildIdx() + 1 and + parent = prevSibling.getParent() + ) + ) + } + + /** + * Parse a close parenthesis to close the currently open group. Parsing algorithm may not behave + * properly for mismatched parenthesis. + */ + private predicate parseStepClose(Tokenized consumeToken, ParsedGroup closed) { + consumeToken.getTokenType() = TCloseParen() and + ( + closed.getStartToken() = consumeToken.getPrevious() + or + exists(Parsed finalChild | + consumeToken.getPrevious() = finalChild.getEndToken() and + finalChild.getParent() = closed + ) + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/Type.qll b/cpp/common/src/codingstandards/cpp/Type.qll index 42d77b8055..32c139fd89 100644 --- a/cpp/common/src/codingstandards/cpp/Type.qll +++ b/cpp/common/src/codingstandards/cpp/Type.qll @@ -1,96 +1,2 @@ -/** - * A module for representing different `Type`s. - */ - -import cpp - -/** - * A fundamental type, as defined by `[basic.fundamental]`. - */ -class FundamentalType extends BuiltInType { - FundamentalType() { - // A fundamental type is any `BuiltInType` except types indicating errors during extraction, or - // "unknown" types inserted into uninstantiated templates - not this instanceof ErroneousType and - not this instanceof UnknownType - } -} - -/** - * A type that is incomplete. - */ -class IncompleteType extends Class { - IncompleteType() { not hasDefinition() } -} - -/** - * A type that implements the BitmaskType trait. - * https://en.cppreference.com/w/cpp/named_req/BitmaskType - */ -abstract class BitmaskType extends Type { } - -/** - * Holds if `enum` implements required overload `overload` to implement - * the BitmaskType trait. - */ -private predicate isRequiredEnumOverload(Enum enum, Function overload) { - overload.getName().regexpMatch("operator([&|^~]|&=|\\|=)") and - forex(Parameter p | p = overload.getAParameter() | - ( - p.getType() = enum - or - p.getType().(ReferenceType).getBaseType() = enum - ) - ) -} - -private class EnumBitmaskType extends BitmaskType, Enum { - EnumBitmaskType() { - // Implements all the required overload - count(Function overload | isRequiredEnumOverload(this, overload)) = 6 - } -} - -/** - * A type without `const` and `volatile` specifiers. - */ -Type stripSpecifiers(Type type) { - if type instanceof SpecifiedType - then result = stripSpecifiers(type.(SpecifiedType).getBaseType()) - else result = type -} - -signature class PossiblySpecifiedBaseType extends Type; - -/** - * This module defines a class `Type` which holds for types `T` and `const/volatile T` etc. - * - * Similar to `getUnspecifiedType()`, but does not resolve typedefs. Useful for matching - * potentially qualified versions of standard typedef types, such as `const mtx_t`. - * - * Example usage: `someType.(PossiblySpecified::Type).strip()` - */ -module PossiblySpecified { - import cpp as cpp - - final class CppType = cpp::Type; - - class Type extends CppType { - BaseType baseType; - - Type() { baseType = stripSpecifiers(this) } - - BaseType strip() { result = baseType } - } -} - -/** - * Get the precision of an integral type, where precision is defined as the number of bits - * that can be used to represent the numeric value. - * https://wiki.sei.cmu.edu/confluence/display/c/INT35-C.+Use+correct+integer+precisions - */ -int getPrecision(IntegralType type) { - type.isExplicitlyUnsigned() and result = type.getSize() * 8 - or - type.isExplicitlySigned() and result = type.getSize() * 8 - 1 -} +import codingstandards.cpp.types.Type +import codingstandards.cpp.types.Uses \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/alertreporting/DeduplicateMacroResults.qll b/cpp/common/src/codingstandards/cpp/alertreporting/DeduplicateMacroResults.qll index b3c3d44ff4..b41de3ef9a 100644 --- a/cpp/common/src/codingstandards/cpp/alertreporting/DeduplicateMacroResults.qll +++ b/cpp/common/src/codingstandards/cpp/alertreporting/DeduplicateMacroResults.qll @@ -38,7 +38,7 @@ signature module MacroReportConfigSig { /** * Create a message to describe a `ResultElement` which is not generated by a macro expansion. */ - string getMessageNotInMacro(ResultElement element); + string getMessageNotInMacro(ResultElement element, Locatable optExtraLoc1, string optExtraStr1); } /** @@ -120,7 +120,7 @@ signature module MacroReportConfigSig { * * from Report::ReportResult report * where not excluded(report.getPrimaryElement(), ...) - * select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocation(), + * select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocatable(), * report.getOptionalPlaceholderMessage() * ``` * @@ -295,7 +295,7 @@ module DeduplicateMacroResults< * To show a report, use the following methods: * - `report.getPrimaryElement()` * - `report.getMessage()` - * - `report.getOptionalPlaceholderLocation()` + * - `report.getOptionalPlaceholderLocatable()` * - `report.getOptionalPlaceholderMessage()` * * The values returned by these methods are configured by the `MacroReportConfigSig` @@ -337,7 +337,7 @@ module DeduplicateMacroResults< or exists(ResultElement def | this = TReportNotInMacro(def) and - result = ReportConfig::getMessageNotInMacro(def) + result = ReportConfig::getMessageNotInMacro(def, _, _) ) } @@ -351,25 +351,25 @@ module DeduplicateMacroResults< this = TReportNotInMacro(result) } - Location getOptionalPlaceholderLocation() { + Locatable getOptionalPlaceholderLocatable() { exists(PrimaryMacroDifferentResultElementInAllInvocations def | this = TReportMacroResultWithVariedName(def) and - result = def.getExampleResultElement().getLocation() + result = def.getExampleResultElement() ) or exists(PrimaryMacroSameResultElementInAllInvocations def | this = TReportMacroResultWithSameName(def) and - result = def.getLocation() + result = def ) or exists(IsolatedMacroExpansionWithResultElement def | this = TReportIsolatedMacroResult(def) and - result = def.getMacro().getLocation() + result = def.getMacro() ) or exists(ResultElement def | this = TReportNotInMacro(def) and - result = def.getLocation() + exists(ReportConfig::getMessageNotInMacro(def, result, _)) ) } @@ -379,14 +379,16 @@ module DeduplicateMacroResults< result = Config::describe(def.getExampleResultElement()) ) or - ( - this = TReportMacroResultWithSameName(_) or - this = TReportNotInMacro(_) - ) and + this = TReportMacroResultWithSameName(_) and result = "(ignored)" or this = TReportIsolatedMacroResult(_) and result = getMacro().getName() + or + exists(ResultElement def | + this = TReportNotInMacro(def) and + exists(ReportConfig::getMessageNotInMacro(def, _, result)) + ) } Macro getMacro() { diff --git a/cpp/common/src/codingstandards/cpp/deadcode/UnusedObjects.qll b/cpp/common/src/codingstandards/cpp/deadcode/UnusedObjects.qll index 60e732873a..94ae16ec4f 100644 --- a/cpp/common/src/codingstandards/cpp/deadcode/UnusedObjects.qll +++ b/cpp/common/src/codingstandards/cpp/deadcode/UnusedObjects.qll @@ -57,8 +57,10 @@ module ReportDeadObjectConfig implements MacroReportConfigSig; + +/** + * Signature module for handling various kinds of potentially recursive type equivalence using the + * module `TypeEquivalence`. + * + * The various kinds of types to be compared all have an overridable predicate with default + * behavior here, and a boolean flag that indicates whether the base types are equal. This pattern + * is used because we can't make a default implementation of a predicate such as + * `equalPointerTypes` that recurses into the `TypeEquivalence` module. Instead, the + * `TypeEquivalence` module drives all of the recursion, and these predicates take the result of + * that recursion and use it to determine whether the types are equivalent. + */ +signature module TypeEquivalenceSig { + /** + * Whether two leaf types are equivalent, such as `int`s and structs. By default, we assume only + * that types are equal to themselves and that equivalent arithmetic types are equal. + */ + bindingset[t1, t2] + default predicate equalLeafTypes(Type t1, Type t2) { + t1 = t2 + or + t1.(IntegralType).getCanonicalArithmeticType() = t2.(IntegralType).getCanonicalArithmeticType() + } + + /** + * A predicate to arbitrarily override the default behavior of the `TypeEquivalence` module, + * including preventing recursion. If this predicate holds for a pair of types, then + * `TypeEquivalence::equalTypes()` holds only if `areEqual` is true. + */ + bindingset[t1, t2] + default predicate overrideTypeComparison(Type t1, Type t2, Boolean areEqual) { none() } + + /** + * Whether two specified types are equivalent. By default, we assume that the specifier sets are + * exactly the same, and the inner types also match. + */ + bindingset[t1, t2] + default predicate equalSpecifiedTypes( + SpecifiedType t1, SpecifiedType t2, Boolean unspecifiedTypesEqual + ) { + specifiersMatchExactly(t1, t2) and + unspecifiedTypesEqual = true + } + + /** + * Whether two specified types are equivalent. By default, we only require that the base (pointed + * to) types match. + */ + bindingset[t1, t2] + default predicate equalPointerTypes(PointerType t1, PointerType t2, Boolean baseTypesEqual) { + baseTypesEqual = true + } + + /** + * Whether two array types are equivalent. By default, we only require that the element types and + * array sizes match. + */ + bindingset[t1, t2] + default predicate equalArrayTypes(ArrayType t1, ArrayType t2, Boolean baseTypesEqual) { + t1.getSize() = t2.getSize() and + baseTypesEqual = true + } + + /** + * Whether two reference types are equivalent. By default, we only require that the base types match. + */ + bindingset[t1, t2] + default predicate equalReferenceTypes(ReferenceType t1, ReferenceType t2, Boolean baseTypesEqual) { + baseTypesEqual = true + } + + /** + * Whether typedefs should be resolved before comparison. By default, we assume `TypeEquivalence` + * should resolve typedefs before comparison. + */ + default predicate resolveTypedefs() { any() } + + /** + * Whether two typedef types are equivalent. + * + * This predicate is only used if `resolveTypedefs()` is false. If so, then we assume two + * typedefs are the same if they have the same name and their base types are equal. + */ + bindingset[t1, t2] + default predicate equalTypedefTypes(TypedefType t1, TypedefType t2, Boolean baseTypesEqual) { + t1.getName() = t2.getName() and + baseTypesEqual = true + } + + /** + * Whether two routine types are equivalent. By default, we only require that the return types and + * parameter types match. + */ + bindingset[t1, t2] + default predicate equalRoutineTypes( + RoutineType t1, RoutineType t2, Boolean returnTypeEqual, Boolean parameterTypesEqual + ) { + returnTypeEqual = true and parameterTypesEqual = true + } + + /** + * Whether two function pointer/reference types are equivalent. By default, we only require that + * the return types and parameter types match. + */ + bindingset[t1, t2] + default predicate equalFunctionPointerIshTypes( + FunctionPointerIshType t1, FunctionPointerIshType t2, Boolean returnTypeEqual, + Boolean parameterTypesEqual + ) { + returnTypeEqual = true and parameterTypesEqual = true + } +} + +/** + * The default equivalence behavior for the `TypeEquivalence` module. + */ +module DefaultEquivalence implements TypeEquivalenceSig { } + +/** + * A signature class used to restrict the set of types considered by `TypeEquivalence`, for + * performance reasons. + */ +signature class TypeSubset extends Type; + +/** + * A module to check the equivalence of two types, as defined by the provided `TypeEquivalenceSig`. + * + * For performance reasons, this module is designed to be used with a `TypeSubset` that restricts + * the set of considered types. All types reachable (in the type graph) from a type in the subset + * will be considered. (See `RelevantType`.) + * + * To use this module, define a `TypeEquivalenceSig` module and implement a subset of `Type` that + * selects the relevant root types to be considered. Then use the predicate `equalTypes(a, b)`. + */ +module TypeEquivalence { + /** + * Check whether two types are equivalent, as defined by the `TypeEquivalenceSig` module. + */ + predicate equalTypes(RelevantType t1, RelevantType t2) { + if Config::overrideTypeComparison(t1, t2, _) + then Config::overrideTypeComparison(t1, t2, true) + else + if t1 instanceof TypedefType and Config::resolveTypedefs() + then equalTypes(t1.(TypedefType).getBaseType(), t2) + else + if t2 instanceof TypedefType and Config::resolveTypedefs() + then equalTypes(t1, t2.(TypedefType).getBaseType()) + else ( + not t1 instanceof DerivedType and + not t2 instanceof DerivedType and + not t1 instanceof TypedefType and + not t2 instanceof TypedefType and + LeafEquiv::getEquivalenceClass(t1) = LeafEquiv::getEquivalenceClass(t2) + or + equalDerivedTypes(t1, t2) + or + equalTypedefTypes(t1, t2) + or + equalFunctionTypes(t1, t2) + ) + } + + /** + * A type that is either part of the type subset, or that is reachable from a type in the subset. + */ + private class RelevantType instanceof Type { + RelevantType() { exists(T t | typeGraph*(t, this)) } + + string toString() { result = this.(Type).toString() } + } + + private class RelevantDerivedType extends RelevantType instanceof DerivedType { + RelevantType getBaseType() { result = this.(DerivedType).getBaseType() } + } + + private class RelevantFunctionType extends RelevantType instanceof FunctionType { + RelevantType getReturnType() { result = this.(FunctionType).getReturnType() } + + RelevantType getParameterType(int i) { result = this.(FunctionType).getParameterType(i) } + } + + private class RelevantTypedefType extends RelevantType instanceof TypedefType { + RelevantType getBaseType() { result = this.(TypedefType).getBaseType() } + } + + private module LeafEquiv = QlBuiltins::EquivalenceRelation; + + private predicate equalLeafRelation(RelevantType t1, RelevantType t2) { + Config::equalLeafTypes(t1, t2) + } + + private RelevantType unspecify(SpecifiedType t) { + // This subtly and importantly handles the complicated cases of typedefs. Under most scenarios, + // if we see a typedef in `equalTypes()` we can simply get the base type and continue. However, + // there is an exception if we have a specified type that points to a typedef that points to + // another specified type. In this case, `SpecifiedType::getASpecifier()` will return all of + // specifiers, not just those above the TypedefType, and `stripTopLevelSpecifiers` will return + // the innermost type that is not a TypedefType or a SpecifiedType, which is what we want, as + // all specifiers have already been accounted for when we visit the outermost `SpecifiedType`. + if Config::resolveTypedefs() + then result = t.(SpecifiedType).stripTopLevelSpecifiers() + else result = t.(SpecifiedType).getBaseType() + } + + bindingset[t1, t2] + private predicate equalDerivedTypes(RelevantDerivedType t1, RelevantDerivedType t2) { + exists(Boolean baseTypesEqual | + (baseTypesEqual = true implies equalTypes(t1.getBaseType(), t2.getBaseType())) and + ( + Config::equalPointerTypes(t1, t2, baseTypesEqual) + or + Config::equalArrayTypes(t1, t2, baseTypesEqual) + or + Config::equalReferenceTypes(t1, t2, baseTypesEqual) + ) + ) + or + exists(Boolean unspecifiedTypesEqual | + // Note that this case is different from the above, in that we don't merely get the base + // type (as that could be a TypedefType that points to another SpecifiedType). We need to + // unspecify the type to see if the base types are equal. + (unspecifiedTypesEqual = true implies equalTypes(unspecify(t1), unspecify(t2))) and + Config::equalSpecifiedTypes(t1, t2, unspecifiedTypesEqual) + ) + } + + bindingset[t1, t2] + private predicate equalFunctionTypes(RelevantFunctionType t1, RelevantFunctionType t2) { + exists(Boolean returnTypeEqual, Boolean parameterTypesEqual | + (returnTypeEqual = true implies equalTypes(t1.getReturnType(), t2.getReturnType())) and + ( + parameterTypesEqual = true + implies + forall(int i | exists([t1, t2].getParameterType(i)) | + equalTypes(t1.getParameterType(i), t2.getParameterType(i)) + ) + ) and + ( + Config::equalRoutineTypes(t1, t2, returnTypeEqual, parameterTypesEqual) + or + Config::equalFunctionPointerIshTypes(t1, t2, returnTypeEqual, parameterTypesEqual) + ) + ) + } + + bindingset[t1, t2] + private predicate equalTypedefTypes(RelevantTypedefType t1, RelevantTypedefType t2) { + exists(Boolean baseTypesEqual | + (baseTypesEqual = true implies equalTypes(t1.getBaseType(), t2.getBaseType())) and + Config::equalTypedefTypes(t1, t2, baseTypesEqual) + ) + } +} + +module FunctionDeclarationTypeEquivalence { + predicate equalReturnTypes(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) { + TypeEquivalence::equalTypes(f1.getType(), f2.getType()) + } + + predicate equalParameterTypes(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) { + f1.getDeclaration() = f2.getDeclaration() and + forall(int i | exists([f1, f2].getParameterDeclarationEntry(i)) | + TypeEquivalence::equalTypes(f1.getParameterDeclarationEntry(i) + .getType(), f2.getParameterDeclarationEntry(i).getType()) + ) + } +} + +/** + * Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType` + * don't have a common ancestor. + */ +private class FunctionType extends Type { + FunctionType() { this instanceof RoutineType or this instanceof FunctionPointerIshType } + + Type getReturnType() { + result = this.(RoutineType).getReturnType() or + result = this.(FunctionPointerIshType).getReturnType() + } + + Type getParameterType(int i) { + result = this.(RoutineType).getParameterType(i) or + result = this.(FunctionPointerIshType).getParameterType(i) + } +} +/* + * predicate typesCompatibleImpl(Type t1, Type t2) { + * // A type is compatible with itself + * t1 = t2 + * or + * // All specifiers must match, but the order does not matter: + * ( + * t1 instanceof SpecifiedType and + * t2 instanceof SpecifiedType + * ) and + * specifiersMatchExactly(t1, t2) and + * typesCompatibleImpl(t1.stripTopLevelSpecifiers(), t2.stripTopLevelSpecifiers()) + * or + * // Identically qualified pointers are compatible if they point to compatible types. + * typesCompatibleImpl(t1.(PointerType).getBaseType(), t2.(PointerType).getBaseType()) + * or + * // Array objects are compatible if they have a compatible element type. If both have a constant + * // size then that size must match. + * typesCompatibleImpl(t1.(ArrayType).getBaseType(), t2.(ArrayType).getBaseType()) and + * count(int i | i = [t1, t2].(ArrayType).getSize()) < 2 + * or + * // Enum types are compatible with one of char, int, or signed int, but the implementation + * // decides. + * [t1, t2] instanceof Enum and + * ([t1, t2] instanceof CharType or [t1, t2] instanceof IntType) + * or + * // `int` is the same as `signed`, `signed int`, while `unsigned` is the same as `unsigned int`. + * t1.(IntegralType).getCanonicalArithmeticType() = t2.(IntegralType).getCanonicalArithmeticType() + * or + * // Function types are compatible if they have the same return type and compatible parameters. + * // Technically, variadic functions are have special behavior not covered here. + * exists(RoutineType f1, RoutineType f2 | f1 = t1 and f2 = t2 | + * typesCompatibleImpl(f1.getReturnType(), f2.getReturnType()) and + * forall(int i | exists([f1, f2].getParameterType(i)) | + * typesCompatibleImpl(f1.getParameterType(i), f2.getParameterType(i)) + * ) + * ) + * or + * // Function pointer types should be covered by `PointerType` and `RoutineType` above, but that is + * // not how they are implemented in CodeQL. + * exists(FunctionPointerIshType f1, FunctionPointerIshType f2 | f1 = t1 and f2 = t2 | + * typesCompatibleImpl(f1.getReturnType(), f2.getReturnType()) and + * forall(int i | exists([f1, f2].getParameterType(i)) | + * typesCompatibleImpl(f1.getParameterType(i), f2.getParameterType(i)) + * ) + * ) + * } + */ + diff --git a/cpp/common/src/codingstandards/cpp/types/Graph.qll b/cpp/common/src/codingstandards/cpp/types/Graph.qll new file mode 100644 index 0000000000..70c51a40ba --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/Graph.qll @@ -0,0 +1,15 @@ +import cpp + +predicate typeGraph(Type t, Type refersTo) { + refersTo = t.(DerivedType).getBaseType() + or + refersTo = t.(RoutineType).getReturnType() + or + refersTo = t.(RoutineType).getAParameterType() + or + refersTo = t.(FunctionPointerIshType).getReturnType() + or + refersTo = t.(FunctionPointerIshType).getAParameterType() + or + refersTo = t.(TypedefType).getBaseType() +} diff --git a/cpp/common/src/codingstandards/cpp/types/LvalueConversion.qll b/cpp/common/src/codingstandards/cpp/types/LvalueConversion.qll new file mode 100644 index 0000000000..252e783438 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/LvalueConversion.qll @@ -0,0 +1,38 @@ +import cpp + +/** + * Get the type of an lvalue after lvalue conversion. + * + * This will return the type itself if no conversion is performed. + */ +Type getLvalueConverted(Type t) { + if exists(performLvalueConversion(t, _)) + then result = performLvalueConversion(t, _) + else result = t +} + +/** + * Perform lvalue conversion on a type, allowing for a description of why the type was converted + * if it was. + * + * Does not return a value if no lvalue conversion was performed. + * + * Warning: This predicate may not return a result if the resulting type is not in the database. + * For convenience, this is accepted here, otherwise we would have to create a new type to return + * that wouldn't implement the type APIs and likely wouldn't be very useful. + */ +Type performLvalueConversion(Type t, string reason) { + result.(PointerType).getBaseType() = t.(ArrayType).getBaseType() and + reason = "array-to-pointer decay" + or + t instanceof RoutineType and + result.(PointerType).getBaseType() = t and + reason = "function-to-function-pointer decay" + or + isObjectType(t) and + exists(t.getASpecifier()) and + result = t.stripTopLevelSpecifiers() and + reason = "qualifiers removed" +} + +private predicate isObjectType(Type t) { not t.stripTopLevelSpecifiers() instanceof PointerType } diff --git a/cpp/common/src/codingstandards/cpp/Pointers.qll b/cpp/common/src/codingstandards/cpp/types/Pointers.qll similarity index 100% rename from cpp/common/src/codingstandards/cpp/Pointers.qll rename to cpp/common/src/codingstandards/cpp/types/Pointers.qll diff --git a/cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll b/cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll new file mode 100644 index 0000000000..e9804c29c4 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll @@ -0,0 +1,45 @@ +import codingstandards.cpp.types.LvalueConversion +import codingstandards.cpp.types.Compatible + +module SimpleAssignment { + final private class FinalType = Type; + + private class RelevantType extends FinalType { + RelevantType() { exists(T t | typeGraph*(t, this) or typeGraph(getLvalueConverted(t), this)) } + + string toString() { result = "relevant type" } + } + + /** + * Whether a pair of qualified or unqualified pointer types satisfy the simple assignment + * constraints from 6.5.16.1. + * + * There are additional constraints not implemented here involving one or more arithmetic types. + */ + predicate satisfiesSimplePointerAssignment(RelevantType left, RelevantType right) { + simplePointerAssignmentImpl(getLvalueConverted(left), right) + } + + /** + * Implementation of 6.5.16.1 for a pair of pointer types, that assumes lvalue conversion has been + * performed on the left operand. + */ + private predicate simplePointerAssignmentImpl(RelevantType left, RelevantType right) { + exists(RelevantType leftBase, RelevantType rightBase | + // The left operand has atomic, qualified, or unqualified pointer type: + leftBase = left.stripTopLevelSpecifiers().(PointerType).getBaseType() and + rightBase = right.stripTopLevelSpecifiers().(PointerType).getBaseType() and + ( + // and both operands are pointers to qualified or unqualified versions of compatible types: + TypeEquivalence::equalTypes(leftBase + .stripTopLevelSpecifiers(), rightBase.stripTopLevelSpecifiers()) + or + // or one operand is a pointer to a qualified or unqualified version of void + [leftBase, rightBase].stripTopLevelSpecifiers() instanceof VoidType + ) and + // and the type pointed to by the left has all the qualifiers of the type pointed to by the + // right: + forall(Specifier s | s = rightBase.getASpecifier() | s = leftBase.getASpecifier()) + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/TrivialType.qll b/cpp/common/src/codingstandards/cpp/types/TrivialType.qll similarity index 100% rename from cpp/common/src/codingstandards/cpp/TrivialType.qll rename to cpp/common/src/codingstandards/cpp/types/TrivialType.qll diff --git a/cpp/common/src/codingstandards/cpp/types/Type.qll b/cpp/common/src/codingstandards/cpp/types/Type.qll new file mode 100644 index 0000000000..42d77b8055 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/Type.qll @@ -0,0 +1,96 @@ +/** + * A module for representing different `Type`s. + */ + +import cpp + +/** + * A fundamental type, as defined by `[basic.fundamental]`. + */ +class FundamentalType extends BuiltInType { + FundamentalType() { + // A fundamental type is any `BuiltInType` except types indicating errors during extraction, or + // "unknown" types inserted into uninstantiated templates + not this instanceof ErroneousType and + not this instanceof UnknownType + } +} + +/** + * A type that is incomplete. + */ +class IncompleteType extends Class { + IncompleteType() { not hasDefinition() } +} + +/** + * A type that implements the BitmaskType trait. + * https://en.cppreference.com/w/cpp/named_req/BitmaskType + */ +abstract class BitmaskType extends Type { } + +/** + * Holds if `enum` implements required overload `overload` to implement + * the BitmaskType trait. + */ +private predicate isRequiredEnumOverload(Enum enum, Function overload) { + overload.getName().regexpMatch("operator([&|^~]|&=|\\|=)") and + forex(Parameter p | p = overload.getAParameter() | + ( + p.getType() = enum + or + p.getType().(ReferenceType).getBaseType() = enum + ) + ) +} + +private class EnumBitmaskType extends BitmaskType, Enum { + EnumBitmaskType() { + // Implements all the required overload + count(Function overload | isRequiredEnumOverload(this, overload)) = 6 + } +} + +/** + * A type without `const` and `volatile` specifiers. + */ +Type stripSpecifiers(Type type) { + if type instanceof SpecifiedType + then result = stripSpecifiers(type.(SpecifiedType).getBaseType()) + else result = type +} + +signature class PossiblySpecifiedBaseType extends Type; + +/** + * This module defines a class `Type` which holds for types `T` and `const/volatile T` etc. + * + * Similar to `getUnspecifiedType()`, but does not resolve typedefs. Useful for matching + * potentially qualified versions of standard typedef types, such as `const mtx_t`. + * + * Example usage: `someType.(PossiblySpecified::Type).strip()` + */ +module PossiblySpecified { + import cpp as cpp + + final class CppType = cpp::Type; + + class Type extends CppType { + BaseType baseType; + + Type() { baseType = stripSpecifiers(this) } + + BaseType strip() { result = baseType } + } +} + +/** + * Get the precision of an integral type, where precision is defined as the number of bits + * that can be used to represent the numeric value. + * https://wiki.sei.cmu.edu/confluence/display/c/INT35-C.+Use+correct+integer+precisions + */ +int getPrecision(IntegralType type) { + type.isExplicitlyUnsigned() and result = type.getSize() * 8 + or + type.isExplicitlySigned() and result = type.getSize() * 8 - 1 +} diff --git a/cpp/common/src/codingstandards/cpp/TypeUses.qll b/cpp/common/src/codingstandards/cpp/types/Uses.qll similarity index 100% rename from cpp/common/src/codingstandards/cpp/TypeUses.qll rename to cpp/common/src/codingstandards/cpp/types/Uses.qll diff --git a/cpp/common/src/codingstandards/cpp/VariablyModifiedTypes.qll b/cpp/common/src/codingstandards/cpp/types/VariablyModifiedTypes.qll similarity index 100% rename from cpp/common/src/codingstandards/cpp/VariablyModifiedTypes.qll rename to cpp/common/src/codingstandards/cpp/types/VariablyModifiedTypes.qll diff --git a/cpp/common/test/guideline_recategorizations/DisappliedQuery.ql b/cpp/common/test/guideline_recategorizations/DisappliedQuery.ql index 0254eca9bd..58ba3239e0 100644 --- a/cpp/common/test/guideline_recategorizations/DisappliedQuery.ql +++ b/cpp/common/test/guideline_recategorizations/DisappliedQuery.ql @@ -10,7 +10,7 @@ import cpp import codingstandards.cpp.CodingStandards -import codingstandards.cpp.TypeUses +import codingstandards.cpp.types.Uses import codingstandards.cpp.exclusions.cpp.RuleMetadata from UserType ut, string reason diff --git a/cpp/common/test/library/codingstandards/cpp/trivialtypes/LiteralType.ql b/cpp/common/test/library/codingstandards/cpp/trivialtypes/LiteralType.ql index 4d0f8567d0..e7a1d5ebd3 100644 --- a/cpp/common/test/library/codingstandards/cpp/trivialtypes/LiteralType.ql +++ b/cpp/common/test/library/codingstandards/cpp/trivialtypes/LiteralType.ql @@ -1,5 +1,5 @@ import cpp -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType from Type t where diff --git a/cpp/common/test/library/codingstandards/cpp/trivialtypes/TrivialType.ql b/cpp/common/test/library/codingstandards/cpp/trivialtypes/TrivialType.ql index a85fcf5676..edbfbe5303 100644 --- a/cpp/common/test/library/codingstandards/cpp/trivialtypes/TrivialType.ql +++ b/cpp/common/test/library/codingstandards/cpp/trivialtypes/TrivialType.ql @@ -1,5 +1,5 @@ import cpp -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType from Type t where diff --git a/cpp/common/test/library/codingstandards/cpp/trivialtypes/TriviallyCopyableType.ql b/cpp/common/test/library/codingstandards/cpp/trivialtypes/TriviallyCopyableType.ql index 3b3f498796..1667372f4b 100644 --- a/cpp/common/test/library/codingstandards/cpp/trivialtypes/TriviallyCopyableType.ql +++ b/cpp/common/test/library/codingstandards/cpp/trivialtypes/TriviallyCopyableType.ql @@ -1,5 +1,5 @@ import cpp -import codingstandards.cpp.TrivialType +import codingstandards.cpp.types.TrivialType from TriviallyCopyableClass t select t diff --git a/rule_packages/c/Generics.json b/rule_packages/c/Generics.json new file mode 100644 index 0000000000..1183bdc709 --- /dev/null +++ b/rule_packages/c/Generics.json @@ -0,0 +1,194 @@ +{ + "MISRA-C-2012": { + "RULE-23-1": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "A generic selection should only be expanded from a macro.", + "kind": "problem", + "name": "A generic selection should only be expanded from a macro", + "precision": "very-high", + "severity": "warning", + "short_name": "GenericSelectionNotExpandedFromAMacro", + "tags": [ + "maintainability", + "external/misra/c/2012/amendment3" + ] + }, + { + "description": "A generic selection should depend on the type of a macro argument.", + "kind": "problem", + "name": "A generic selection should depend on the type of a macro argument", + "precision": "high", + "severity": "warning", + "short_name": "GenericSelectionDoesntDependOnMacroArgument", + "tags": [ + "correctness", + "maintainability", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A generic selection should only be expanded from a macro" + }, + "RULE-23-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A generic selection that is not expanded from a macro shall not contain potential side effects in the controlling expression.", + "kind": "problem", + "name": "A generic selection shall not contain side-effects if it is not expanded from a macro", + "precision": "high", + "severity": "warning", + "short_name": "GenericSelectionNotFromMacroWithSideEffects", + "tags": [ + "maintainability", + "external/misra/c/2012/amendment3" + ] + } + ], + "implementation_scope": { + "items": [ + "Due to limited information in the CodeQL database for macro argument expansions, this implementation reports generics not of the form `_Generic((X)` where all invocations of that generic contain a side effect in the controlling expression." + ] + }, + "title": "A generic selection that is not expanded from a macro shall not contain potential side effects in the controlling expression" + }, + "RULE-23-3": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "A generic selection should contain at least one non-default association.", + "kind": "problem", + "name": "A generic selection should contain at least one non-default association", + "precision": "very-high", + "severity": "warning", + "short_name": "GenericWithoutNonDefaultAssociation", + "tags": [ + "correctness", + "maintainability", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A generic selection should contain at least one non-default association" + }, + "RULE-23-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Generic selections undergo lvalue conversion before type comparison, leading to certain types being impossible to select.", + "kind": "problem", + "name": "A generic association shall list an appropriate type", + "precision": "very-high", + "severity": "error", + "short_name": "GenericAssociationWithUnselectableType", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A generic association shall list an appropriate type" + }, + "RULE-23-5": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Pointer types in a generic selection do not undergo pointer conversions and should not counterintuitively fall through to the default association.", + "kind": "problem", + "name": "A generic selection should not depend on implicit pointer type conversion", + "precision": "very-high", + "severity": "warning", + "short_name": "DangerousDefaultSelectionForPointerInGeneric", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A generic selection should not depend on implicit pointer type conversion" + }, + "RULE-23-6": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The controlling expression of a generic selection shall have an essential type that matches its standard type.", + "kind": "problem", + "name": "The controlling expression of a generic selection shall have an essential type that matches its standard type", + "precision": "high", + "severity": "error", + "short_name": "GenericExpressionWithIncorrectEssentialType", + "tags": [ + "correctness", + "external/misra/c/2012/amendment3" + ] + } + ], + "implementation_scope": { + "items": [ + "The CodeQL extractor will expand character literals passed into macros into integer literals, and therefore the essential type system for character literals will not necessarily be analyzed correctly." + ] + }, + "title": "The controlling expression of a generic selection shall have an essential type that matches its standard type" + }, + "RULE-23-7": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "A generic selection that is expanded from a macro should evaluate its argument only once.", + "kind": "problem", + "name": "A generic selection that is expanded from a macro should evaluate its argument only once", + "precision": "medium", + "severity": "warning", + "short_name": "InvalidGenericMacroArgumentEvaluation", + "tags": [ + "correctness", + "maintainability", + "external/misra/c/2012/amendment3" + ] + } + ], + "implementation_scope": { + "items": [ + "Due to limited information in the CodeQL database for macro argument expansions, this implementation performs string matching on the macro parameters against the macro body to determine where parameters are expanded. If text indicating a nonevaluated context such as sizeof() or _Alignof() appear, there will be no positive result." + ] + }, + "title": "A generic selection that is expanded from a macro should evaluate its argument only once" + }, + "RULE-23-8": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A default association shall appear as either the first or the last association of a generic selection", + "kind": "problem", + "name": "A default association shall appear as either the first or the last association of a generic", + "precision": "very-high", + "severity": "warning", + "short_name": "DefaultGenericSelectionNotFirstOrLast", + "tags": [ + "maintainability", + "external/misra/c/2012/amendment3" + ] + } + ], + "title": "A default association shall appear as either the first or the last association of a generic selection" + } + } +} \ No newline at end of file From cec1948777ab22388c622f460e1dfde9577f66d9 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 9 Mar 2025 23:05:52 -0700 Subject: [PATCH 02/12] Use clang-format v11, which is closer to v14 installed on ci/cd --- c/misra/test/rules/RULE-23-1/test.c | 26 +-- c/misra/test/rules/RULE-23-2/test.c | 28 +-- c/misra/test/rules/RULE-23-3/test.c | 16 +- c/misra/test/rules/RULE-23-4/test.c | 70 +++---- c/misra/test/rules/RULE-23-5/test.c | 301 ++++++++++++++-------------- c/misra/test/rules/RULE-23-6/test.c | 20 +- c/misra/test/rules/RULE-23-7/test.c | 57 +++--- c/misra/test/rules/RULE-23-8/test.c | 20 +- 8 files changed, 270 insertions(+), 268 deletions(-) diff --git a/c/misra/test/rules/RULE-23-1/test.c b/c/misra/test/rules/RULE-23-1/test.c index f27541dd38..c7d33b1a70 100644 --- a/c/misra/test/rules/RULE-23-1/test.c +++ b/c/misra/test/rules/RULE-23-1/test.c @@ -1,25 +1,25 @@ // NON_COMPLIANT: -#define M1 _Generic(1, int: 1) +#define M1 _Generic(1, int : 1) // NON_COMPLIANT: -#define M2(X) _Generic(1, int: X) +#define M2(X) _Generic(1, int : X) // COMPLIANT: -#define M3(X) _Generic((X), int: 1) +#define M3(X) _Generic((X), int : 1) // COMPLIANT: -#define M4(X) _Generic((X), int: 1) +#define M4(X) _Generic((X), int : 1) // COMPLIANT: -#define M5(X) _Generic((X + X), int: 1) +#define M5(X) _Generic((X + X), int : 1) int f1(int a, int b); // COMPLIANT: -#define M6(X) _Generic(f(1, (X)), int: 1) -#define M7(X) 1 + _Generic((X), int: 1) +#define M6(X) _Generic(f(1, (X)), int : 1) +#define M7(X) 1 + _Generic((X), int : 1) // COMPLIANT: -#define M8(X) g(_Generic((X), int: 1)) +#define M8(X) g(_Generic((X), int : 1)) // NON_COMPLIANT: -#define M9(X) g(_Generic((Y), int: 1)) +#define M9(X) g(_Generic((Y), int : 1)) void f2() { - _Generic(1, int: 1); // NON_COMPLIANT - M1; // NON_COMPLIANT - M2(1); // NON_COMPLIANT - M3(1); // COMPLIANT + _Generic(1, int : 1); // NON_COMPLIANT + M1; // NON_COMPLIANT + M2(1); // NON_COMPLIANT + M3(1); // COMPLIANT } diff --git a/c/misra/test/rules/RULE-23-2/test.c b/c/misra/test/rules/RULE-23-2/test.c index 6a25e15189..9e4c6ca6b2 100644 --- a/c/misra/test/rules/RULE-23-2/test.c +++ b/c/misra/test/rules/RULE-23-2/test.c @@ -1,30 +1,30 @@ -#define M1(X) _Generic((X), int: 1) +#define M1(X) _Generic((X), int : 1) // NON_COMPLIANT: -#define M2(X) _Generic((X)++, int: 1) +#define M2(X) _Generic((X)++, int : 1) // NON_COMPLIANT: -#define M3(X) _Generic(l1++, int: (X)) +#define M3(X) _Generic(l1++, int : (X)) // COMPLIANT: #define M3_WRAPPER(X) M3(X) -#define M4(X) _Generic((X)(), int: 1) +#define M4(X) _Generic((X)(), int : 1) void f1() { int l1; - _Generic(1, int: 1); // COMPLIANT - M1(1); // COMPLIANT - _Generic(l1, int: 1); // COMPLIANT - M1(l1); // COMPLIANT + _Generic(1, int : 1); // COMPLIANT + M1(1); // COMPLIANT + _Generic(l1, int : 1); // COMPLIANT + M1(l1); // COMPLIANT _Generic(l1++, - int: 1); // COMPLIANT: side effect is not from a macro argument. - M1(l1++); // COMPLIANT - M2(l1); // NON-COMPLIANT: at macro definition - M3(1); // NON-COMPLIANT: at macro definition - M3_WRAPPER(1); // NON-COMPLIANT: at definition of M3 + int : 1); // COMPLIANT: side effect is not from a macro argument. + M1(l1++); // COMPLIANT + M2(l1); // NON-COMPLIANT: at macro definition + M3(1); // NON-COMPLIANT: at macro definition + M3_WRAPPER(1); // NON-COMPLIANT: at definition of M3 } int g1; @@ -41,7 +41,7 @@ void f2() { #define M5(X) \ static volatile l##X; \ - _Generic(l##X, int: 1) + _Generic(l##X, int : 1) void f3() { M5(a); // NON-COMPLIANT diff --git a/c/misra/test/rules/RULE-23-3/test.c b/c/misra/test/rules/RULE-23-3/test.c index d3f093f242..616c14bc80 100644 --- a/c/misra/test/rules/RULE-23-3/test.c +++ b/c/misra/test/rules/RULE-23-3/test.c @@ -1,20 +1,20 @@ // NON-COMPLIANT -#define M1 _Generic(1, default: 1); +#define M1 _Generic(1, default : 1); // COMPLIANT -#define M2 _Generic(1, int: 1); +#define M2 _Generic(1, int : 1); // COMPLIANT -#define M3 _Generic(1, int: 1, default: 1); +#define M3 _Generic(1, int : 1, default : 1); // COMPLIANT -#define M4 _Generic(1, int: 1, long: 1); +#define M4 _Generic(1, int : 1, long : 1); void f() { // Invalid generics: // _Generic(1); // _Generic(1, void: 1); - _Generic(1, default: 1); // NON-COMPLIANT - _Generic(1, int: 1); // COMPLIANT - _Generic(1, int: 1, default: 1); // COMPLIANT - _Generic(1, int: 1, long: 1); // COMPLIANT + _Generic(1, default : 1); // NON-COMPLIANT + _Generic(1, int : 1); // COMPLIANT + _Generic(1, int : 1, default : 1); // COMPLIANT + _Generic(1, int : 1, long : 1); // COMPLIANT M1; M2; diff --git a/c/misra/test/rules/RULE-23-4/test.c b/c/misra/test/rules/RULE-23-4/test.c index 135ab4d0f7..cfef26318c 100644 --- a/c/misra/test/rules/RULE-23-4/test.c +++ b/c/misra/test/rules/RULE-23-4/test.c @@ -7,41 +7,41 @@ union empty_union {}; void f() { _Generic(1, - int: 1, // COMPLIANT - const int: 1, // NON-COMPLIANT - volatile int: 1, // NON-COMPLIANT - _Atomic int: 1, // NON-COMPLIANT - int *: 1, // COMPLIANT - int const *: 1, // COMPLIANT - const volatile int: 1, // NON-COMPLIANT - int volatile const *: 1, // COMPLIANT - struct {}: 1, // NON-COMPLIANT - struct {} *: 1, // NON-COMPLIANT - empty_struct_t: 1, // COMPLIANT - struct empty_struct: 1, // COMPLIANT - empty_struct_t *: 1, // COMPLIANT - struct empty_struct *: 1, // COMPLIANT - union {}: 1, // NON-COMPLIANT - union {} *: 1, // NON-COMPLIANT - empty_union_t: 1, // COMPLIANT - union empty_union: 1, // COMPLIANT - empty_union_t *: 1, // COMPLIANT - union empty_union *: 1, // COMPLIANT - // int[]: 1, // compile error - int[3]: 1, // NON-COMPLIANT - int(*)[3]: 1, // COMPLIANT: pointer to array OK - // int (int*): 1, // compile error - int (*)(int *): 1, // COMPLIANT: function pointers OK - default: 1 // COMPLIANT + int : 1, // COMPLIANT + const int : 1, // NON-COMPLIANT + volatile int : 1, // NON-COMPLIANT + _Atomic int : 1, // NON-COMPLIANT + int * : 1, // COMPLIANT + int const * : 1, // COMPLIANT + const volatile int : 1, // NON-COMPLIANT + int volatile const * : 1, // COMPLIANT + struct {} : 1, // NON-COMPLIANT + struct {} * : 1, // NON-COMPLIANT + empty_struct_t : 1, // COMPLIANT + struct empty_struct : 1, // COMPLIANT + empty_struct_t * : 1, // COMPLIANT + struct empty_struct * : 1, // COMPLIANT + union {} : 1, // NON-COMPLIANT + union {} * : 1, // NON-COMPLIANT + empty_union_t : 1, // COMPLIANT + union empty_union : 1, // COMPLIANT + empty_union_t * : 1, // COMPLIANT + union empty_union * : 1, // COMPLIANT + // int[]: 1, // compile error + int[3] : 1, // NON-COMPLIANT + int(*)[3] : 1, // COMPLIANT: pointer to array OK + // int (int*): 1, // compile error + int (*)(int *) : 1, // COMPLIANT: function pointers OK + default : 1 // COMPLIANT ); } // NON-COMPLIANT -#define M1(X) _Generic((X), const int: 1, default: 0) +#define M1(X) _Generic((X), const int : 1, default : 0) // NON-COMPLIANT -#define M2(X) _Generic(1, X[3]: 1, default: 0) +#define M2(X) _Generic(1, X[3] : 1, default : 0) // COMPLIANT -#define M3(X) _Generic(1, X: 1, default: 0) +#define M3(X) _Generic(1, X : 1, default : 0) void f2() { M1(1); @@ -60,12 +60,12 @@ typedef long const *long_const_ptr; void f3() { _Generic(1, - int_t: 1, // COMPLIANT - const_int: 1, // NON-COMPLIANT - const_int_ptr: 1, // COMPLIANT - long_const_ptr: 1, // COMPLIANT - const int_ptr: 1, // COMPLIANT - default: 1 // COMPLIANT + int_t : 1, // COMPLIANT + const_int : 1, // NON-COMPLIANT + const_int_ptr : 1, // COMPLIANT + long_const_ptr : 1, // COMPLIANT + const int_ptr : 1, // COMPLIANT + default : 1 // COMPLIANT ); } diff --git a/c/misra/test/rules/RULE-23-5/test.c b/c/misra/test/rules/RULE-23-5/test.c index d9e140f0af..5d48cc58bd 100644 --- a/c/misra/test/rules/RULE-23-5/test.c +++ b/c/misra/test/rules/RULE-23-5/test.c @@ -19,85 +19,85 @@ void f2() { void *: f1, const void *: f1, default: f1); // COMPLIANT - _Generic(l2, int: f1, default: f1); // COMPLIANT - _Generic(l3, int: f1, default: f1); // COMPLIANT - _Generic(l4, int: f1, default: f1); // COMPLIANT - _Generic(l5, int: f1, default: f1); // COMPLIANT + _Generic(l2, int : f1, default : f1); // COMPLIANT + _Generic(l3, int : f1, default : f1); // COMPLIANT + _Generic(l4, int : f1, default : f1); // COMPLIANT + _Generic(l5, int : f1, default : f1); // COMPLIANT // Compliant, default case is not matched - _Generic(l1, int: f1); // COMPLIANT - _Generic(l2, int *: f1); // COMPLIANT - _Generic(l3, const int *: f1); // COMPLIANT - _Generic(l4, volatile int *: f1); // COMPLIANT - _Generic(l5, volatile const int *: f1); // COMPLIANT - _Generic(l6, void *: f1); // COMPLIANT - _Generic(l7, const void *: f1); // COMPLIANT - _Generic(l8, volatile void *: f1); // COMPLIANT - _Generic(l9, const volatile void *: f1); // COMPLIANT + _Generic(l1, int : f1); // COMPLIANT + _Generic(l2, int * : f1); // COMPLIANT + _Generic(l3, const int * : f1); // COMPLIANT + _Generic(l4, volatile int * : f1); // COMPLIANT + _Generic(l5, volatile const int * : f1); // COMPLIANT + _Generic(l6, void * : f1); // COMPLIANT + _Generic(l7, const void * : f1); // COMPLIANT + _Generic(l8, volatile void * : f1); // COMPLIANT + _Generic(l9, const volatile void * : f1); // COMPLIANT // Violation, match default case due to lack of pointer to pointer // conversions: - _Generic(l2, int *: f1, default: f1); // COMPLIANT - _Generic(l2, const int *: f1, default: f1); // NON-COMPLIANT - _Generic(l2, volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l2, const volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l2, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l2, const void *: f1, default: f1); // NON-COMPLIANT - _Generic(l2, const volatile void *: f1, default: f1); // NON-COMPLIANT - - _Generic(l3, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l3, const int *: f1, default: f1); // COMPLIANT - _Generic(l3, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l3, const void *: f1, default: f1); // NON-COMPLIANT - _Generic(l3, const volatile void *: f1, default: f1); // NON-COMPLIANT + _Generic(l2, int * : f1, default : f1); // COMPLIANT + _Generic(l2, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l3, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l3, const int * : f1, default : f1); // COMPLIANT + _Generic(l3, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l3, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l3, const volatile void * : f1, default : f1); // NON-COMPLIANT // Obviously not volatile: - _Generic(l3, volatile int *: f1, default: f1); // COMPLIANT + _Generic(l3, volatile int * : f1, default : f1); // COMPLIANT // Debatable, but volatile const int* is assignable to const int* so its // considered risky - _Generic(l3, const volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l3, const volatile int * : f1, default : f1); // NON-COMPLIANT - _Generic(l4, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l4, volatile int *: f1, default: f1); // COMPLIANT - _Generic(l4, const volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l4, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l4, const volatile void *: f1, default: f1); // NON-COMPLIANT + _Generic(l4, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l4, volatile int * : f1, default : f1); // COMPLIANT + _Generic(l4, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l4, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l4, const volatile void * : f1, default : f1); // NON-COMPLIANT // Debatable, but volatile int* isn't assignable to const int* or vice versa. - _Generic(l4, const int *: f1, default: f1); // COMPLIANT + _Generic(l4, const int * : f1, default : f1); // COMPLIANT // Debatable, but volatile int* isn't assignable to const void* or vice versa. - _Generic(l4, const void *: f1, default: f1); // COMPLIANT - - _Generic(l5, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l5, const int *: f1, default: f1); // NON-COMPLIANT - _Generic(l5, volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l5, const volatile int *: f1, default: f1); // COMPLIANT - _Generic(l5, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l5, const void *: f1, default: f1); // NON-COMPLIANT - _Generic(l5, const volatile void *: f1, default: f1); // NON-COMPLIANT - - _Generic(l6, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l6, const int *: f1, default: f1); // NON-COMPLIANT - _Generic(l6, volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l6, const volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l6, void *: f1, default: f1); // COMPLIANT - _Generic(l6, const void *: f1, default: f1); // NON-COMPLIANT - _Generic(l6, const volatile void *: f1, default: f1); // NON-COMPLIANT - - _Generic(l7, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l7, const int *: f1, default: f1); // NON-COMPLIANT - _Generic(l7, const volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l7, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l7, const void *: f1, default: f1); // COMPLIANT - _Generic(l7, const volatile void *: f1, default: f1); // NON-COMPLIANT + _Generic(l4, const void * : f1, default : f1); // COMPLIANT + + _Generic(l5, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l5, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l5, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l5, const volatile int * : f1, default : f1); // COMPLIANT + _Generic(l5, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l5, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l5, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l6, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l6, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l6, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l6, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l6, void * : f1, default : f1); // COMPLIANT + _Generic(l6, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l6, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l7, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l7, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l7, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l7, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l7, const void * : f1, default : f1); // COMPLIANT + _Generic(l7, const volatile void * : f1, default : f1); // NON-COMPLIANT // Debatable, but const void* isn't assignable to volatile int* or vice versa. - _Generic(l7, volatile int *: f1, default: f1); // COMPLIANT + _Generic(l7, volatile int * : f1, default : f1); // COMPLIANT - _Generic(l9, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l9, const int *: f1, default: f1); // NON-COMPLIANT - _Generic(l9, volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l9, const volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l9, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l9, const void *: f1, default: f1); // NON-COMPLIANT - _Generic(l9, const volatile void *: f1, default: f1); // COMPLIANT + _Generic(l9, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l9, const volatile void * : f1, default : f1); // COMPLIANT /** * Edge case 1: The controlling expression undergoes lvalue conversion, so @@ -110,66 +110,66 @@ void f2() { int *const l14; const int *const l15; - _Generic(l10, int *: f1, default: f1); // COMPLIANT - _Generic(l11, const int *: f1, default: f1); // COMPLIANT - _Generic(l12, volatile int *: f1, default: f1); // COMPLIANT - _Generic(l13, const volatile int *: f1, default: f1); // COMPLIANT - - _Generic(l10, int *: f1, default: f1); // COMPLIANT - _Generic(l10, const int *: f1, default: f1); // NON-COMPLIANT - _Generic(l10, volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l10, const volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l10, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l10, const void *: f1, default: f1); // NON-COMPLIANT - _Generic(l10, const volatile void *: f1, default: f1); // NON-COMPLIANT - - _Generic(l11, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l11, const int *: f1, default: f1); // COMPLIANT - _Generic(l11, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l11, const void *: f1, default: f1); // NON-COMPLIANT - _Generic(l11, const volatile void *: f1, default: f1); // NON-COMPLIANT + _Generic(l10, int * : f1, default : f1); // COMPLIANT + _Generic(l11, const int * : f1, default : f1); // COMPLIANT + _Generic(l12, volatile int * : f1, default : f1); // COMPLIANT + _Generic(l13, const volatile int * : f1, default : f1); // COMPLIANT + + _Generic(l10, int * : f1, default : f1); // COMPLIANT + _Generic(l10, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l10, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l10, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l10, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l10, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l10, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l11, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l11, const int * : f1, default : f1); // COMPLIANT + _Generic(l11, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l11, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l11, const volatile void * : f1, default : f1); // NON-COMPLIANT // Obviously not volatile: - _Generic(l11, volatile int *: f1, default: f1); // COMPLIANT + _Generic(l11, volatile int * : f1, default : f1); // COMPLIANT // Debatable, but volatile const int* is assignable to const int* so its // considered risky - _Generic(l11, const volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l11, const volatile int * : f1, default : f1); // NON-COMPLIANT - _Generic(l12, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l12, volatile int *: f1, default: f1); // COMPLIANT - _Generic(l12, const volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l12, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l12, const volatile void *: f1, default: f1); // NON-COMPLIANT + _Generic(l12, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l12, volatile int * : f1, default : f1); // COMPLIANT + _Generic(l12, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l12, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l12, const volatile void * : f1, default : f1); // NON-COMPLIANT // Debatab12e, but volatile int* isn't assignable to const int* or vice versa. - _Generic(l12, const int *: f1, default: f1); // COMPLIANT + _Generic(l12, const int * : f1, default : f1); // COMPLIANT // Debatable, but volatile int* isn't assignable to const void* or vice versa. - _Generic(l12, const void *: f1, default: f1); // COMPLIANT - - _Generic(l13, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l13, const int *: f1, default: f1); // NON-COMPLIANT - _Generic(l13, volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l13, const volatile int *: f1, default: f1); // COMPLIANT - _Generic(l13, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l13, const void *: f1, default: f1); // NON-COMPLIANT - _Generic(l13, const volatile void *: f1, default: f1); // NON-COMPLIANT - - _Generic(l14, int *: f1, default: f1); // COMPLIANT - _Generic(l14, const int *: f1, default: f1); // NON-COMPLIANT - _Generic(l14, volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l14, const volatile int *: f1, default: f1); // NON-COMPLIANT - _Generic(l14, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l14, const void *: f1, default: f1); // NON-COMPLIANT - _Generic(l14, const volatile void *: f1, default: f1); // NON-COMPLIANT - - _Generic(l15, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l15, const int *: f1, default: f1); // COMPLIANT - _Generic(l15, void *: f1, default: f1); // NON-COMPLIANT - _Generic(l15, const void *: f1, default: f1); // NON-COMPLIANT - _Generic(l15, const volatile void *: f1, default: f1); // NON-COMPLIANT + _Generic(l12, const void * : f1, default : f1); // COMPLIANT + + _Generic(l13, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l13, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l13, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l13, const volatile int * : f1, default : f1); // COMPLIANT + _Generic(l13, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l13, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l13, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l14, int * : f1, default : f1); // COMPLIANT + _Generic(l14, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l14, volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l14, const volatile int * : f1, default : f1); // NON-COMPLIANT + _Generic(l14, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l14, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l14, const volatile void * : f1, default : f1); // NON-COMPLIANT + + _Generic(l15, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l15, const int * : f1, default : f1); // COMPLIANT + _Generic(l15, void * : f1, default : f1); // NON-COMPLIANT + _Generic(l15, const void * : f1, default : f1); // NON-COMPLIANT + _Generic(l15, const volatile void * : f1, default : f1); // NON-COMPLIANT // Obviously not volatile: - _Generic(l15, volatile int *: f1, default: f1); // COMPLIANT + _Generic(l15, volatile int * : f1, default : f1); // COMPLIANT // Debatable, but volatile const int* is assignable to const int* so its // considered risky - _Generic(l15, const volatile int *: f1, default: f1); // NON-COMPLIANT + _Generic(l15, const volatile int * : f1, default : f1); // NON-COMPLIANT /** * Edge case 2: Types don't have to be identical to be compatible. @@ -177,18 +177,19 @@ void f2() { int(*l16)[3]; // This is a risky conversion that should be reported: - _Generic(l16, int(*const)[3]: f1, default: f1); // NON-COMPLIANT + _Generic(l16, int(*const)[3] : f1, default : f1); // NON-COMPLIANT // However, in this one, there is a match on the second selector, because it // it is an array type with a compatible element type, and sizes only have to // match if both arrays have a constant size. Therefore, the default selector // is not chosen and this is not a violation. - _Generic(l16, int(*const)[3]: f1, int(*)[]: f1, default: f1); // COMPLIANT + _Generic(l16, int(*const)[3] : f1, int(*)[] : f1, default : f1); // COMPLIANT // In this case, the second selector is not a compatible type because the // array has a constant size that doesn't match, and this should be reported. - _Generic(l16, - int(*const)[3]: f1, - int(*)[4]: f1, - default: f1); // NON-COMPLIANT + _Generic(l16, int(*const)[3] + : f1, int(*)[4] + : f1, + default + : f1); // NON-COMPLIANT /** * Edge case 3: Conversion on _Generic, make sure we use the fully converted @@ -196,10 +197,10 @@ void f2() { */ int *l17; void *l18; - _Generic((void *)l17, void *: f1, default: f1); // COMPLIANT - _Generic((void *)l17, int *: f1, default: f1); // NON-COMPLIANT - _Generic((int *)l18, void *: f1, default: f1); // NON-COMPLIANT - _Generic((int *)l18, int *: f1, default: f1); // COMPLIANT + _Generic((void *)l17, void * : f1, default : f1); // COMPLIANT + _Generic((void *)l17, int * : f1, default : f1); // NON-COMPLIANT + _Generic((int *)l18, void * : f1, default : f1); // NON-COMPLIANT + _Generic((int *)l18, int * : f1, default : f1); // COMPLIANT /** * Edge case 4: Typedefs must be resolved properly. @@ -210,27 +211,27 @@ void f2() { c_int_t *l20; volatile c_int_t *l21; - _Generic(l2, int *: f1, default: f1); // COMPLIANT - _Generic(l2, int_t *: f1, default: f1); // COMPLIANT - _Generic(l2, const int *: f1, default: f1); // NON-COMPLIANT - _Generic(l2, const int_t *: f1, default: f1); // NON-COMPLIANT - _Generic(l2, c_int_t *: f1, default: f1); // NON-COMPLIANT - - _Generic(l19, int *: f1, default: f1); // COMPLIANT - _Generic(l19, int_t *: f1, default: f1); // COMPLIANT - _Generic(l19, const int *: f1, default: f1); // NON-COMPLIANT - _Generic(l19, const int_t *: f1, default: f1); // NON-COMPLIANT - _Generic(l19, c_int_t *: f1, default: f1); // NON-COMPLIANT - - _Generic(l3, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l3, int_t *: f1, default: f1); // NON-COMPLIANT - _Generic(l3, const int *: f1, default: f1); // COMPLIANT - _Generic(l3, const int_t *: f1, default: f1); // COMPLIANT - _Generic(l3, c_int_t *: f1, default: f1); // COMPLIANT - - _Generic(l20, int *: f1, default: f1); // NON-COMPLIANT - _Generic(l20, int_t *: f1, default: f1); // NON-COMPLIANT - _Generic(l20, const int *: f1, default: f1); // COMPLIANT - _Generic(l20, const int_t *: f1, default: f1); // COMPLIANT - _Generic(l20, c_int_t *: f1, default: f1); // COMPLIANT + _Generic(l2, int * : f1, default : f1); // COMPLIANT + _Generic(l2, int_t * : f1, default : f1); // COMPLIANT + _Generic(l2, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, const int_t * : f1, default : f1); // NON-COMPLIANT + _Generic(l2, c_int_t * : f1, default : f1); // NON-COMPLIANT + + _Generic(l19, int * : f1, default : f1); // COMPLIANT + _Generic(l19, int_t * : f1, default : f1); // COMPLIANT + _Generic(l19, const int * : f1, default : f1); // NON-COMPLIANT + _Generic(l19, const int_t * : f1, default : f1); // NON-COMPLIANT + _Generic(l19, c_int_t * : f1, default : f1); // NON-COMPLIANT + + _Generic(l3, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l3, int_t * : f1, default : f1); // NON-COMPLIANT + _Generic(l3, const int * : f1, default : f1); // COMPLIANT + _Generic(l3, const int_t * : f1, default : f1); // COMPLIANT + _Generic(l3, c_int_t * : f1, default : f1); // COMPLIANT + + _Generic(l20, int * : f1, default : f1); // NON-COMPLIANT + _Generic(l20, int_t * : f1, default : f1); // NON-COMPLIANT + _Generic(l20, const int * : f1, default : f1); // COMPLIANT + _Generic(l20, const int_t * : f1, default : f1); // COMPLIANT + _Generic(l20, c_int_t * : f1, default : f1); // COMPLIANT } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-6/test.c b/c/misra/test/rules/RULE-23-6/test.c index 7d59c1e199..2b18f4cd2c 100644 --- a/c/misra/test/rules/RULE-23-6/test.c +++ b/c/misra/test/rules/RULE-23-6/test.c @@ -3,21 +3,21 @@ int l2; long l3; enum { E1 } l4; -#define M1(X) _Generic((X), int: 1, unsigned int: 1, short: 2, long: 3) +#define M1(X) _Generic((X), int : 1, unsigned int : 1, short : 2, long : 3) void f1() { M1(l1); // COMPLIANT M1(l2); // COMPLIANT M1(l3); // COMPLIANT M1(l4); // NON-COMPLIANT - M1(1); // COMPLIANT - M1(1u); // COMPLIANT - M1(l1 + l1); // NON-COMPLIANT - M1((int)(l1 + l1)); // COMPLIANT - M1('c'); // NON-COMPLIANT[false negative] - _Generic('c', int: 1); // NON-COMPLIANT - _Generic(_Generic(0, default: l1 + l1), default: 1); // NON-COMPLIANT - _Generic(((short)_Generic(0, default: (l1 + l1))), default: 1); // COMPLIANT + M1(1); // COMPLIANT + M1(1u); // COMPLIANT + M1(l1 + l1); // NON-COMPLIANT + M1((int)(l1 + l1)); // COMPLIANT + M1('c'); // NON-COMPLIANT[false negative] + _Generic('c', int : 1); // NON-COMPLIANT + _Generic(_Generic(0, default : l1 + l1), default : 1); // NON-COMPLIANT + _Generic(((short)_Generic(0, default : (l1 + l1))), default : 1); // COMPLIANT } void f2() { @@ -29,5 +29,5 @@ void f2() { struct S1 { int m1; }; - _Generic((const struct S1){.m1 = 0}, default: 1); + _Generic((const struct S1){.m1 = 0}, default : 1); } \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-7/test.c b/c/misra/test/rules/RULE-23-7/test.c index 41a5d6ab6c..e2f5e98ee5 100644 --- a/c/misra/test/rules/RULE-23-7/test.c +++ b/c/misra/test/rules/RULE-23-7/test.c @@ -2,59 +2,60 @@ int f1(int p1); int f2(int p1, int p2); // COMPLIANT -- standard correct cases: -#define M1(X) _Generic((X), int: f1, default: f1)(X) -#define M2(X) _Generic((X), int: f1(X), default: f1(X)) +#define M1(X) _Generic((X), int : f1, default : f1)(X) +#define M2(X) _Generic((X), int : f1(X), default : f1(X)) // NON-COMPLIANT -- standard incorrect cases: -#define M3(X) _Generic((X), int: f1(X), default: 0) -#define M4(X) (X) + _Generic((X), int: f1(X), default: f1(X)) -#define M5(X) _Generic((X), int: f1(X), default: f1(X)) + (X) -#define M6(X) _Generic((X), int: f1((X) + (X)), default: f1(X)) +#define M3(X) _Generic((X), int : f1(X), default : 0) +#define M4(X) (X) + _Generic((X), int : f1(X), default : f1(X)) +#define M5(X) _Generic((X), int : f1(X), default : f1(X)) + (X) +#define M6(X) _Generic((X), int : f1((X) + (X)), default : f1(X)) // Compliant by exception // COMPLIANT -#define M7(X) _Generic((X), int: 1, default: 0) +#define M7(X) _Generic((X), int : 1, default : 0) // NON-COMPLIANT[FALSE NEGATIVE] -- Without an expansion, we can't tell if this // macro has only constant expressions or not. -#define M8(X) _Generic((X), int: f1(1)) +#define M8(X) _Generic((X), int : f1(1)) // NON-COMPLIANT -- If the macro is expanded we can detect constant expressions -#define M9(X) _Generic((X), int: f1) +#define M9(X) _Generic((X), int : f1) // NON-COMPLIANT -- If the macro is expanded we can detect constant expressions -#define M10(X) _Generic((X), int: f1(1)) +#define M10(X) _Generic((X), int : f1(1)) void f3() { M9(1); M10(1); } // COMPLIANT -- multiple uses in the controlling expression is OK: -#define M11(X) _Generic((X) + (X), int: f1(X), default: f1(X)) +#define M11(X) _Generic((X) + (X), int : f1(X), default : f1(X)) // NON-COMPLIANT -- the rule should still be enforced otherwise: -#define M12(X) _Generic((X) + (X), int: f1(X), default: 1) -#define M13(X) _Generic((X) + (X), int: f1(X), default: f1(X)) + (X) +#define M12(X) _Generic((X) + (X), int : f1(X), default : 1) +#define M13(X) _Generic((X) + (X), int : f1(X), default : f1(X)) + (X) // COMPLIANT -- the argument is not used in the controlling expression: -#define M14(X) _Generic(1, int: f1((X) + (X)), default: f1(X)) -#define M15(X) _Generic(1, int: f1(X), default: f1(X)) + (X) +#define M14(X) _Generic(1, int : f1((X) + (X)), default : f1(X)) +#define M15(X) _Generic(1, int : f1(X), default : f1(X)) + (X) // Test cases with more than one argument: // COMPLIANT -- Y is not used in the controlling expression: -#define M16(X, Y) _Generic((X), int: f2((X), (Y)), default: f2((X), 1)) +#define M16(X, Y) _Generic((X), int : f2((X), (Y)), default : f2((X), 1)) // NON-COMPLIANT -- Y is used in the controlling expression -#define M17(X, Y) _Generic((X) + (Y), int: f2((X), (Y)), default: f2((X), 1)) +#define M17(X, Y) _Generic((X) + (Y), int : f2((X), (Y)), default : f2((X), 1)) // COMPLIANT -- Y is used in the controlling expression correctly -#define M18(X, Y) _Generic((X) + (Y), int: f2((X), (Y)), default: f2((X), (Y))) +#define M18(X, Y) \ + _Generic((X) + (Y), int : f2((X), (Y)), default : f2((X), (Y))) // Test unevaluated contexts: // COMPLIANT -- sizeof is not evaluated: -#define M19(X) _Generic((X), int[sizeof(X)]: f1, default: f1)(X) -#define M20(X) _Generic((X), int: f1(sizeof(X)), default: f1)(X) -#define M21(X) _Generic((X), int: f1(X), default: f1(X)) + sizeof(X) +#define M19(X) _Generic((X), int[sizeof(X)] : f1, default : f1)(X) +#define M20(X) _Generic((X), int : f1(sizeof(X)), default : f1)(X) +#define M21(X) _Generic((X), int : f1(X), default : f1(X)) + sizeof(X) // NON-COMPLIANT[FALSE NEGATIVE] -- sizeof plus evaluated context -#define M22(X) _Generic((X), int: f1(sizeof(X) + X), default: f1(X))(X) +#define M22(X) _Generic((X), int : f1(sizeof(X) + X), default : f1(X))(X) // NON-COMPLIANT[FALSE NEGATIVE] -- array type sizes may be evaluated -#define M23(X) _Generic((X), int[X]: f1, default: f1)(X) +#define M23(X) _Generic((X), int[X] : f1, default : f1)(X) // COMPLIANT -- alignof, typeof are not evaluated: -#define M24(X) _Generic((X), int[X]: f1, default: f1)(X) +#define M24(X) _Generic((X), int[X] : f1, default : f1)(X) // Nested macros: #define ONCE(X) (X) @@ -64,8 +65,8 @@ void f3() { // COMPLIANT #define M25(X) _Generic((X), int: ONCE(f1(X)), default: ONCE(f1(X)) // COMPLIANT[FALSE POSITIVE] -#define M26(X) _Generic((X), int: IGNORE_2ND(X, X), default: IGNORE_2ND(X, X)) -#define M27(X) _Generic((X), int: f1(IGNORE(X)), default: f1(IGNORE(X)))(X) +#define M26(X) _Generic((X), int : IGNORE_2ND(X, X), default : IGNORE_2ND(X, X)) +#define M27(X) _Generic((X), int : f1(IGNORE(X)), default : f1(IGNORE(X)))(X) // NON-COMPLIANT[FASE NEGATIVE] -#define M28(X) _Generic((X), int: f1(IGNORE(X)), default: f1(IGNORE(X))) -#define M29(X) _Generic((X), int: TWICE(f1(X)), default: TWICE(f1(X))) \ No newline at end of file +#define M28(X) _Generic((X), int : f1(IGNORE(X)), default : f1(IGNORE(X))) +#define M29(X) _Generic((X), int : TWICE(f1(X)), default : TWICE(f1(X))) \ No newline at end of file diff --git a/c/misra/test/rules/RULE-23-8/test.c b/c/misra/test/rules/RULE-23-8/test.c index fdd6e66044..340ddb16db 100644 --- a/c/misra/test/rules/RULE-23-8/test.c +++ b/c/misra/test/rules/RULE-23-8/test.c @@ -2,21 +2,21 @@ * Cases where the macro itself is always compliant or non compliant: */ // COMPLIANT -#define M1(X) _Generic((X), int: 1, unsigned int: 2) +#define M1(X) _Generic((X), int : 1, unsigned int : 2) // COMPLIANT -#define M2(X) _Generic((X), int: 1, unsigned int: 2, default: 0) +#define M2(X) _Generic((X), int : 1, unsigned int : 2, default : 0) // COMPLIANT -#define M3(X) _Generic((X), default: 0, int: 1, unsigned int: 2) +#define M3(X) _Generic((X), default : 0, int : 1, unsigned int : 2) // NON-COMPLIANT -#define M4(X) _Generic((X), int: 1, default: 0, unsigned int: 2) +#define M4(X) _Generic((X), int : 1, default : 0, unsigned int : 2) /** * Macros that are compliant or not based on use: */ // NON-COMPLIANT: because every use is non compliant -#define M5(...) _Generic(0, __VA_ARGS__, default: 0, int: 1) +#define M5(...) _Generic(0, __VA_ARGS__, default : 0, int : 1) // COMPLIANT: because some uses are compliant -#define M6(...) _Generic(0, __VA_ARGS__, int: 1) +#define M6(...) _Generic(0, __VA_ARGS__, int : 1) void f1() { M1(0); // COMPLIANT @@ -42,8 +42,8 @@ void f1() { * RULE-23-1. */ void f2() { - _Generic(0, int: 1, unsigned int: 2); // COMPLIANT - _Generic(0, int: 1, unsigned int: 2, default: 0); // COMPLIANT - _Generic(0, default: 0, int: 1, unsigned int: 2); // COMPLIANT - _Generic(0, int: 1, default: 0, unsigned int: 2); // NON-COMPLIANT + _Generic(0, int : 1, unsigned int : 2); // COMPLIANT + _Generic(0, int : 1, unsigned int : 2, default : 0); // COMPLIANT + _Generic(0, default : 0, int : 1, unsigned int : 2); // COMPLIANT + _Generic(0, int : 1, default : 0, unsigned int : 2); // NON-COMPLIANT } \ No newline at end of file From b2aef27eb8227aabb772ff1da27c597a4678c169 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 9 Mar 2025 23:10:33 -0700 Subject: [PATCH 03/12] Format Type.qll --- cpp/common/src/codingstandards/cpp/Type.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/common/src/codingstandards/cpp/Type.qll b/cpp/common/src/codingstandards/cpp/Type.qll index 32c139fd89..052096559a 100644 --- a/cpp/common/src/codingstandards/cpp/Type.qll +++ b/cpp/common/src/codingstandards/cpp/Type.qll @@ -1,2 +1,2 @@ import codingstandards.cpp.types.Type -import codingstandards.cpp.types.Uses \ No newline at end of file +import codingstandards.cpp.types.Uses From 183100e621e5eb5c72ba36552d7111d14bdef32c Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 9 Mar 2025 23:10:44 -0700 Subject: [PATCH 04/12] Fix Generics.json package --- rule_packages/c/Generics.json | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/rule_packages/c/Generics.json b/rule_packages/c/Generics.json index 1183bdc709..02c7cb2364 100644 --- a/rule_packages/c/Generics.json +++ b/rule_packages/c/Generics.json @@ -52,9 +52,8 @@ } ], "implementation_scope": { - "items": [ + "description": "Due to limited information in the CodeQL database for macro argument expansions, this implementation reports generics not of the form `_Generic((X)` where all invocations of that generic contain a side effect in the controlling expression." - ] }, "title": "A generic selection that is not expanded from a macro shall not contain potential side effects in the controlling expression" }, @@ -138,9 +137,8 @@ } ], "implementation_scope": { - "items": [ + "description": "The CodeQL extractor will expand character literals passed into macros into integer literals, and therefore the essential type system for character literals will not necessarily be analyzed correctly." - ] }, "title": "The controlling expression of a generic selection shall have an essential type that matches its standard type" }, @@ -164,9 +162,8 @@ } ], "implementation_scope": { - "items": [ + "description": "Due to limited information in the CodeQL database for macro argument expansions, this implementation performs string matching on the macro parameters against the macro body to determine where parameters are expanded. If text indicating a nonevaluated context such as sizeof() or _Alignof() appear, there will be no positive result." - ] }, "title": "A generic selection that is expanded from a macro should evaluate its argument only once" }, @@ -176,7 +173,7 @@ }, "queries": [ { - "description": "A default association shall appear as either the first or the last association of a generic selection", + "description": "A default association shall appear as either the first or the last association of a generic selection.", "kind": "problem", "name": "A default association shall appear as either the first or the last association of a generic", "precision": "very-high", From 61b2852f614e0143f4af11abb99c8126ce43d033 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 9 Mar 2025 23:10:59 -0700 Subject: [PATCH 05/12] Fix DeduplicateMacroResults test --- .../DeduplicateMacroResults.expected | 14 +++++++------- .../cpp/alertreporting/DeduplicateMacroResults.ql | 6 ++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.expected b/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.expected index d9a7fe6a07..c273346e3b 100644 --- a/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.expected +++ b/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.expected @@ -1,7 +1,7 @@ -| deduplicatemacroresults.cpp:4:8:4:9 | definition of g1 | Findme var 'g1'. | deduplicatemacroresults.cpp:4:8:4:9 | deduplicatemacroresults.cpp:4:8:4:9 | (ignored) | -| deduplicatemacroresults.cpp:10:1:10:34 | SOMETIMES_HAS_RESULTS1(type,name) | Invocation of macro $@ has findme var 'g3'. | deduplicatemacroresults.cpp:6:1:6:52 | deduplicatemacroresults.cpp:6:1:6:52 | SOMETIMES_HAS_RESULTS1 | -| deduplicatemacroresults.cpp:13:1:13:34 | SOMETIMES_HAS_RESULTS2(type,name) | Invocation of macro $@ has findme var 'g5'. | deduplicatemacroresults.cpp:7:1:7:53 | deduplicatemacroresults.cpp:7:1:7:53 | SOMETIMES_HAS_RESULTS2 | -| deduplicatemacroresults.cpp:15:1:15:50 | #define ALWAYS_HAS_SAME_RESULT() extern findme g6; | Macro ALWAYS_HAS_SAME_RESULT always has findme var named g6 | deduplicatemacroresults.cpp:15:1:15:50 | deduplicatemacroresults.cpp:15:1:15:50 | (ignored) | -| deduplicatemacroresults.cpp:21:1:21:70 | #define ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(name) extern findme name; | Macro ALWAYS_HAS_RESULT_VARIED_DESCRIPTION always has findme var, for example '$@'. | deduplicatemacroresults.cpp:23:38:23:39 | deduplicatemacroresults.cpp:23:38:23:39 | g7 | -| deduplicatemacroresults.cpp:30:1:31:50 | #define OUTER_ALWAYS_HAS_SAME_RESULT() extern INNER_SOMETIMES_HAS_RESULTS(findme, g10); | Macro OUTER_ALWAYS_HAS_SAME_RESULT always has findme var named g10 | deduplicatemacroresults.cpp:30:1:31:50 | deduplicatemacroresults.cpp:30:1:31:50 | (ignored) | -| deduplicatemacroresults.cpp:37:1:38:52 | #define OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(name) INNER_SOMETIMES_HAS_RESULTS(findme, name ## suffix); | Macro OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION always has findme var, for example '$@'. | deduplicatemacroresults.cpp:40:44:40:47 | deduplicatemacroresults.cpp:40:44:40:47 | g11suffix | +| deduplicatemacroresults.cpp:4:8:4:9 | definition of g1 | Findme var 'g1'. | deduplicatemacroresults.cpp:4:8:4:9 | definition of g1 | | +| deduplicatemacroresults.cpp:10:1:10:34 | SOMETIMES_HAS_RESULTS1(type,name) | Invocation of macro $@ has findme var 'g3'. | deduplicatemacroresults.cpp:6:1:6:52 | #define SOMETIMES_HAS_RESULTS1(type,name) type name | SOMETIMES_HAS_RESULTS1 | +| deduplicatemacroresults.cpp:13:1:13:34 | SOMETIMES_HAS_RESULTS2(type,name) | Invocation of macro $@ has findme var 'g5'. | deduplicatemacroresults.cpp:7:1:7:53 | #define SOMETIMES_HAS_RESULTS2(type,name) type name; | SOMETIMES_HAS_RESULTS2 | +| deduplicatemacroresults.cpp:15:1:15:50 | #define ALWAYS_HAS_SAME_RESULT() extern findme g6; | Macro ALWAYS_HAS_SAME_RESULT always has findme var named g6 | deduplicatemacroresults.cpp:15:1:15:50 | #define ALWAYS_HAS_SAME_RESULT() extern findme g6; | (ignored) | +| deduplicatemacroresults.cpp:21:1:21:70 | #define ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(name) extern findme name; | Macro ALWAYS_HAS_RESULT_VARIED_DESCRIPTION always has findme var, for example '$@'. | deduplicatemacroresults.cpp:23:38:23:39 | declaration of g7 | g7 | +| deduplicatemacroresults.cpp:30:1:31:50 | #define OUTER_ALWAYS_HAS_SAME_RESULT() extern INNER_SOMETIMES_HAS_RESULTS(findme, g10); | Macro OUTER_ALWAYS_HAS_SAME_RESULT always has findme var named g10 | deduplicatemacroresults.cpp:30:1:31:50 | #define OUTER_ALWAYS_HAS_SAME_RESULT() extern INNER_SOMETIMES_HAS_RESULTS(findme, g10); | (ignored) | +| deduplicatemacroresults.cpp:37:1:38:52 | #define OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION(name) INNER_SOMETIMES_HAS_RESULTS(findme, name ## suffix); | Macro OUTER_ALWAYS_HAS_RESULT_VARIED_DESCRIPTION always has findme var, for example '$@'. | deduplicatemacroresults.cpp:40:44:40:47 | definition of g11suffix | g11suffix | diff --git a/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.ql b/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.ql index 3c9f2fc345..68cfc9a78f 100644 --- a/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.ql +++ b/cpp/common/test/library/codingstandards/cpp/alertreporting/DeduplicateMacroResults.ql @@ -23,12 +23,14 @@ module FindMeReportConfig implements MacroReportConfigSig { result = "Invocation of macro $@ has findme var '" + f.getName() + "'." } - string getMessageNotInMacro(FindMe f) { result = "Findme var '" + f.getName() + "'." } + string getMessageNotInMacro(FindMe f, Locatable extra, string extraString) { + result = "Findme var '" + f.getName() + "'." and extra = f and extraString = "" + } } import DeduplicateMacroResults import DeduplicateMacroResults::Report from ReportResult report -select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocation(), +select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocatable(), report.getOptionalPlaceholderMessage() From 30748787907f4ac2ccf2042f2adec6417bb87f92 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 9 Mar 2025 23:17:48 -0700 Subject: [PATCH 06/12] Fix query formats --- c/common/src/codingstandards/c/Generic.qll | 11 +++-------- .../GenericSelectionNotExpandedFromAMacro.ql | 6 ++---- .../GenericSelectionNotFromMacroWithSideEffects.ql | 2 +- .../GenericWithoutNonDefaultAssociation.ql | 3 ++- .../GenericAssociationWithUnselectableType.ql | 4 ++-- .../GenericExpressionWithIncorrectEssentialType.ql | 6 +++--- .../DefaultGenericSelectionNotFirstOrLast.ql | 9 ++++++--- .../DeclarationsOfAFunctionSameNameAndType.ql | 14 +++++++++----- .../DeclarationsOfAnObjectSameNameAndType.ql | 4 ++-- 9 files changed, 30 insertions(+), 29 deletions(-) diff --git a/c/common/src/codingstandards/c/Generic.qll b/c/common/src/codingstandards/c/Generic.qll index 1bf4282017..784c16778e 100644 --- a/c/common/src/codingstandards/c/Generic.qll +++ b/c/common/src/codingstandards/c/Generic.qll @@ -10,7 +10,6 @@ string deparenthesize(string input) { result = input.substring(1, input.length() - 1) } - class GenericMacro extends Macro { string ctrlExpr; @@ -61,9 +60,7 @@ class ParsedGenericMacro extends Macro { ) } - string getAParameter() { - result = this.(FunctionLikeMacro).getAParameter() - } + string getAParameter() { result = this.(FunctionLikeMacro).getAParameter() } int getAParsedGenericCommaSeparatorOffset() { exists(ParsedText text | @@ -118,9 +115,7 @@ class ParsedGenericMacro extends Macro { ) } - string getControllingExprString() { - result = getSelectionString(1) - } + string getControllingExprString() { result = getSelectionString(1) } bindingset[str, word] private int countWordInString(string word, string str) { @@ -152,4 +147,4 @@ class ParsedGenericMacro extends Macro { not idx = 0 and result = expansionsInsideSelection(parameter, idx + 1) } -} \ No newline at end of file +} diff --git a/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql b/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql index ef2a2e75c5..540804ffc4 100644 --- a/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql +++ b/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql @@ -18,8 +18,6 @@ from C11GenericExpr generic, Expr ctrlExpr where not isExcluded(generic, GenericsPackage::genericSelectionNotExpandedFromAMacroQuery()) and ctrlExpr = generic.getControllingExpr() and - not exists(MacroInvocation mi | - mi.getAGeneratedElement() = generic.getExpr() - ) + not exists(MacroInvocation mi | mi.getAGeneratedElement() = generic.getExpr()) select generic, "Generic expression with controlling expression $@ is not expanded froma macro", -ctrlExpr, ctrlExpr.toString() + ctrlExpr, ctrlExpr.toString() diff --git a/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql b/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql index 3ec53a08bf..4b1a2d26c3 100644 --- a/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql +++ b/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql @@ -84,4 +84,4 @@ where not isExcluded(res.getPrimaryElement(), GenericsPackage::genericSelectionNotFromMacroWithSideEffectsQuery()) select res.getPrimaryElement(), res.getMessage(), res.getOptionalPlaceholderLocatable(), - res.getOptionalPlaceholderMessage() \ No newline at end of file + res.getOptionalPlaceholderMessage() diff --git a/c/misra/src/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql b/c/misra/src/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql index f3a0227022..bb2d57a714 100644 --- a/c/misra/src/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql +++ b/c/misra/src/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.ql @@ -31,5 +31,6 @@ where not exists(Type t | t = generic.getAnAssociationType() and not t instanceof VoidType - ) and primaryElement = MacroUnwrapper::unwrapElement(generic) + ) and + primaryElement = MacroUnwrapper::unwrapElement(generic) select primaryElement, "Generic selection contains no non-default association." diff --git a/c/misra/src/rules/RULE-23-4/GenericAssociationWithUnselectableType.ql b/c/misra/src/rules/RULE-23-4/GenericAssociationWithUnselectableType.ql index e8ed88f757..2d707548fa 100644 --- a/c/misra/src/rules/RULE-23-4/GenericAssociationWithUnselectableType.ql +++ b/c/misra/src/rules/RULE-23-4/GenericAssociationWithUnselectableType.ql @@ -20,10 +20,10 @@ import codingstandards.cpp.alertreporting.DeduplicateMacroResults /** * Check if a type contains an unmatchable anonymous struct or union. - * + * * Anonymous structs and unions are only equal to themselves. So any anonymous struct, or compound * type containing an anonymous struct, is unmatchable. - * + * * However, there is an exception if the anonymous struct is behind a typedef. All uses of that * typedef will resolve to the same anonymous struct, and so the typedef is matchable. */ diff --git a/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql b/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql index c27ce3dc55..6bf93947d8 100644 --- a/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql +++ b/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql @@ -57,6 +57,6 @@ where ) ) select generic, - "Controlling expression in generic " + extraMessage + - "has standard type " + ctrlType.toString() + ", which doesn't match its essential type " + - ctrlEssentialType.toString() + ".", extraElement, extraString \ No newline at end of file + "Controlling expression in generic " + extraMessage + "has standard type " + ctrlType.toString() + + ", which doesn't match its essential type " + ctrlEssentialType.toString() + ".", extraElement, + extraString diff --git a/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql b/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql index 04b12ac436..164ffffb1e 100644 --- a/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql +++ b/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql @@ -53,13 +53,15 @@ module GenericMisplacedDefaultReportConfig implements bindingset[description] string getMessageSameResultInAllExpansions(Macro m, string description) { result = - "Generic macro " + m.getName() + " has default as " + description + " association, which is not first or last." + "Generic macro " + m.getName() + " has default as " + description + + " association, which is not first or last." } /* Create a message to describe this macro, using '$@' to describe an example `ResultElement`. */ string getMessageVariedResultInAllExpansions(Macro m) { result = - "Generic macro " + m.getName() + " has a default association which is not first or last, for example $@." + "Generic macro " + m.getName() + + " has a default association which is not first or last, for example $@." } /** @@ -68,7 +70,8 @@ module GenericMisplacedDefaultReportConfig implements */ string getMessageResultInIsolatedExpansion(GenericWithMisplacedDefault element) { result = - "Generic macro $@, in this expansion, has default as " + describe(element) + " association, which is not first or last." + "Generic macro $@, in this expansion, has default as " + describe(element) + + " association, which is not first or last." } /** diff --git a/c/misra/src/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql b/c/misra/src/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql index a0d7c0c9ab..2de2e4fd0a 100644 --- a/c/misra/src/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql +++ b/c/misra/src/rules/RULE-8-3/DeclarationsOfAFunctionSameNameAndType.ql @@ -25,15 +25,19 @@ where //return type check ( not FunctionDeclarationTypeEquivalence::equalReturnTypes(f1, f2) and - case = "return type" and pluralDo = "does" + case = "return type" and + pluralDo = "does" or //parameter type check not FunctionDeclarationTypeEquivalence::equalParameterTypes(f1, f2) and - case = "parameter types" and pluralDo = "do" + case = "parameter types" and + pluralDo = "do" or //parameter name check parameterNamesUnmatched(f1, f2) and - case = "parameter names" and pluralDo = "do" + case = "parameter names" and + pluralDo = "do" ) -select f1, "The " + case + " of re-declaration of $@ " + pluralDo + " not use the same type names as declaration $@", f1, - f1.getName(), f2, f2.getName() +select f1, + "The " + case + " of re-declaration of $@ " + pluralDo + + " not use the same type names as declaration $@", f1, f1.getName(), f2, f2.getName() diff --git a/c/misra/src/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql b/c/misra/src/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql index 83c67e2efa..12ff583b6b 100644 --- a/c/misra/src/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql +++ b/c/misra/src/rules/RULE-8-3/DeclarationsOfAnObjectSameNameAndType.ql @@ -52,5 +52,5 @@ where decl2.getType()) select decl1, "The object $@ of type " + decl1.getType().toString() + - " does not use the same type names as re-declaration $@ of type " + decl2.getType().toString(), decl1, - decl1.getName(), decl2, decl2.getName() + " does not use the same type names as re-declaration $@ of type " + decl2.getType().toString(), + decl1, decl1.getName(), decl2, decl2.getName() From f1ec355fc7d08bcf8f1cc05c1dc61d0d1a15f47a Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Sun, 9 Mar 2025 23:19:09 -0700 Subject: [PATCH 07/12] Fix query metadata --- .../RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql | 4 +++- .../rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql b/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql index 6bf93947d8..f02f92b45a 100644 --- a/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql +++ b/c/misra/src/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.ql @@ -4,9 +4,11 @@ * @description The controlling expression of a generic selection shall have an essential type that * matches its standard type. * @kind problem - * @precision very-high + * @precision high * @problem.severity error * @tags external/misra/id/rule-23-6 + * correctness + * external/misra/c/2012/amendment3 * external/misra/obligation/required */ diff --git a/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql b/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql index 164ffffb1e..6e443bd162 100644 --- a/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql +++ b/c/misra/src/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.ql @@ -2,7 +2,7 @@ * @id c/misra/default-generic-selection-not-first-or-last * @name RULE-23-8: A default association shall appear as either the first or the last association of a generic * @description A default association shall appear as either the first or the last association of a - * generic selection + * generic selection. * @kind problem * @precision very-high * @problem.severity warning From f363371916d4e8aecf6540597c07985648d01dc0 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Tue, 11 Mar 2025 22:22:30 -0700 Subject: [PATCH 08/12] Update generic test expectation offsets post formatting --- ...ectionDoesntDependOnMacroArgument.expected | 6 +- ...ricSelectionNotExpandedFromAMacro.expected | 2 +- ...ectionNotFromMacroWithSideEffects.expected | 6 +- ...nericWithoutNonDefaultAssociation.expected | 4 +- ...icAssociationWithUnselectableType.expected | 26 +-- ...faultSelectionForPointerInGeneric.expected | 168 +++++++++--------- ...ressionWithIncorrectEssentialType.expected | 8 +- ...lidGenericMacroArgumentEvaluation.expected | 24 +-- ...ultGenericSelectionNotFirstOrLast.expected | 8 +- 9 files changed, 126 insertions(+), 126 deletions(-) diff --git a/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.expected b/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.expected index 2534e47012..a903827391 100644 --- a/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.expected +++ b/c/misra/test/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.expected @@ -1,3 +1,3 @@ -| test.c:2:1:2:30 | #define M1 _Generic(1, int: 1) | Generic macro M1 uses controlling expr 1, which doesn't match any macro parameter. | -| test.c:4:1:4:33 | #define M2(X) _Generic(1, int: X) | Generic macro M2 uses controlling expr 1, which doesn't match any macro parameter. | -| test.c:18:1:18:38 | #define M9(X) g(_Generic((Y), int: 1)) | Generic macro M9 uses controlling expr (Y), which doesn't match any macro parameter. | +| test.c:2:1:2:31 | #define M1 _Generic(1, int : 1) | Generic macro M1 uses controlling expr 1, which doesn't match any macro parameter. | +| test.c:4:1:4:34 | #define M2(X) _Generic(1, int : X) | Generic macro M2 uses controlling expr 1, which doesn't match any macro parameter. | +| test.c:18:1:18:39 | #define M9(X) g(_Generic((Y), int : 1)) | Generic macro M9 uses controlling expr (Y), which doesn't match any macro parameter. | diff --git a/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.expected b/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.expected index 476a9320b8..aa3516354e 100644 --- a/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.expected +++ b/c/misra/test/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.expected @@ -1 +1 @@ -| test.c:21:3:21:21 | _Generic | Generic expression with controlling expression $@ is not expanded froma macro | test.c:21:12:21:12 | 1 | 1 | +| test.c:21:3:21:22 | _Generic | Generic expression with controlling expression $@ is not expanded froma macro | test.c:21:12:21:12 | 1 | 1 | diff --git a/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.expected b/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.expected index 1abcb4f2bb..b0a970bbcf 100644 --- a/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.expected +++ b/c/misra/test/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.expected @@ -1,3 +1,3 @@ -| test.c:4:1:4:37 | #define M2(X) _Generic((X)++, int: 1) | Generic selection macro M2 contains a side effect '... ++', which is not from macro invocation arguments. | test.c:4:1:4:37 | #define M2(X) _Generic((X)++, int: 1) | (ignored) | -| test.c:7:1:7:38 | #define M3(X) _Generic(l1++, int: (X)) | Generic selection macro M3 contains a side effect '... ++', which is not from macro invocation arguments. | test.c:7:1:7:38 | #define M3(X) _Generic(l1++, int: (X)) | (ignored) | -| test.c:42:1:44:24 | #define M5(X) static volatile l ## X; _Generic(l ## X, int: 1) | Generic selection in macro M5 contains an invocation-dependent side effect which is not from macro invocation arguments, for example $@. | test.c:47:3:47:7 | _Generic | side effect 'la' | +| test.c:4:1:4:38 | #define M2(X) _Generic((X)++, int : 1) | Generic selection macro M2 contains a side effect '... ++', which is not from macro invocation arguments. | test.c:4:1:4:38 | #define M2(X) _Generic((X)++, int : 1) | (ignored) | +| test.c:7:1:7:39 | #define M3(X) _Generic(l1++, int : (X)) | Generic selection macro M3 contains a side effect '... ++', which is not from macro invocation arguments. | test.c:7:1:7:39 | #define M3(X) _Generic(l1++, int : (X)) | (ignored) | +| test.c:42:1:44:25 | #define M5(X) static volatile l ## X; _Generic(l ## X, int : 1) | Generic selection in macro M5 contains an invocation-dependent side effect which is not from macro invocation arguments, for example $@. | test.c:47:3:47:7 | _Generic | side effect 'la' | diff --git a/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.expected b/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.expected index 50d6277e84..6a56026947 100644 --- a/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.expected +++ b/c/misra/test/rules/RULE-23-3/GenericWithoutNonDefaultAssociation.expected @@ -1,2 +1,2 @@ -| test.c:2:1:2:35 | #define M1 _Generic(1, default: 1); | Generic selection contains no non-default association. | -| test.c:14:3:14:25 | _Generic | Generic selection contains no non-default association. | +| test.c:2:1:2:36 | #define M1 _Generic(1, default : 1); | Generic selection contains no non-default association. | +| test.c:14:3:14:26 | _Generic | Generic selection contains no non-default association. | diff --git a/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.expected b/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.expected index 27030fc768..132bb82979 100644 --- a/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.expected +++ b/c/misra/test/rules/RULE-23-4/GenericAssociationWithUnselectableType.expected @@ -1,13 +1,13 @@ -| test.c:11:18:11:18 | 1 | Generic selection uses unselectable type 'const int', due to qualifiers removed'. | test.c:11:18:11:18 | 1 | side effect | -| test.c:12:21:12:21 | 1 | Generic selection uses unselectable type 'volatile int', due to qualifiers removed'. | test.c:12:21:12:21 | 1 | side effect | -| test.c:13:20:13:20 | 1 | Generic selection uses unselectable type '_Atomic(int)', due to qualifiers removed'. | test.c:13:20:13:20 | 1 | side effect | -| test.c:16:27:16:27 | 1 | Generic selection uses unselectable type 'const volatile int', due to qualifiers removed'. | test.c:16:27:16:27 | 1 | side effect | -| test.c:18:18:18:18 | 1 | Generic selection uses unselectable type '(unnamed class/struct/union)', due to containing an anonymous struct or union type'. | test.c:18:18:18:18 | 1 | side effect | -| test.c:19:20:19:20 | 1 | Generic selection uses unselectable type 'struct *', due to containing an anonymous struct or union type'. | test.c:19:20:19:20 | 1 | side effect | -| test.c:24:17:24:17 | 1 | Generic selection uses unselectable type '(unnamed class/struct/union)', due to containing an anonymous struct or union type'. | test.c:24:17:24:17 | 1 | side effect | -| test.c:25:19:25:19 | 1 | Generic selection uses unselectable type 'union *', due to containing an anonymous struct or union type'. | test.c:25:19:25:19 | 1 | side effect | -| test.c:31:15:31:15 | 1 | Generic selection uses unselectable type 'int[3]', due to array-to-pointer decay'. | test.c:31:15:31:15 | 1 | side effect | -| test.c:40:1:40:53 | #define M1(X) _Generic((X), const int: 1, default: 0) | Generic in macro M1 has unselectable type 'const int', due to qualifiers removed. | test.c:40:1:40:53 | #define M1(X) _Generic((X), const int: 1, default: 0) | (ignored) | -| test.c:42:1:42:46 | #define M2(X) _Generic(1, X[3]: 1, default: 0) | Generic in macro M2 has an invocation-dependent unselectable type, for example $@. | test.c:49:3:49:10 | 1 | 'char[3]', due to array-to-pointer decay | -| test.c:52:3:52:15 | M3(X) | Generic resulting from invocation of macro $@ contains an unselectable type 'const int', due to qualifiers removed. | test.c:44:1:44:43 | #define M3(X) _Generic(1, X: 1, default: 0) | M3 | -| test.c:64:18:64:18 | 1 | Generic selection uses unselectable type 'const_int', due to qualifiers removed'. | test.c:64:18:64:18 | 1 | side effect | +| test.c:11:24:11:24 | 1 | Generic selection uses unselectable type 'const int', due to qualifiers removed'. | test.c:11:24:11:24 | 1 | side effect | +| test.c:12:27:12:27 | 1 | Generic selection uses unselectable type 'volatile int', due to qualifiers removed'. | test.c:12:27:12:27 | 1 | side effect | +| test.c:13:26:13:26 | 1 | Generic selection uses unselectable type '_Atomic(int)', due to qualifiers removed'. | test.c:13:26:13:26 | 1 | side effect | +| test.c:16:33:16:33 | 1 | Generic selection uses unselectable type 'const volatile int', due to qualifiers removed'. | test.c:16:33:16:33 | 1 | side effect | +| test.c:18:24:18:24 | 1 | Generic selection uses unselectable type '(unnamed class/struct/union)', due to containing an anonymous struct or union type'. | test.c:18:24:18:24 | 1 | side effect | +| test.c:19:26:19:26 | 1 | Generic selection uses unselectable type 'struct *', due to containing an anonymous struct or union type'. | test.c:19:26:19:26 | 1 | side effect | +| test.c:24:23:24:23 | 1 | Generic selection uses unselectable type '(unnamed class/struct/union)', due to containing an anonymous struct or union type'. | test.c:24:23:24:23 | 1 | side effect | +| test.c:25:25:25:25 | 1 | Generic selection uses unselectable type 'union *', due to containing an anonymous struct or union type'. | test.c:25:25:25:25 | 1 | side effect | +| test.c:31:21:31:21 | 1 | Generic selection uses unselectable type 'int[3]', due to array-to-pointer decay'. | test.c:31:21:31:21 | 1 | side effect | +| test.c:40:1:40:55 | #define M1(X) _Generic((X), const int : 1, default : 0) | Generic in macro M1 has unselectable type 'const int', due to qualifiers removed. | test.c:40:1:40:55 | #define M1(X) _Generic((X), const int : 1, default : 0) | (ignored) | +| test.c:42:1:42:48 | #define M2(X) _Generic(1, X[3] : 1, default : 0) | Generic in macro M2 has an invocation-dependent unselectable type, for example $@. | test.c:49:3:49:10 | 1 | 'char[3]', due to array-to-pointer decay | +| test.c:52:3:52:15 | M3(X) | Generic resulting from invocation of macro $@ contains an unselectable type 'const int', due to qualifiers removed. | test.c:44:1:44:45 | #define M3(X) _Generic(1, X : 1, default : 0) | M3 | +| test.c:64:24:64:24 | 1 | Generic selection uses unselectable type 'const_int', due to qualifiers removed'. | test.c:64:24:64:24 | 1 | side effect | diff --git a/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.expected b/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.expected index 994d55968c..3ed6b3f26b 100644 --- a/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.expected +++ b/c/misra/test/rules/RULE-23-5/DangerousDefaultSelectionForPointerInGeneric.expected @@ -1,84 +1,84 @@ -| test.c:41:3:41:44 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:41:3:41:44 | _Generic | | -| test.c:42:3:42:47 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:42:3:42:47 | _Generic | | -| test.c:43:3:43:53 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:43:3:43:53 | _Generic | | -| test.c:44:3:44:39 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:44:3:44:39 | _Generic | | -| test.c:45:3:45:45 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:45:3:45:45 | _Generic | | -| test.c:46:3:46:54 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:46:3:46:54 | _Generic | | -| test.c:48:3:48:38 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:48:3:48:38 | _Generic | | -| test.c:50:3:50:39 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:50:3:50:39 | _Generic | | -| test.c:51:3:51:45 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:51:3:51:45 | _Generic | | -| test.c:52:3:52:54 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:52:3:52:54 | _Generic | | -| test.c:57:3:57:53 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:57:3:57:53 | _Generic | | -| test.c:59:3:59:38 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to int *. | test.c:59:3:59:38 | _Generic | | -| test.c:61:3:61:53 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile int *. | test.c:61:3:61:53 | _Generic | | -| test.c:62:3:62:39 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to void *. | test.c:62:3:62:39 | _Generic | | -| test.c:63:3:63:54 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile void *. | test.c:63:3:63:54 | _Generic | | -| test.c:69:3:69:38 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to int *. | test.c:69:3:69:38 | _Generic | | -| test.c:70:3:70:44 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const int *. | test.c:70:3:70:44 | _Generic | | -| test.c:71:3:71:47 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to volatile int *. | test.c:71:3:71:47 | _Generic | | -| test.c:73:3:73:39 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to void *. | test.c:73:3:73:39 | _Generic | | -| test.c:74:3:74:45 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const void *. | test.c:74:3:74:45 | _Generic | | -| test.c:75:3:75:54 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const volatile void *. | test.c:75:3:75:54 | _Generic | | -| test.c:77:3:77:38 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to int *. | test.c:77:3:77:38 | _Generic | | -| test.c:78:3:78:44 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const int *. | test.c:78:3:78:44 | _Generic | | -| test.c:79:3:79:47 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to volatile int *. | test.c:79:3:79:47 | _Generic | | -| test.c:80:3:80:53 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const volatile int *. | test.c:80:3:80:53 | _Generic | | -| test.c:82:3:82:45 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const void *. | test.c:82:3:82:45 | _Generic | | -| test.c:83:3:83:54 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const volatile void *. | test.c:83:3:83:54 | _Generic | | -| test.c:85:3:85:38 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to int *. | test.c:85:3:85:38 | _Generic | | -| test.c:86:3:86:44 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const int *. | test.c:86:3:86:44 | _Generic | | -| test.c:87:3:87:53 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const volatile int *. | test.c:87:3:87:53 | _Generic | | -| test.c:88:3:88:39 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to void *. | test.c:88:3:88:39 | _Generic | | -| test.c:90:3:90:54 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const volatile void *. | test.c:90:3:90:54 | _Generic | | -| test.c:94:3:94:38 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to int *. | test.c:94:3:94:38 | _Generic | | -| test.c:95:3:95:44 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const int *. | test.c:95:3:95:44 | _Generic | | -| test.c:96:3:96:47 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to volatile int *. | test.c:96:3:96:47 | _Generic | | -| test.c:97:3:97:53 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const volatile int *. | test.c:97:3:97:53 | _Generic | | -| test.c:98:3:98:39 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to void *. | test.c:98:3:98:39 | _Generic | | -| test.c:99:3:99:45 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const void *. | test.c:99:3:99:45 | _Generic | | -| test.c:119:3:119:45 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:119:3:119:45 | _Generic | | -| test.c:120:3:120:48 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:120:3:120:48 | _Generic | | -| test.c:121:3:121:54 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:121:3:121:54 | _Generic | | -| test.c:122:3:122:40 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:122:3:122:40 | _Generic | | -| test.c:123:3:123:46 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:123:3:123:46 | _Generic | | -| test.c:124:3:124:55 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:124:3:124:55 | _Generic | | -| test.c:126:3:126:39 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:126:3:126:39 | _Generic | | -| test.c:128:3:128:40 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:128:3:128:40 | _Generic | | -| test.c:129:3:129:46 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:129:3:129:46 | _Generic | | -| test.c:130:3:130:55 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:130:3:130:55 | _Generic | | -| test.c:135:3:135:54 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:135:3:135:54 | _Generic | | -| test.c:137:3:137:39 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to int *. | test.c:137:3:137:39 | _Generic | | -| test.c:139:3:139:54 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile int *. | test.c:139:3:139:54 | _Generic | | -| test.c:140:3:140:40 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to void *. | test.c:140:3:140:40 | _Generic | | -| test.c:141:3:141:55 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile void *. | test.c:141:3:141:55 | _Generic | | -| test.c:147:3:147:39 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to int *. | test.c:147:3:147:39 | _Generic | | -| test.c:148:3:148:45 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const int *. | test.c:148:3:148:45 | _Generic | | -| test.c:149:3:149:48 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to volatile int *. | test.c:149:3:149:48 | _Generic | | -| test.c:151:3:151:40 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to void *. | test.c:151:3:151:40 | _Generic | | -| test.c:152:3:152:46 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const void *. | test.c:152:3:152:46 | _Generic | | -| test.c:153:3:153:55 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const volatile void *. | test.c:153:3:153:55 | _Generic | | -| test.c:156:3:156:45 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:156:3:156:45 | _Generic | | -| test.c:157:3:157:48 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:157:3:157:48 | _Generic | | -| test.c:158:3:158:54 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:158:3:158:54 | _Generic | | -| test.c:159:3:159:40 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:159:3:159:40 | _Generic | | -| test.c:160:3:160:46 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:160:3:160:46 | _Generic | | -| test.c:161:3:161:55 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:161:3:161:55 | _Generic | | -| test.c:163:3:163:39 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:163:3:163:39 | _Generic | | -| test.c:165:3:165:40 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:165:3:165:40 | _Generic | | -| test.c:166:3:166:46 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:166:3:166:46 | _Generic | | -| test.c:167:3:167:55 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:167:3:167:55 | _Generic | | -| test.c:172:3:172:54 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:172:3:172:54 | _Generic | | -| test.c:180:3:180:48 | _Generic | Generic matched default selection, as controlling argument type int(*)[3] does not undergo pointer conversion to int(*const)[3]. | test.c:180:3:180:48 | _Generic | | -| test.c:188:3:191:18 | _Generic | Generic matched default selection, as controlling argument type int(*)[3] does not undergo pointer conversion to int(*const)[3]. | test.c:188:3:191:18 | _Generic | | -| test.c:200:3:200:47 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to int *. | test.c:200:3:200:47 | _Generic | | -| test.c:201:3:201:47 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:201:3:201:47 | _Generic | | -| test.c:215:3:215:44 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:215:3:215:44 | _Generic | | -| test.c:216:3:216:46 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int_t *. | test.c:216:3:216:46 | _Generic | | -| test.c:217:3:217:42 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to c_int_t *. | test.c:217:3:217:42 | _Generic | | -| test.c:221:3:221:45 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to const int *. | test.c:221:3:221:45 | _Generic | | -| test.c:222:3:222:47 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to const int_t *. | test.c:222:3:222:47 | _Generic | | -| test.c:223:3:223:43 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to c_int_t *. | test.c:223:3:223:43 | _Generic | | -| test.c:225:3:225:38 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:225:3:225:38 | _Generic | | -| test.c:226:3:226:40 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int_t *. | test.c:226:3:226:40 | _Generic | | -| test.c:231:3:231:39 | _Generic | Generic matched default selection, as controlling argument type c_int_t * does not undergo pointer conversion to int *. | test.c:231:3:231:39 | _Generic | | -| test.c:232:3:232:41 | _Generic | Generic matched default selection, as controlling argument type c_int_t * does not undergo pointer conversion to int_t *. | test.c:232:3:232:41 | _Generic | | +| test.c:41:3:41:46 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:41:3:41:46 | _Generic | | +| test.c:42:3:42:49 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:42:3:42:49 | _Generic | | +| test.c:43:3:43:55 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:43:3:43:55 | _Generic | | +| test.c:44:3:44:41 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:44:3:44:41 | _Generic | | +| test.c:45:3:45:47 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:45:3:45:47 | _Generic | | +| test.c:46:3:46:56 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:46:3:46:56 | _Generic | | +| test.c:48:3:48:40 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:48:3:48:40 | _Generic | | +| test.c:50:3:50:41 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:50:3:50:41 | _Generic | | +| test.c:51:3:51:47 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:51:3:51:47 | _Generic | | +| test.c:52:3:52:56 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:52:3:52:56 | _Generic | | +| test.c:57:3:57:55 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:57:3:57:55 | _Generic | | +| test.c:59:3:59:40 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to int *. | test.c:59:3:59:40 | _Generic | | +| test.c:61:3:61:55 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile int *. | test.c:61:3:61:55 | _Generic | | +| test.c:62:3:62:41 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to void *. | test.c:62:3:62:41 | _Generic | | +| test.c:63:3:63:56 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile void *. | test.c:63:3:63:56 | _Generic | | +| test.c:69:3:69:40 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to int *. | test.c:69:3:69:40 | _Generic | | +| test.c:70:3:70:46 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const int *. | test.c:70:3:70:46 | _Generic | | +| test.c:71:3:71:49 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to volatile int *. | test.c:71:3:71:49 | _Generic | | +| test.c:73:3:73:41 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to void *. | test.c:73:3:73:41 | _Generic | | +| test.c:74:3:74:47 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const void *. | test.c:74:3:74:47 | _Generic | | +| test.c:75:3:75:56 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const volatile void *. | test.c:75:3:75:56 | _Generic | | +| test.c:77:3:77:40 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to int *. | test.c:77:3:77:40 | _Generic | | +| test.c:78:3:78:46 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const int *. | test.c:78:3:78:46 | _Generic | | +| test.c:79:3:79:49 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to volatile int *. | test.c:79:3:79:49 | _Generic | | +| test.c:80:3:80:55 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const volatile int *. | test.c:80:3:80:55 | _Generic | | +| test.c:82:3:82:47 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const void *. | test.c:82:3:82:47 | _Generic | | +| test.c:83:3:83:56 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to const volatile void *. | test.c:83:3:83:56 | _Generic | | +| test.c:85:3:85:40 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to int *. | test.c:85:3:85:40 | _Generic | | +| test.c:86:3:86:46 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const int *. | test.c:86:3:86:46 | _Generic | | +| test.c:87:3:87:55 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const volatile int *. | test.c:87:3:87:55 | _Generic | | +| test.c:88:3:88:41 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to void *. | test.c:88:3:88:41 | _Generic | | +| test.c:90:3:90:56 | _Generic | Generic matched default selection, as controlling argument type const void * does not undergo pointer conversion to const volatile void *. | test.c:90:3:90:56 | _Generic | | +| test.c:94:3:94:40 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to int *. | test.c:94:3:94:40 | _Generic | | +| test.c:95:3:95:46 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const int *. | test.c:95:3:95:46 | _Generic | | +| test.c:96:3:96:49 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to volatile int *. | test.c:96:3:96:49 | _Generic | | +| test.c:97:3:97:55 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const volatile int *. | test.c:97:3:97:55 | _Generic | | +| test.c:98:3:98:41 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to void *. | test.c:98:3:98:41 | _Generic | | +| test.c:99:3:99:47 | _Generic | Generic matched default selection, as controlling argument type const volatile void * does not undergo pointer conversion to const void *. | test.c:99:3:99:47 | _Generic | | +| test.c:119:3:119:47 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:119:3:119:47 | _Generic | | +| test.c:120:3:120:50 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:120:3:120:50 | _Generic | | +| test.c:121:3:121:56 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:121:3:121:56 | _Generic | | +| test.c:122:3:122:42 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:122:3:122:42 | _Generic | | +| test.c:123:3:123:48 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:123:3:123:48 | _Generic | | +| test.c:124:3:124:57 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:124:3:124:57 | _Generic | | +| test.c:126:3:126:41 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:126:3:126:41 | _Generic | | +| test.c:128:3:128:42 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:128:3:128:42 | _Generic | | +| test.c:129:3:129:48 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:129:3:129:48 | _Generic | | +| test.c:130:3:130:57 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:130:3:130:57 | _Generic | | +| test.c:135:3:135:56 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:135:3:135:56 | _Generic | | +| test.c:137:3:137:41 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to int *. | test.c:137:3:137:41 | _Generic | | +| test.c:139:3:139:56 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile int *. | test.c:139:3:139:56 | _Generic | | +| test.c:140:3:140:42 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to void *. | test.c:140:3:140:42 | _Generic | | +| test.c:141:3:141:57 | _Generic | Generic matched default selection, as controlling argument type volatile int * does not undergo pointer conversion to const volatile void *. | test.c:141:3:141:57 | _Generic | | +| test.c:147:3:147:41 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to int *. | test.c:147:3:147:41 | _Generic | | +| test.c:148:3:148:47 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const int *. | test.c:148:3:148:47 | _Generic | | +| test.c:149:3:149:50 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to volatile int *. | test.c:149:3:149:50 | _Generic | | +| test.c:151:3:151:42 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to void *. | test.c:151:3:151:42 | _Generic | | +| test.c:152:3:152:48 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const void *. | test.c:152:3:152:48 | _Generic | | +| test.c:153:3:153:57 | _Generic | Generic matched default selection, as controlling argument type const volatile int * does not undergo pointer conversion to const volatile void *. | test.c:153:3:153:57 | _Generic | | +| test.c:156:3:156:47 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:156:3:156:47 | _Generic | | +| test.c:157:3:157:50 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to volatile int *. | test.c:157:3:157:50 | _Generic | | +| test.c:158:3:158:56 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile int *. | test.c:158:3:158:56 | _Generic | | +| test.c:159:3:159:42 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:159:3:159:42 | _Generic | | +| test.c:160:3:160:48 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const void *. | test.c:160:3:160:48 | _Generic | | +| test.c:161:3:161:57 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const volatile void *. | test.c:161:3:161:57 | _Generic | | +| test.c:163:3:163:41 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:163:3:163:41 | _Generic | | +| test.c:165:3:165:42 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to void *. | test.c:165:3:165:42 | _Generic | | +| test.c:166:3:166:48 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const void *. | test.c:166:3:166:48 | _Generic | | +| test.c:167:3:167:57 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile void *. | test.c:167:3:167:57 | _Generic | | +| test.c:172:3:172:56 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to const volatile int *. | test.c:172:3:172:56 | _Generic | | +| test.c:180:3:180:50 | _Generic | Generic matched default selection, as controlling argument type int(*)[3] does not undergo pointer conversion to int(*const)[3]. | test.c:180:3:180:50 | _Generic | | +| test.c:188:3:192:16 | _Generic | Generic matched default selection, as controlling argument type int(*)[3] does not undergo pointer conversion to int(*const)[3]. | test.c:188:3:192:16 | _Generic | | +| test.c:201:3:201:49 | _Generic | Generic matched default selection, as controlling argument type void * does not undergo pointer conversion to int *. | test.c:201:3:201:49 | _Generic | | +| test.c:202:3:202:49 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to void *. | test.c:202:3:202:49 | _Generic | | +| test.c:216:3:216:46 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int *. | test.c:216:3:216:46 | _Generic | | +| test.c:217:3:217:48 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to const int_t *. | test.c:217:3:217:48 | _Generic | | +| test.c:218:3:218:44 | _Generic | Generic matched default selection, as controlling argument type int * does not undergo pointer conversion to c_int_t *. | test.c:218:3:218:44 | _Generic | | +| test.c:222:3:222:47 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to const int *. | test.c:222:3:222:47 | _Generic | | +| test.c:223:3:223:49 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to const int_t *. | test.c:223:3:223:49 | _Generic | | +| test.c:224:3:224:45 | _Generic | Generic matched default selection, as controlling argument type int_t * does not undergo pointer conversion to c_int_t *. | test.c:224:3:224:45 | _Generic | | +| test.c:226:3:226:40 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int *. | test.c:226:3:226:40 | _Generic | | +| test.c:227:3:227:42 | _Generic | Generic matched default selection, as controlling argument type const int * does not undergo pointer conversion to int_t *. | test.c:227:3:227:42 | _Generic | | +| test.c:232:3:232:41 | _Generic | Generic matched default selection, as controlling argument type c_int_t * does not undergo pointer conversion to int *. | test.c:232:3:232:41 | _Generic | | +| test.c:233:3:233:43 | _Generic | Generic matched default selection, as controlling argument type c_int_t * does not undergo pointer conversion to int_t *. | test.c:233:3:233:43 | _Generic | | diff --git a/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.expected b/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.expected index 1cdcc82698..4f02d039ce 100644 --- a/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.expected +++ b/c/misra/test/rules/RULE-23-6/GenericExpressionWithIncorrectEssentialType.expected @@ -1,4 +1,4 @@ -| test.c:11:3:11:8 | _Generic | Controlling expression in generic macro $@ has standard type (unnamed enum), which doesn't match its essential type (unnamed enum). | test.c:6:1:6:71 | #define M1(X) _Generic((X), int: 1, unsigned int: 1, short: 2, long: 3) | M1 | -| test.c:15:3:15:13 | _Generic | Controlling expression in generic macro $@ has standard type int, which doesn't match its essential type short. | test.c:6:1:6:71 | #define M1(X) _Generic((X), int: 1, unsigned int: 1, short: 2, long: 3) | M1 | -| test.c:18:3:18:23 | _Generic | Controlling expression in generic has standard type int, which doesn't match its essential type char. | test.c:18:3:18:23 | _Generic | | -| test.c:19:3:19:53 | _Generic | Controlling expression in generic has standard type int, which doesn't match its essential type short. | test.c:19:3:19:53 | _Generic | | +| test.c:11:3:11:8 | _Generic | Controlling expression in generic macro $@ has standard type (unnamed enum), which doesn't match its essential type (unnamed enum). | test.c:6:1:6:75 | #define M1(X) _Generic((X), int : 1, unsigned int : 1, short : 2, long : 3) | M1 | +| test.c:15:3:15:13 | _Generic | Controlling expression in generic macro $@ has standard type int, which doesn't match its essential type short. | test.c:6:1:6:75 | #define M1(X) _Generic((X), int : 1, unsigned int : 1, short : 2, long : 3) | M1 | +| test.c:18:3:18:24 | _Generic | Controlling expression in generic has standard type int, which doesn't match its essential type char. | test.c:18:3:18:24 | _Generic | | +| test.c:19:3:19:55 | _Generic | Controlling expression in generic has standard type int, which doesn't match its essential type short. | test.c:19:3:19:55 | _Generic | | diff --git a/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.expected b/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.expected index 57eecd6be8..47a8acce92 100644 --- a/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.expected +++ b/c/misra/test/rules/RULE-23-7/InvalidGenericMacroArgumentEvaluation.expected @@ -1,12 +1,12 @@ -| test.c:9:1:9:51 | #define M3(X) _Generic((X), int: f1(X), default: 0) | Generic macro M3 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 2. | -| test.c:10:1:10:61 | #define M4(X) (X) + _Generic((X), int: f1(X), default: f1(X)) | Generic macro M4 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | -| test.c:11:1:11:61 | #define M5(X) _Generic((X), int: f1(X), default: f1(X)) + (X) | Generic macro M5 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | -| test.c:12:1:12:63 | #define M6(X) _Generic((X), int: f1((X) + (X)), default: f1(X)) | Generic macro M6 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 1 more than once. | -| test.c:21:1:21:36 | #define M9(X) _Generic((X), int: f1) | Generic macro M9 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 1. | -| test.c:23:1:23:40 | #define M10(X) _Generic((X), int: f1(1)) | Generic macro M10 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 1. | -| test.c:32:1:32:58 | #define M12(X) _Generic((X) + (X), int: f1(X), default: 1) | Generic macro M12 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 2. | -| test.c:33:1:33:68 | #define M13(X) _Generic((X) + (X), int: f1(X), default: f1(X)) + (X) | Generic macro M13 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | -| test.c:43:1:43:77 | #define M17(X,Y) _Generic((X) + (Y), int: f2((X), (Y)), default: f2((X), 1)) | Generic macro M17 may have unexpected behavior from side effects in parameter Y, as it is not expanded in generic selection 2. | -| test.c:67:1:67:78 | #define M26(X) _Generic((X), int: IGNORE_2ND(X, X), default: IGNORE_2ND(X, X)) | Generic macro M26 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 1 more than once. | -| test.c:67:1:67:78 | #define M26(X) _Generic((X), int: IGNORE_2ND(X, X), default: IGNORE_2ND(X, X)) | Generic macro M26 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 2 more than once. | -| test.c:68:1:68:75 | #define M27(X) _Generic((X), int: f1(IGNORE(X)), default: f1(IGNORE(X)))(X) | Generic macro M27 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | +| test.c:9:1:9:53 | #define M3(X) _Generic((X), int : f1(X), default : 0) | Generic macro M3 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 2. | +| test.c:10:1:10:63 | #define M4(X) (X) + _Generic((X), int : f1(X), default : f1(X)) | Generic macro M4 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | +| test.c:11:1:11:63 | #define M5(X) _Generic((X), int : f1(X), default : f1(X)) + (X) | Generic macro M5 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | +| test.c:12:1:12:65 | #define M6(X) _Generic((X), int : f1((X) + (X)), default : f1(X)) | Generic macro M6 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 1 more than once. | +| test.c:21:1:21:37 | #define M9(X) _Generic((X), int : f1) | Generic macro M9 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 1. | +| test.c:23:1:23:41 | #define M10(X) _Generic((X), int : f1(1)) | Generic macro M10 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 1. | +| test.c:32:1:32:60 | #define M12(X) _Generic((X) + (X), int : f1(X), default : 1) | Generic macro M12 may have unexpected behavior from side effects in parameter X, as it is not expanded in generic selection 2. | +| test.c:33:1:33:70 | #define M13(X) _Generic((X) + (X), int : f1(X), default : f1(X)) + (X) | Generic macro M13 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | +| test.c:43:1:43:79 | #define M17(X,Y) _Generic((X) + (Y), int : f2((X), (Y)), default : f2((X), 1)) | Generic macro M17 may have unexpected behavior from side effects in parameter Y, as it is not expanded in generic selection 2. | +| test.c:68:1:68:80 | #define M26(X) _Generic((X), int : IGNORE_2ND(X, X), default : IGNORE_2ND(X, X)) | Generic macro M26 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 1 more than once. | +| test.c:68:1:68:80 | #define M26(X) _Generic((X), int : IGNORE_2ND(X, X), default : IGNORE_2ND(X, X)) | Generic macro M26 may have unexpected behavior from side effects in parameter X, as it is expanded in generic selection 2 more than once. | +| test.c:69:1:69:77 | #define M27(X) _Generic((X), int : f1(IGNORE(X)), default : f1(IGNORE(X)))(X) | Generic macro M27 may have unexpected behavior from side effects in parameter X, as it is expanded outside the generic selection and inside the generic selection. | diff --git a/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.expected b/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.expected index 5951834d00..fb407e2ff1 100644 --- a/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.expected +++ b/c/misra/test/rules/RULE-23-8/DefaultGenericSelectionNotFirstOrLast.expected @@ -1,4 +1,4 @@ -| test.c:11:1:11:64 | #define M4(X) _Generic((X), int: 1, default: 0, unsigned int: 2) | Generic macro M4 has default as 2nd association, which is not first or last. | test.c:11:1:11:64 | #define M4(X) _Generic((X), int: 1, default: 0, unsigned int: 2) | (ignored) | -| test.c:17:1:17:60 | #define M5(__VA_ARGS__...) _Generic(0, __VA_ARGS__, default: 0, int: 1) | Generic macro M5 has a default association which is not first or last, for example $@. | test.c:28:5:28:23 | _Generic | 2nd | -| test.c:34:5:34:27 | M6(__VA_ARGS__...) | Generic macro $@, in this expansion, has default as 2nd association, which is not first or last. | test.c:19:1:19:48 | #define M6(__VA_ARGS__...) _Generic(0, __VA_ARGS__, int: 1) | M6 | -| test.c:44:5:44:52 | _Generic | Generic has default as 2nd association, which is not first or last. | test.c:44:5:44:52 | _Generic | | +| test.c:11:1:11:67 | #define M4(X) _Generic((X), int : 1, default : 0, unsigned int : 2) | Generic macro M4 has default as 2nd association, which is not first or last. | test.c:11:1:11:67 | #define M4(X) _Generic((X), int : 1, default : 0, unsigned int : 2) | (ignored) | +| test.c:17:1:17:62 | #define M5(__VA_ARGS__...) _Generic(0, __VA_ARGS__, default : 0, int : 1) | Generic macro M5 has a default association which is not first or last, for example $@. | test.c:30:3:30:22 | _Generic | 2nd | +| test.c:37:3:37:27 | M6(__VA_ARGS__...) | Generic macro $@, in this expansion, has default as 2nd association, which is not first or last. | test.c:19:1:19:49 | #define M6(__VA_ARGS__...) _Generic(0, __VA_ARGS__, int : 1) | M6 | +| test.c:48:3:48:53 | _Generic | Generic has default as 2nd association, which is not first or last. | test.c:48:3:48:53 | _Generic | | From 2265ed48d7b8739a4061e12ac601c5d2277e7ef9 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 21 Mar 2025 00:50:52 -0700 Subject: [PATCH 09/12] Address feedback --- c/common/src/codingstandards/c/Generic.qll | 2 +- ...ricSelectionDoesntDependOnMacroArgument.ql | 9 ++-- .../GenericSelectionNotExpandedFromAMacro.ql | 4 +- ...ricSelectionNotFromMacroWithSideEffects.ql | 1 + .../GenericWithoutNonDefaultAssociation.ql | 2 +- ...ectionDoesntDependOnMacroArgument.expected | 6 +-- ...ricSelectionNotExpandedFromAMacro.expected | 2 +- .../codingstandards/cpp/types/Compatible.qll | 51 +------------------ .../cpp/types/SimpleAssignment.qll | 6 +++ 9 files changed, 21 insertions(+), 62 deletions(-) diff --git a/c/common/src/codingstandards/c/Generic.qll b/c/common/src/codingstandards/c/Generic.qll index 784c16778e..1281be5f71 100644 --- a/c/common/src/codingstandards/c/Generic.qll +++ b/c/common/src/codingstandards/c/Generic.qll @@ -115,7 +115,7 @@ class ParsedGenericMacro extends Macro { ) } - string getControllingExprString() { result = getSelectionString(1) } + string getControllingExprString() { result = getSelectionString(1).trim() } bindingset[str, word] private int countWordInString(string word, string str) { diff --git a/c/misra/src/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql b/c/misra/src/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql index 0dc0e5273a..1a76339f50 100644 --- a/c/misra/src/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql +++ b/c/misra/src/rules/RULE-23-1/GenericSelectionDoesntDependOnMacroArgument.ql @@ -19,8 +19,9 @@ import codingstandards.c.Generic from ParsedGenericMacro macro, string ctrlExpr where not isExcluded(macro, GenericsPackage::genericSelectionDoesntDependOnMacroArgumentQuery()) and - ctrlExpr = macro.getControllingExprString().trim() and - not macro.expansionsInsideControllingExpr(_) > 0 + ctrlExpr = macro.getControllingExprString() and + // No parameter exists that is expanded in the controlling expression one or more times + not exists(string parameter | macro.expansionsInsideControllingExpr(parameter) > 0) select macro, - "Generic macro " + macro.getName() + " uses controlling expr " + ctrlExpr + - ", which doesn't match any macro parameter." + "Generic macro " + macro.getName() + " doesn't refer to a macro parameter in controlling expr '" + + ctrlExpr + "'." diff --git a/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql b/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql index 540804ffc4..603c44e8e1 100644 --- a/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql +++ b/c/misra/src/rules/RULE-23-1/GenericSelectionNotExpandedFromAMacro.ql @@ -19,5 +19,5 @@ where not isExcluded(generic, GenericsPackage::genericSelectionNotExpandedFromAMacroQuery()) and ctrlExpr = generic.getControllingExpr() and not exists(MacroInvocation mi | mi.getAGeneratedElement() = generic.getExpr()) -select generic, "Generic expression with controlling expression $@ is not expanded froma macro", - ctrlExpr, ctrlExpr.toString() +select generic, "$@ in generic expression does not expand a macro parameter.", ctrlExpr, + "Controlling expression" diff --git a/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql b/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql index 4b1a2d26c3..d7fcb13d76 100644 --- a/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql +++ b/c/misra/src/rules/RULE-23-2/GenericSelectionNotFromMacroWithSideEffects.ql @@ -72,6 +72,7 @@ module GenericSideEffectReportConfig implements MacroReportConfigSig Date: Tue, 25 Mar 2025 00:30:09 -0700 Subject: [PATCH 10/12] Format SimpleAssignment.qll --- cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll b/cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll index a38939c9ea..4f7a85c80a 100644 --- a/cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll +++ b/cpp/common/src/codingstandards/cpp/types/SimpleAssignment.qll @@ -1,9 +1,10 @@ /** * Helper predicates related to C11/C17 constraints on simple assignment between two types. - * + * * Currently only a subset of the constraints are implemented, specifically those * related to pointer types. */ + import codingstandards.cpp.types.LvalueConversion import codingstandards.cpp.types.Compatible From f268604d7d3920dad7c3546c60d56e90152ef6f5 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Tue, 25 Mar 2025 00:31:41 -0700 Subject: [PATCH 11/12] Update regex --- c/common/src/codingstandards/c/Generic.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/common/src/codingstandards/c/Generic.qll b/c/common/src/codingstandards/c/Generic.qll index 1281be5f71..19d5aad443 100644 --- a/c/common/src/codingstandards/c/Generic.qll +++ b/c/common/src/codingstandards/c/Generic.qll @@ -2,7 +2,7 @@ import cpp import codingstandards.cpp.Macro import codingstandards.cpp.MatchingParenthesis -string genericRegexp() { result = ".*_Generic\\s*\\(\\s*(.+),.*" } +string genericRegexp() { result = "\\b_Generic\\s*\\(\\s*(.+),.*" } bindingset[input] string deparenthesize(string input) { From 6245f7210e709e205b21e092e2cd907689cdd638 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Tue, 25 Mar 2025 00:34:09 -0700 Subject: [PATCH 12/12] Format --- cpp/common/src/codingstandards/cpp/types/Compatible.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/common/src/codingstandards/cpp/types/Compatible.qll b/cpp/common/src/codingstandards/cpp/types/Compatible.qll index fb8bf46aad..d6f65126e8 100644 --- a/cpp/common/src/codingstandards/cpp/types/Compatible.qll +++ b/cpp/common/src/codingstandards/cpp/types/Compatible.qll @@ -369,4 +369,4 @@ private class FunctionType extends Type { result = this.(RoutineType).getParameterType(i) or result = this.(FunctionPointerIshType).getParameterType(i) } -} \ No newline at end of file +}