Skip to content

Commit 46b1ea1

Browse files
authored
Merge branch 'main' into feat-type-member-alias-a14-5-2
2 parents 7e8d2a1 + 3f73b26 commit 46b1ea1

File tree

39 files changed

+891
-19
lines changed

39 files changed

+891
-19
lines changed

c/misra/src/codingstandards/c/misra/EssentialTypes.qll

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,17 @@ EssentialTypeCategory getEssentialTypeCategory(Type type) {
130130
essentialType.(IntegralType).isSigned() and
131131
not essentialType instanceof PlainCharType
132132
or
133+
// Anonymous enums are considered to be signed
134+
result = EssentiallySignedType() and
135+
essentialType instanceof AnonymousEnumType and
136+
not essentialType instanceof MisraBoolType
137+
or
133138
result = EssentiallyUnsignedType() and
134139
essentialType.(IntegralType).isUnsigned() and
135140
not essentialType instanceof PlainCharType
136141
or
137142
result = EssentiallyEnumType() and
138-
essentialType instanceof Enum and
143+
essentialType instanceof NamedEnumType and
139144
not essentialType instanceof MisraBoolType
140145
or
141146
result = EssentiallyFloatingType() and
@@ -348,16 +353,51 @@ class EssentialBinaryArithmeticExpr extends EssentialExpr, BinaryArithmeticOpera
348353
}
349354
}
350355

356+
/**
357+
* A named Enum type, as per D.5.
358+
*/
359+
class NamedEnumType extends Enum {
360+
NamedEnumType() {
361+
not isAnonymous()
362+
or
363+
exists(Type useOfEnum | this = useOfEnum.stripType() |
364+
exists(TypedefType t | t.getBaseType() = useOfEnum)
365+
or
366+
exists(Function f | f.getType() = useOfEnum or f.getAParameter().getType() = useOfEnum)
367+
or
368+
exists(Struct s | s.getAField().getType() = useOfEnum)
369+
or
370+
exists(Variable v | v.getType() = useOfEnum)
371+
)
372+
}
373+
}
374+
375+
/**
376+
* An anonymous Enum type, as per D.5.
377+
*/
378+
class AnonymousEnumType extends Enum {
379+
AnonymousEnumType() { not this instanceof NamedEnumType }
380+
}
381+
382+
/**
383+
* The EssentialType of an EnumConstantAccess, which may be essentially enum or essentially signed.
384+
*/
351385
class EssentialEnumConstantAccess extends EssentialExpr, EnumConstantAccess {
352-
override Type getEssentialType() { result = getTarget().getDeclaringEnum() }
386+
override Type getEssentialType() {
387+
exists(Enum e | e = getTarget().getDeclaringEnum() |
388+
if e instanceof NamedEnumType then result = e else result = stlr(this)
389+
)
390+
}
353391
}
354392

355393
class EssentialLiteral extends EssentialExpr, Literal {
356394
override Type getEssentialType() {
357395
if this instanceof BooleanLiteral
358-
then result instanceof MisraBoolType
396+
then
397+
// This returns a multitude of types - not sure if we really want that
398+
result instanceof MisraBoolType
359399
else (
360-
if this.(CharLiteral).getCharacter().length() = 1
400+
if this instanceof CharLiteral
361401
then result instanceof PlainCharType
362402
else
363403
exists(Type underlyingStandardType |

c/misra/src/rules/RULE-10-4/OperandsWithMismatchedEssentialTypeCategory.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ where
3838
// be reported as non-compliant.
3939
leftOpTypeCategory = EssentiallyEnumType() and
4040
rightOpTypeCategory = EssentiallyEnumType() and
41-
not leftOpEssentialType = rightOpEssentialType and
41+
not leftOpEssentialType.getUnspecifiedType() = rightOpEssentialType.getUnspecifiedType() and
4242
message =
4343
"The operands of this operator with usual arithmetic conversions have mismatched essentially Enum types (left operand: "
4444
+ leftOpEssentialType + ", right operand: " + rightOpEssentialType + ")."

c/misra/src/rules/RULE-11-5/ConversionFromPointerToVoidIntoPointerToObject.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import codingstandards.cpp.Pointers
1919
from Cast cast, VoidPointerType type, PointerToObjectType newType
2020
where
2121
not isExcluded(cast, Pointers1Package::conversionFromPointerToVoidIntoPointerToObjectQuery()) and
22-
type = cast.getExpr().getUnderlyingType() and
22+
type = cast.getExpr().getUnspecifiedType() and
2323
newType = cast.getUnderlyingType() and
2424
not isNullPointerConstant(cast.getExpr())
2525
select cast,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* @id c/misra/incorrectly-sized-integer-constant-macro-argument
3+
* @name RULE-7-5: The argument of an integer constant macro shall have an appropriate size
4+
* @description Integer constant macros argument values should be values of a compatible size.
5+
* @kind problem
6+
* @precision very-high
7+
* @problem.severity error
8+
* @tags external/misra/id/rule-7-5
9+
* correctness
10+
* external/misra/c/2012/amendment3
11+
* external/misra/obligation/required
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.misra
16+
import codingstandards.cpp.IntegerConstantMacro
17+
import codingstandards.cpp.Literals
18+
19+
predicate matchesSign(IntegerConstantMacro macro, PossiblyNegativeLiteral literal) {
20+
literal.isNegative() implies macro.isSigned()
21+
}
22+
23+
predicate matchesSize(IntegerConstantMacro macro, PossiblyNegativeLiteral literal) {
24+
literal.getRawValue() <= macro.maxValue() and
25+
literal.getRawValue() >= macro.minValue()
26+
}
27+
28+
from
29+
PossiblyNegativeLiteral literal, MacroInvocation invoke, IntegerConstantMacro macro,
30+
string explanation
31+
where
32+
not isExcluded(invoke, Types2Package::incorrectlySizedIntegerConstantMacroArgumentQuery()) and
33+
invoke.getMacro() = macro and
34+
literal = invoke.getExpr() and
35+
(
36+
not matchesSign(macro, literal) and
37+
explanation = " cannot be negative"
38+
or
39+
matchesSign(macro, literal) and
40+
// Wait for BigInt support to check 64 bit macro types.
41+
macro.getSize() < 64 and
42+
not matchesSize(macro, literal) and
43+
explanation = " is outside of the allowed range " + macro.getRangeString()
44+
)
45+
select literal, "Value provided to integer constant macro " + macro.getName() + explanation
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* @id c/misra/integer-constant-macro-argument-uses-suffix
3+
* @name RULE-7-5: The argument of an integer constant macro shall not use literal suffixes u, l, or ul
4+
* @description Integer constant macros should be used integer literal values with no u/l suffix.
5+
* @kind problem
6+
* @precision high
7+
* @problem.severity warning
8+
* @tags external/misra/id/rule-7-5
9+
* readability
10+
* maintainability
11+
* external/misra/c/2012/amendment3
12+
* external/misra/obligation/required
13+
*/
14+
15+
import cpp
16+
import codingstandards.c.misra
17+
import codingstandards.cpp.IntegerConstantMacro
18+
import codingstandards.cpp.Literals
19+
20+
string argumentSuffix(MacroInvocation invoke) {
21+
// Extractor strips the suffix unless we look at the unexpanded argument text.
22+
// Unexpanded argument text can be malformed in all sorts of ways, so make
23+
// this match relatively strict, to be safe.
24+
result = invoke.getUnexpandedArgument(0).regexpCapture("([0-9]+|0[xX][0-9A-F]+)([uUlL]+)$", 2)
25+
}
26+
27+
from MacroInvocation invoke, PossiblyNegativeLiteral argument, string suffix
28+
where
29+
not isExcluded(invoke, Types2Package::integerConstantMacroArgumentUsesSuffixQuery()) and
30+
invoke.getMacro() instanceof IntegerConstantMacro and
31+
invoke.getExpr() = argument and
32+
suffix = argumentSuffix(invoke)
33+
select argument,
34+
"Value suffix '" + suffix + "' is not allowed on provided argument to integer constant macro " +
35+
invoke.getMacroName() + "."
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* @id c/misra/invalid-integer-constant-macro-argument
3+
* @name RULE-7-5: The argument of an integer constant macro shall be a literal
4+
* @description Integer constant macros should be given a literal value as an argument.
5+
* @kind problem
6+
* @precision very-high
7+
* @problem.severity warning
8+
* @tags external/misra/id/rule-7-5
9+
* correctness
10+
* external/misra/c/2012/amendment3
11+
* external/misra/obligation/required
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.misra
16+
import codingstandards.cpp.IntegerConstantMacro
17+
import codingstandards.cpp.Literals
18+
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
19+
20+
from MacroInvocation invoke, IntegerConstantMacro macro
21+
where
22+
not isExcluded(invoke, Types2Package::invalidIntegerConstantMacroArgumentQuery()) and
23+
invoke.getMacro() = macro and
24+
(
25+
not invoke.getExpr() instanceof PossiblyNegativeLiteral
26+
or
27+
any(MacroInvocation inner).getParentInvocation() = invoke
28+
)
29+
select invoke.getExpr(),
30+
"Argument to integer constant macro " + macro.getName() + " must be an integer literal."
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* @id c/misra/invalid-literal-for-integer-constant-macro-argument
3+
* @name RULE-7-5: The argument of an integer constant macro shall be a decimal, hex, or octal literal
4+
* @description Integer constant macro arguments should be a decimal, hex, or octal literal.
5+
* @kind problem
6+
* @precision very-high
7+
* @problem.severity error
8+
* @tags external/misra/id/rule-7-5
9+
* correctness
10+
* external/misra/c/2012/amendment3
11+
* external/misra/obligation/required
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.misra
16+
import codingstandards.cpp.IntegerConstantMacro
17+
import codingstandards.cpp.Literals
18+
19+
/**
20+
* Floating point literals are not allowed. Neither are char or string
21+
* literals, although those are not `NumericLiteral`s and therefore detected in
22+
* `InvalidIntegerConstantMacroArgument.ql`.
23+
*/
24+
predicate validLiteralType(PossiblyNegativeLiteral literal) {
25+
literal.getBaseLiteral() instanceof Cpp14Literal::DecimalLiteral or
26+
literal.getBaseLiteral() instanceof Cpp14Literal::OctalLiteral or
27+
literal.getBaseLiteral() instanceof Cpp14Literal::HexLiteral or
28+
// Ignore cases where the AST/extractor don't give us enough information:
29+
literal.getBaseLiteral() instanceof Cpp14Literal::UnrecognizedNumericLiteral
30+
}
31+
32+
/**
33+
* Clang accepts `xINTsize_C(0b01)`, and expands the argument into a decimal
34+
* literal. Binary literals are not standard c nor are they allowed by rule 7-5.
35+
* Detect this pattern before macro expansion.
36+
*/
37+
predicate seemsBinaryLiteral(MacroInvocation invoke) {
38+
invoke.getUnexpandedArgument(0).regexpMatch("-?0[bB][01]+")
39+
}
40+
41+
/**
42+
* Extractor converts `xINTsize_C('a')` to a decimal literal. Therefore, detect
43+
* this pattern before macro expansion.
44+
*/
45+
predicate seemsCharLiteral(MacroInvocation invoke) {
46+
invoke.getUnexpandedArgument(0).regexpMatch("-?'\\\\?.'")
47+
}
48+
49+
string explainIncorrectArgument(MacroInvocation invoke) {
50+
if seemsBinaryLiteral(invoke)
51+
then result = "binary literal"
52+
else
53+
if seemsCharLiteral(invoke)
54+
then result = "char literal"
55+
else
56+
exists(PossiblyNegativeLiteral literal |
57+
literal = invoke.getExpr() and
58+
if literal.getBaseLiteral() instanceof Cpp14Literal::FloatingLiteral
59+
then result = "floating point literal"
60+
else result = "invalid literal"
61+
)
62+
}
63+
64+
from MacroInvocation invoke, PossiblyNegativeLiteral literal
65+
where
66+
not isExcluded(invoke, Types2Package::invalidLiteralForIntegerConstantMacroArgumentQuery()) and
67+
invoke.getMacro() instanceof IntegerConstantMacro and
68+
literal = invoke.getExpr() and
69+
(
70+
not validLiteralType(literal) or
71+
seemsBinaryLiteral(invoke) or
72+
seemsCharLiteral(invoke)
73+
)
74+
select literal,
75+
"Integer constant macro " + invoke.getMacroName() + " used with " +
76+
explainIncorrectArgument(invoke) +
77+
" argument, only decimal, octal, or hex integer literal allowed."
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @id c/misra/use-of-banned-small-integer-constant-macro
3+
* @name RULE-7-6: The small integer variants of the minimum-width integer constant macros shall not be used
4+
* @description Small integer constant macros expression are promoted to type int, which can lead to
5+
* unexpected results.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity warning
9+
* @tags external/misra/id/rule-7-6
10+
* readability
11+
* external/misra/c/2012/amendment3
12+
* external/misra/obligation/required
13+
*/
14+
15+
import cpp
16+
import codingstandards.c.misra
17+
import codingstandards.cpp.IntegerConstantMacro
18+
19+
from MacroInvocation macroInvoke, IntegerConstantMacro macro
20+
where
21+
not isExcluded(macroInvoke, Types2Package::useOfBannedSmallIntegerConstantMacroQuery()) and
22+
macroInvoke.getMacro() = macro and
23+
macro.isSmall()
24+
select macroInvoke, "Usage of small integer constant macro " + macro.getName() + " is not allowed."

c/misra/test/c/misra/EssentialTypes.expected

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type |
2+
| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type |
3+
| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type |
4+
| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type |
5+
| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type |
6+
| file://:0:0:0:0 | 0 | signed char | signed char | essentially Signed type |
17
| test.c:4:20:4:20 | 1 | signed char | signed char | essentially Signed type |
28
| test.c:4:20:4:20 | (unsigned int)... | unsigned int | unsigned int | essentially Unsigned type |
39
| test.c:5:23:5:23 | 1 | signed char | signed char | essentially Signed type |
@@ -73,3 +79,14 @@
7379
| test.c:54:3:54:5 | ~ ... | int | int | essentially Signed type |
7480
| test.c:54:4:54:5 | (int)... | int | int | essentially Signed type |
7581
| test.c:54:4:54:5 | ss | signed short | signed short | essentially Signed type |
82+
| test.c:63:30:63:32 | ((unnamed enum))... | (unnamed enum) | (unnamed enum) | essentially Enum Type |
83+
| test.c:63:30:63:32 | EC5 | (unnamed enum) | (unnamed enum) | essentially Enum Type |
84+
| test.c:70:3:70:5 | EC1 | signed char | signed char | essentially Signed type |
85+
| test.c:71:3:71:5 | EC2 | E1 | E1 | essentially Enum Type |
86+
| test.c:72:3:72:5 | EC3 | (unnamed enum) | (unnamed enum) | essentially Enum Type |
87+
| test.c:73:3:73:5 | EC4 | (unnamed enum) | (unnamed enum) | essentially Enum Type |
88+
| test.c:74:3:74:5 | EC5 | (unnamed enum) | (unnamed enum) | essentially Enum Type |
89+
| test.c:75:3:75:5 | EC6 | (unnamed enum) | (unnamed enum) | essentially Enum Type |
90+
| test.c:79:3:79:5 | 97 | char | char | essentially Character type |
91+
| test.c:80:3:80:6 | 10 | char | char | essentially Character type |
92+
| test.c:81:3:81:6 | 0 | char | char | essentially Character type |

c/misra/test/c/misra/test.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,31 @@ void testUnary() {
5252
~us; // Should be essentially unsigned
5353
~s; // Should be essentially signed
5454
~ss; // Should be essentially signed
55+
}
56+
57+
enum { EC1 };
58+
enum E1 { EC2 };
59+
typedef enum { EC3 } E2;
60+
61+
enum { EC4 } g;
62+
63+
enum { EC5 } test() { return EC5; }
64+
65+
struct S1 {
66+
enum { EC6 } m;
67+
};
68+
69+
void testEnums() {
70+
EC1; // Should be essentially signed
71+
EC2; // Should be essentially enum
72+
EC3; // Should be essentially enum
73+
EC4; // Should be essentially enum
74+
EC5; // Should be essentially enum
75+
EC6; // Should be essentially enum
76+
}
77+
78+
void testControlChar() {
79+
'a'; // Essentially char
80+
'\n'; // Essentially char
81+
'\0'; // Essentially char
5582
}

c/misra/test/rules/RULE-10-4/test.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,13 @@ void testOps() {
3333
A < A; // COMPLIANT
3434
e1a < e2a; // NON_COMPLIANT
3535
A < D; // NON_COMPLIANT
36+
37+
enum { G };
38+
s32 + G; // COMPLIANT
39+
c == '\n'; // COMPLIANT
40+
41+
typedef enum { H } E3;
42+
43+
E3 e3a = H;
44+
e3a < H; // COMPLIANT
3645
}

c/misra/test/rules/RULE-11-3/CastBetweenObjectPointerAndDifferentObjectType.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22
| test.c:14:8:14:9 | (int *)... | Cast performed between a pointer to object type (char) and a pointer to a different object type (int). |
33
| test.c:15:8:15:25 | (int *)... | Cast performed between a pointer to object type (short) and a pointer to a different object type (int). |
44
| test.c:15:15:15:25 | (short *)... | Cast performed between a pointer to object type (char) and a pointer to a different object type (short). |
5+
| test.c:20:3:20:17 | (const int *)... | Cast performed between a pointer to object type (char) and a pointer to a different object type (const int). |
6+
| test.c:21:3:21:16 | (int *)... | Cast performed between a pointer to object type (char) and a pointer to a different object type (int). |
7+
| test.c:22:20:22:21 | (int *)... | Cast performed between a pointer to object type (char) and a pointer to a different object type (int). |
8+
| test.c:23:3:23:18 | (long long *)... | Cast performed between a pointer to object type (int) and a pointer to a different object type (long long). |

0 commit comments

Comments
 (0)