From 006e4a1d464573e48e7fdfd866549d2939e09a7a Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 15 Oct 2024 00:00:10 +0100 Subject: [PATCH 1/5] EssentialTypes: Correctly handle enumerations --- .../c/misra/EssentialTypes.qll | 42 ++++++++++++++++++- c/misra/test/c/misra/EssentialTypes.expected | 14 +++++++ c/misra/test/c/misra/test.c | 21 ++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/c/misra/src/codingstandards/c/misra/EssentialTypes.qll b/c/misra/src/codingstandards/c/misra/EssentialTypes.qll index d01bc81038..c92b6403c7 100644 --- a/c/misra/src/codingstandards/c/misra/EssentialTypes.qll +++ b/c/misra/src/codingstandards/c/misra/EssentialTypes.qll @@ -130,12 +130,17 @@ EssentialTypeCategory getEssentialTypeCategory(Type type) { essentialType.(IntegralType).isSigned() and not essentialType instanceof PlainCharType or + // Anonymous enums are considered to be signed + result = EssentiallySignedType() and + essentialType instanceof AnonymousEnumType and + not essentialType instanceof MisraBoolType + or result = EssentiallyUnsignedType() and essentialType.(IntegralType).isUnsigned() and not essentialType instanceof PlainCharType or result = EssentiallyEnumType() and - essentialType instanceof Enum and + essentialType instanceof NamedEnumType and not essentialType instanceof MisraBoolType or result = EssentiallyFloatingType() and @@ -348,8 +353,41 @@ class EssentialBinaryArithmeticExpr extends EssentialExpr, BinaryArithmeticOpera } } +/** + * A named Enum type, as per D.5. + */ +class NamedEnumType extends Enum { + NamedEnumType() { + not isAnonymous() + or + exists(Type useOfEnum | this = useOfEnum.stripType() | + exists(TypedefType t | t.getBaseType() = useOfEnum) + or + exists(Function f | f.getType() = useOfEnum or f.getAParameter().getType() = useOfEnum) + or + exists(Struct s | s.getAField().getType() = useOfEnum) + or + exists(Variable v | v.getType() = useOfEnum) + ) + } +} + +/** + * An anonymous Enum type, as per D.5. + */ +class AnonymousEnumType extends Enum { + AnonymousEnumType() { not this instanceof NamedEnumType } +} + +/** + * The EssentialType of an EnumConstantAccess, which may be essentially enum or essentially signed. + */ class EssentialEnumConstantAccess extends EssentialExpr, EnumConstantAccess { - override Type getEssentialType() { result = getTarget().getDeclaringEnum() } + override Type getEssentialType() { + exists(Enum e | e = getTarget().getDeclaringEnum() | + if e instanceof NamedEnumType then result = e else result = stlr(this) + ) + } } class EssentialLiteral extends EssentialExpr, Literal { diff --git a/c/misra/test/c/misra/EssentialTypes.expected b/c/misra/test/c/misra/EssentialTypes.expected index 8b6b45a2f0..f7f8aed9c8 100644 --- a/c/misra/test/c/misra/EssentialTypes.expected +++ b/c/misra/test/c/misra/EssentialTypes.expected @@ -1,3 +1,9 @@ +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | +| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type | | test.c:4:20:4:20 | 1 | signed char | signed char | essentially Signed type | | test.c:4:20:4:20 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type | | test.c:5:23:5:23 | 1 | signed char | signed char | essentially Signed type | @@ -73,3 +79,11 @@ | test.c:54:3:54:5 | ~ ... | int | int | essentially Signed type | | test.c:54:4:54:5 | (int)... | int | int | essentially Signed type | | test.c:54:4:54:5 | ss | signed short | signed short | essentially Signed type | +| test.c:63:30:63:32 | ((unnamed enum))... | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:63:30:63:32 | EC5 | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:70:3:70:5 | EC1 | signed char | signed char | essentially Signed type | +| test.c:71:3:71:5 | EC2 | E1 | E1 | essentially Enum Type | +| test.c:72:3:72:5 | EC3 | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:73:3:73:5 | EC4 | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:74:3:74:5 | EC5 | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:75:3:75:5 | EC6 | (unnamed enum) | (unnamed enum) | essentially Enum Type | diff --git a/c/misra/test/c/misra/test.c b/c/misra/test/c/misra/test.c index 6156e9440e..64546f410c 100644 --- a/c/misra/test/c/misra/test.c +++ b/c/misra/test/c/misra/test.c @@ -52,4 +52,25 @@ void testUnary() { ~us; // Should be essentially unsigned ~s; // Should be essentially signed ~ss; // Should be essentially signed +} + +enum { EC1 }; +enum E1 { EC2 }; +typedef enum { EC3 } E2; + +enum { EC4 } g; + +enum { EC5 } test() { return EC5; } + +struct S1 { + enum { EC6 } m; +}; + +void testEnums() { + EC1; // Should be essentially signed + EC2; // Should be essentially enum + EC3; // Should be essentially enum + EC4; // Should be essentially enum + EC5; // Should be essentially enum + EC6; // Should be essentially enum } \ No newline at end of file From 352f778af08e8d7eae2c071bd62c495bb6cc5351 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 15 Oct 2024 00:02:48 +0100 Subject: [PATCH 2/5] Handle non-zero length characters --- c/misra/src/codingstandards/c/misra/EssentialTypes.qll | 6 ++++-- c/misra/test/c/misra/EssentialTypes.expected | 3 +++ c/misra/test/c/misra/test.c | 6 ++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/c/misra/src/codingstandards/c/misra/EssentialTypes.qll b/c/misra/src/codingstandards/c/misra/EssentialTypes.qll index c92b6403c7..4783547ed2 100644 --- a/c/misra/src/codingstandards/c/misra/EssentialTypes.qll +++ b/c/misra/src/codingstandards/c/misra/EssentialTypes.qll @@ -393,9 +393,11 @@ class EssentialEnumConstantAccess extends EssentialExpr, EnumConstantAccess { class EssentialLiteral extends EssentialExpr, Literal { override Type getEssentialType() { if this instanceof BooleanLiteral - then result instanceof MisraBoolType + then + // This returns a multitude of types - not sure if we really want that + result instanceof MisraBoolType else ( - if this.(CharLiteral).getCharacter().length() = 1 + if this instanceof CharLiteral then result instanceof PlainCharType else exists(Type underlyingStandardType | diff --git a/c/misra/test/c/misra/EssentialTypes.expected b/c/misra/test/c/misra/EssentialTypes.expected index f7f8aed9c8..c0e010b8e4 100644 --- a/c/misra/test/c/misra/EssentialTypes.expected +++ b/c/misra/test/c/misra/EssentialTypes.expected @@ -87,3 +87,6 @@ | test.c:73:3:73:5 | EC4 | (unnamed enum) | (unnamed enum) | essentially Enum Type | | test.c:74:3:74:5 | EC5 | (unnamed enum) | (unnamed enum) | essentially Enum Type | | test.c:75:3:75:5 | EC6 | (unnamed enum) | (unnamed enum) | essentially Enum Type | +| test.c:79:3:79:5 | 97 | char | char | essentially Character type | +| test.c:80:3:80:6 | 10 | char | char | essentially Character type | +| test.c:81:3:81:6 | 0 | char | char | essentially Character type | diff --git a/c/misra/test/c/misra/test.c b/c/misra/test/c/misra/test.c index 64546f410c..b3fdddd591 100644 --- a/c/misra/test/c/misra/test.c +++ b/c/misra/test/c/misra/test.c @@ -73,4 +73,10 @@ void testEnums() { EC4; // Should be essentially enum EC5; // Should be essentially enum EC6; // Should be essentially enum +} + +void testControlChar() { + 'a'; // Essentially char + '\n'; // Essentially char + '\0'; // Essentially char } \ No newline at end of file From b5377b402bb0f15575cfb7afb8412c6907c021b2 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 15 Oct 2024 00:05:16 +0100 Subject: [PATCH 3/5] Rule 10.4: Update to reflect EssentialType improvements --- c/misra/test/rules/RULE-10-4/test.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/c/misra/test/rules/RULE-10-4/test.c b/c/misra/test/rules/RULE-10-4/test.c index 666590a2d5..b803d487a0 100644 --- a/c/misra/test/rules/RULE-10-4/test.c +++ b/c/misra/test/rules/RULE-10-4/test.c @@ -33,4 +33,8 @@ void testOps() { A < A; // COMPLIANT e1a < e2a; // NON_COMPLIANT A < D; // NON_COMPLIANT + + enum { G }; + s32 + G; // COMPLIANT + c == '\n'; // COMPLIANT } \ No newline at end of file From 54f724e76885a05605fb9a5bb4439f2dfa17cb38 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 15 Oct 2024 00:07:03 +0100 Subject: [PATCH 4/5] Rule 10.4: Resolve typedefs before determining if enums are equal --- .../RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.ql | 2 +- c/misra/test/rules/RULE-10-4/test.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/c/misra/src/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.ql b/c/misra/src/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.ql index cc4c860d7d..d1fed06319 100644 --- a/c/misra/src/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.ql +++ b/c/misra/src/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.ql @@ -38,7 +38,7 @@ where // be reported as non-compliant. leftOpTypeCategory = EssentiallyEnumType() and rightOpTypeCategory = EssentiallyEnumType() and - not leftOpEssentialType = rightOpEssentialType and + not leftOpEssentialType.getUnspecifiedType() = rightOpEssentialType.getUnspecifiedType() and message = "The operands of this operator with usual arithmetic conversions have mismatched essentially Enum types (left operand: " + leftOpEssentialType + ", right operand: " + rightOpEssentialType + ")." diff --git a/c/misra/test/rules/RULE-10-4/test.c b/c/misra/test/rules/RULE-10-4/test.c index b803d487a0..cbcb7191f6 100644 --- a/c/misra/test/rules/RULE-10-4/test.c +++ b/c/misra/test/rules/RULE-10-4/test.c @@ -37,4 +37,9 @@ void testOps() { enum { G }; s32 + G; // COMPLIANT c == '\n'; // COMPLIANT + + typedef enum { H } E3; + + E3 e3a = H; + e3a < H; // COMPLIANT } \ No newline at end of file From 2ae343c294b5ec893677b289f63cadf57a1fc8ac Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 15 Oct 2024 00:11:02 +0100 Subject: [PATCH 5/5] Add change note --- change_notes/2024-10-15-lits-and-constants-10-4.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 change_notes/2024-10-15-lits-and-constants-10-4.md diff --git a/change_notes/2024-10-15-lits-and-constants-10-4.md b/change_notes/2024-10-15-lits-and-constants-10-4.md new file mode 100644 index 0000000000..cfcb309204 --- /dev/null +++ b/change_notes/2024-10-15-lits-and-constants-10-4.md @@ -0,0 +1,5 @@ + - `RULE-10-4` - `OperandswithMismatchedEssentialTypeCategory.ql`: + - Removed false positives where a specified or typedef'd enum type was compared to an enum constant type. + - `EssentialType` - for all queries related to essential types: + - `\n` and other control characters are now correctly deduced as essentially char type, instead of an essentially integer type. + - Enum constants for anonymous enums are now correctly deduced as an essentially signed integer type instead of essentially enum. \ No newline at end of file