Skip to content

Commit 3e35c56

Browse files
authored
Merge pull request #760 from github/michaelrfairhurst/implement-package-alignment
Implement alignment package for MISRA C 2012 amdmt 3
2 parents 03be7b8 + a490400 commit 3e35c56

20 files changed

+488
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @id c/misra/redeclaration-of-object-with-unmatched-alignment
3+
* @name RULE-8-15: Alignment should match between all declarations of an object
4+
* @description All declarations of an object with an explicit alignment specification shall specify
5+
* the same alignment.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity error
9+
* @tags external/misra/id/rule-8-15
10+
* external/misra/c/2012/amendment3
11+
* readability
12+
* maintainability
13+
* external/misra/obligation/required
14+
*/
15+
16+
import cpp
17+
import codingstandards.c.misra
18+
import semmle.code.cpp.valuenumbering.HashCons
19+
20+
predicate lexicallyEqual(AttributeArgument a, AttributeArgument b) {
21+
hashCons(a.getValueConstant()) = hashCons(b.getValueConstant()) or
22+
a.getValueType() = b.getValueType()
23+
}
24+
25+
from Attribute alignment, Attribute mismatched, string variable
26+
where
27+
not isExcluded(alignment, AlignmentPackage::redeclarationOfObjectWithUnmatchedAlignmentQuery()) and
28+
alignment.hasName("_Alignas") and
29+
mismatched.hasName("_Alignas") and
30+
exists(Variable v |
31+
v.getAnAttribute() = alignment and v.getAnAttribute() = mismatched and v.getName() = variable
32+
) and
33+
not lexicallyEqual(alignment.getArgument(0), mismatched.getArgument(0))
34+
select alignment,
35+
"Variable " + variable + " declared with lexically different _Alignof() values '$@' and '$@'.",
36+
alignment, alignment.getArgument(0).toString(), mismatched, mismatched.getArgument(0).toString()
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* @id c/misra/redeclaration-of-object-without-alignment
3+
* @name RULE-8-15: Alignment should match between all declarations of an object
4+
* @description An object declared with an explicit alignment shall be explicitly aligned in all
5+
* declarations.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity error
9+
* @tags external/misra/id/rule-8-15
10+
* external/misra/c/2012/amendment3
11+
* readability
12+
* maintainability
13+
* external/misra/obligation/required
14+
*/
15+
16+
import cpp
17+
import codingstandards.c.misra
18+
19+
/**
20+
* Performance optimization; start query by joining attributes to declarations
21+
* rather than locations.
22+
*
23+
* Including the entry location also speeds up search.
24+
*/
25+
newtype TAttributeDeclLocation =
26+
TAttributeDeclLocationInfo(Attribute attribute, DeclarationEntry entry, Location entryLocation) {
27+
entry.getDeclaration().(Variable).getAnAttribute() = attribute and
28+
entryLocation = entry.getLocation()
29+
}
30+
31+
/**
32+
* Get a DeclarationEntry along with its explicitly declared Attributes.
33+
*
34+
* DeclarationEntry does not have a method for getting Attributes by default,
35+
* because an attribute declared on any DeclarationEntry affects all others,
36+
* and attributes really belong to the declared variable rather than the
37+
* declaration itself.
38+
*
39+
* In order to support this rule, we find for each attribute
40+
* - A declaration entry which
41+
* - corresponds to a variable associated with this attribute
42+
* - is in the same file as this attribute
43+
* - has identifier location after the attribute declaration
44+
* - has no other declaration entry between this one and the attribute.
45+
*
46+
* This should give us a highly reliable means of finding which attributes are
47+
* associated with which `DeclarationEntry`s.
48+
*
49+
* One note of caution: the location of the associated `Variable` must be
50+
* treated with caution, as calls to `getLocation()` on a redeclared `Variable`
51+
* can return multiple results. This class must act on `DeclarationEntry`s to
52+
* deliver reliable results.
53+
*/
54+
class DeclarationEntryAttribute extends Attribute {
55+
DeclarationEntry declarationEntry;
56+
Location location;
57+
Location declLocation;
58+
File file;
59+
TAttributeDeclLocation locInfo;
60+
61+
DeclarationEntryAttribute() {
62+
locInfo = TAttributeDeclLocationInfo(this, declarationEntry, declLocation) and
63+
file = getFile() and
64+
location = getLocation() and
65+
declLocation = declarationEntry.getLocation() and
66+
declarationEntry.getDeclaration().(Variable).getAnAttribute() = this and
67+
declarationEntry.getFile() = file and
68+
location.isBefore(declLocation, _) and
69+
not exists(TAttributeDeclLocation blocInfo, DeclarationEntry betterFit, Location blocation |
70+
blocInfo = TAttributeDeclLocationInfo(this, betterFit, blocation) and
71+
not betterFit = declarationEntry and
72+
blocation = betterFit.getLocation() and
73+
betterFit.getFile() = file and
74+
betterFit.getDeclaration() = declarationEntry.getDeclaration() and
75+
blocation.isBefore(declLocation, _) and
76+
location.isBefore(blocation, _)
77+
)
78+
}
79+
80+
DeclarationEntry getDeclarationEntry() { result = declarationEntry }
81+
}
82+
83+
from DeclarationEntry unaligned, DeclarationEntry aligned, DeclarationEntryAttribute attribute
84+
where
85+
not isExcluded(unaligned, AlignmentPackage::redeclarationOfObjectWithoutAlignmentQuery()) and
86+
attribute.hasName("_Alignas") and
87+
attribute.getDeclarationEntry() = aligned and
88+
aligned.getDeclaration() = unaligned.getDeclaration() and
89+
not exists(DeclarationEntryAttribute matchingAlignment |
90+
matchingAlignment.hasName("_Alignas") and
91+
matchingAlignment.getDeclarationEntry() = unaligned
92+
)
93+
select unaligned,
94+
"Variable " + unaligned.getName() +
95+
" declared without explicit alignment to match $@ with alignment $@.", aligned,
96+
"other definition", attribute, attribute.toString()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @id c/misra/alignment-with-size-zero
3+
* @name RULE-8-16: The alignment specification of zero should not appear in an object declaration
4+
* @description A declaration shall not have an alignment of size zero.
5+
* @kind problem
6+
* @precision very-high
7+
* @problem.severity error
8+
* @tags external/misra/id/rule-8-16
9+
* external/misra/c/2012/amendment3
10+
* readability
11+
* maintainability
12+
* external/misra/obligation/advisory
13+
*/
14+
15+
import cpp
16+
import codingstandards.c.misra
17+
18+
from Attribute a, Variable v
19+
where
20+
not isExcluded(a, AlignmentPackage::alignmentWithSizeZeroQuery()) and
21+
a.hasName("_Alignas") and
22+
a.getArgument(0).getValueInt() = 0 and
23+
v.getAnAttribute() = a
24+
select a.getArgument(0), "Invalid alignof() size set to zero for variable $@.", v, v.getName()
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @id c/misra/more-than-one-alignment-specifier-on-declaration
3+
* @name RULE-8-17: At most one explicit alignment specifier should appear in an object declaration
4+
* @description While C permits the usage of multiple alignment specifiers, doing so reduces
5+
* readability and may obscure the intent of the declaration.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity error
9+
* @tags external/misra/id/rule-8-17
10+
* external/misra/c/2012/amendment3
11+
* readability
12+
* external/misra/obligation/advisory
13+
*/
14+
15+
import cpp
16+
import codingstandards.c.misra
17+
18+
from Variable v, Attribute first, Attribute last
19+
where
20+
not isExcluded(v, AlignmentPackage::moreThanOneAlignmentSpecifierOnDeclarationQuery()) and
21+
first = v.getAnAttribute() and
22+
last = v.getAnAttribute() and
23+
not first = last and
24+
first.hasName("_Alignas") and
25+
last.hasName("_Alignas") and
26+
// Handle double reporting: the first Attribute should really be first, and the last Attribute
27+
// should really be last. This implies the first is before the last. This approach also ensures
28+
// a single result for variables that have more than two alignment specifiers.
29+
not exists(Attribute beforeFirst |
30+
beforeFirst.getLocation().isBefore(first.getLocation(), _) and
31+
v.getAnAttribute() = beforeFirst
32+
) and
33+
not exists(Attribute afterLast |
34+
last.getLocation().isBefore(afterLast.getLocation(), _) and
35+
v.getAnAttribute() = afterLast
36+
)
37+
select v, "Variable " + v.getName() + " contains more than one alignment specifier, $@ and $@",
38+
first, first.toString(), last, last.toString()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
| test.c:18:8:18:15 | alignas(...) | Variable g6 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:18:8:18:15 | alignas(...) | int | test.c:19:8:19:15 | alignas(...) | 4 |
2+
| test.c:19:8:19:15 | alignas(...) | Variable g6 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:19:8:19:15 | alignas(...) | 4 | test.c:18:8:18:15 | alignas(...) | int |
3+
| test.c:22:8:22:15 | alignas(...) | Variable g7 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:22:8:22:15 | alignas(...) | ... * ... | test.c:23:8:23:15 | alignas(...) | 32 |
4+
| test.c:23:8:23:15 | alignas(...) | Variable g7 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:23:8:23:15 | alignas(...) | 32 | test.c:22:8:22:15 | alignas(...) | ... * ... |
5+
| test.c:28:8:28:15 | alignas(...) | Variable g9 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:28:8:28:15 | alignas(...) | ... * ... | test.c:29:8:29:15 | alignas(...) | ... * ... |
6+
| test.c:29:8:29:15 | alignas(...) | Variable g9 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:29:8:29:15 | alignas(...) | ... * ... | test.c:28:8:28:15 | alignas(...) | ... * ... |
7+
| test.c:34:8:34:15 | alignas(...) | Variable g11 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:34:8:34:15 | alignas(...) | signed int | test.c:35:8:35:15 | alignas(...) | unsigned int |
8+
| test.c:35:8:35:15 | alignas(...) | Variable g11 declared with lexically different _Alignof() values '$@' and '$@'. | test.c:35:8:35:15 | alignas(...) | unsigned int | test.c:34:8:34:15 | alignas(...) | signed int |
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
| test.c:11:8:11:15 | alignas(...) | Variable g4 declared with lexically different _Alignof() values '$@' and '$@' | test.c:11:8:11:15 | alignas(...) | 16 | test.c:12:8:12:15 | alignas(...) | 32 |
2+
| test.c:12:8:12:15 | alignas(...) | Variable g4 declared with lexically different _Alignof() values '$@' and '$@' | test.c:12:8:12:15 | alignas(...) | 32 | test.c:11:8:11:15 | alignas(...) | 16 |
3+
| test.c:18:8:18:15 | alignas(...) | Variable g6 declared with lexically different _Alignof() values '$@' and '$@' | test.c:18:8:18:15 | alignas(...) | int | test.c:19:8:19:15 | alignas(...) | 4 |
4+
| test.c:19:8:19:15 | alignas(...) | Variable g6 declared with lexically different _Alignof() values '$@' and '$@' | test.c:19:8:19:15 | alignas(...) | 4 | test.c:18:8:18:15 | alignas(...) | int |
5+
| test.c:22:8:22:15 | alignas(...) | Variable g7 declared with lexically different _Alignof() values '$@' and '$@' | test.c:22:8:22:15 | alignas(...) | ... * ... | test.c:23:8:23:15 | alignas(...) | 32 |
6+
| test.c:23:8:23:15 | alignas(...) | Variable g7 declared with lexically different _Alignof() values '$@' and '$@' | test.c:23:8:23:15 | alignas(...) | 32 | test.c:22:8:22:15 | alignas(...) | ... * ... |
7+
| test.c:28:8:28:15 | alignas(...) | Variable g9 declared with lexically different _Alignof() values '$@' and '$@' | test.c:28:8:28:15 | alignas(...) | ... * ... | test.c:29:8:29:15 | alignas(...) | ... * ... |
8+
| test.c:29:8:29:15 | alignas(...) | Variable g9 declared with lexically different _Alignof() values '$@' and '$@' | test.c:29:8:29:15 | alignas(...) | ... * ... | test.c:28:8:28:15 | alignas(...) | ... * ... |
9+
| test.c:34:8:34:15 | alignas(...) | Variable g11 declared with lexically different _Alignof() values '$@' and '$@' | test.c:34:8:34:15 | alignas(...) | signed int | test.c:35:8:35:15 | alignas(...) | unsigned int |
10+
| test.c:35:8:35:15 | alignas(...) | Variable g11 declared with lexically different _Alignof() values '$@' and '$@' | test.c:35:8:35:15 | alignas(...) | unsigned int | test.c:34:8:34:15 | alignas(...) | signed int |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.ql
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| test.c:5:12:5:13 | declaration of g2 | Variable g2 declared without explicit alignment to match $@ with alignment $@. | test.c:4:25:4:26 | declaration of g2 | other definition | test.c:4:8:4:15 | alignas(...) | alignas(...) |
2+
| test.c:7:12:7:13 | declaration of g3 | Variable g3 declared without explicit alignment to match $@ with alignment $@. | test.c:8:25:8:26 | declaration of g3 | other definition | test.c:8:8:8:15 | alignas(...) | alignas(...) |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.ql

c/misra/test/rules/RULE-8-15/test.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
extern _Alignas(16) int g1; // COMPLIANT
2+
extern _Alignas(16) int g1; // COMPLIANT
3+
4+
extern _Alignas(16) int g2;
5+
extern int g2; // NON_COMPLIANT
6+
7+
extern int g3; // NON_COMPLIANT
8+
extern _Alignas(16) int g3;
9+
10+
// Does not compile on clang:
11+
// extern _Alignas(16) int g4; // COMPLIANT
12+
// extern _Alignas(32) int g4; // COMPLIANT
13+
14+
extern int g5; // COMPLIANT
15+
extern int g5; // COMPLIANT
16+
17+
// Spec says elements must be lexically identical after macro expansion
18+
extern _Alignas(int) int g6; // NON_COMPLIANT
19+
extern _Alignas(4) int g6; // NON_COMPLIANT
20+
21+
#define THIRTY_TWO 32
22+
extern _Alignas(16 * 2) int g7; // NON_COMPLIANT
23+
extern _Alignas(32) int g7; // NON_COMPLIANT
24+
25+
extern _Alignas(THIRTY_TWO) int g8; // COMPLIANT
26+
extern _Alignas(32) int g8; // COMPLIANT
27+
28+
extern _Alignas(16 * 2) int g9; // NON_COMPLIANT
29+
extern _Alignas(2 * 16) int g9; // NON_COMPLIANT
30+
31+
extern _Alignas(int) int g10; // COMPLIANT
32+
extern _Alignas(int) int g10; // COMPLIANT
33+
34+
extern _Alignas(signed int) int g11; // NON_COMPLIANT
35+
extern _Alignas(unsigned int) int g11; // NON_COMPLIANT
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
extern _Alignas(16) int g1; // COMPLIANT
2+
extern _Alignas(16) int g1; // COMPLIANT
3+
4+
extern _Alignas(16) int g2;
5+
extern int g2; // NON_COMPLIANT
6+
7+
extern int g3; // NON_COMPLIANT
8+
extern _Alignas(16) int g3;
9+
10+
// Does not compile on clang:
11+
extern _Alignas(16) int g4; // COMPLIANT
12+
extern _Alignas(32) int g4; // COMPLIANT
13+
14+
extern int g5; // COMPLIANT
15+
extern int g5; // COMPLIANT
16+
17+
// Spec says elements must be lexically identical after macro expansion
18+
extern _Alignas(int) int g6; // NON_COMPLIANT
19+
extern _Alignas(4) int g6; // NON_COMPLIANT
20+
21+
#define THIRTY_TWO 32
22+
extern _Alignas(16 * 2) int g7; // NON_COMPLIANT
23+
extern _Alignas(32) int g7; // NON_COMPLIANT
24+
25+
extern _Alignas(THIRTY_TWO) int g8; // COMPLIANT
26+
extern _Alignas(32) int g8; // COMPLIANT
27+
28+
extern _Alignas(16 * 2) int g9; // NON_COMPLIANT
29+
extern _Alignas(2 * 16) int g9; // NON_COMPLIANT
30+
31+
extern _Alignas(int) int g10; // COMPLIANT
32+
extern _Alignas(int) int g10; // COMPLIANT
33+
34+
extern _Alignas(signed int) int g11; // NON_COMPLIANT
35+
extern _Alignas(unsigned int) int g11; // NON_COMPLIANT
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| test.c:2:10:2:10 | 0 | Invalid alignof() size set to zero for variable $@. | test.c:2:17:2:18 | g2 | g2 |
2+
| test.c:3:10:3:14 | ... - ... | Invalid alignof() size set to zero for variable $@. | test.c:3:21:3:22 | g3 | g3 |
3+
| test.c:8:12:8:12 | 0 | Invalid alignof() size set to zero for variable $@. | test.c:8:19:8:20 | m2 | m2 |
4+
| test.c:13:12:13:12 | 0 | Invalid alignof() size set to zero for variable $@. | test.c:13:19:13:20 | l2 | l2 |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-8-16/AlignmentWithSizeZero.ql

c/misra/test/rules/RULE-8-16/test.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
_Alignas(8) int g1; // COMPLIANT
2+
_Alignas(0) int g2; // NON-COMPLIANT
3+
_Alignas(8 - 8) int g3; // NON-COMPLIANT
4+
_Alignas(float) int g4; // COMPLIANT
5+
6+
struct s {
7+
_Alignas(64) int m1; // COMPLIANT
8+
_Alignas(0) int m2; // NON_COMPLIANT
9+
};
10+
11+
void f() {
12+
_Alignas(8) int l1; // COMPLIANT
13+
_Alignas(0) int l2; // NON-COMPLIANT
14+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
| test.c:2:30:2:31 | g2 | Variable g2 contains more than one alignment specifier, $@ and $@ | test.c:2:1:2:8 | alignas(...) | alignas(...) | test.c:2:13:2:20 | alignas(...) | alignas(...) |
2+
| test.c:3:29:3:30 | g3 | Variable g3 contains more than one alignment specifier, $@ and $@ | test.c:3:1:3:8 | alignas(...) | alignas(...) | test.c:3:13:3:20 | alignas(...) | alignas(...) |
3+
| test.c:4:35:4:36 | g4 | Variable g4 contains more than one alignment specifier, $@ and $@ | test.c:4:1:4:8 | alignas(...) | alignas(...) | test.c:4:17:4:24 | alignas(...) | alignas(...) |
4+
| test.c:6:53:6:54 | g5 | Variable g5 contains more than one alignment specifier, $@ and $@ | test.c:5:1:5:8 | alignas(...) | alignas(...) | test.c:6:33:6:40 | alignas(...) | alignas(...) |
5+
| test.c:10:35:10:36 | m2 | Variable m2 contains more than one alignment specifier, $@ and $@ | test.c:10:3:10:10 | alignas(...) | alignas(...) | test.c:10:18:10:25 | alignas(...) | alignas(...) |
6+
| test.c:15:35:15:36 | l2 | Variable l2 contains more than one alignment specifier, $@ and $@ | test.c:15:3:15:10 | alignas(...) | alignas(...) | test.c:15:18:15:25 | alignas(...) | alignas(...) |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.ql

c/misra/test/rules/RULE-8-17/test.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
_Alignas(8) int g1; // COMPLIANT
2+
_Alignas(8) _Alignas(16) int g2; // NON-COMPLIANT
3+
_Alignas(8) _Alignas(8) int g3; // NON-COMPLIANT
4+
_Alignas(float) _Alignas(int) int g4; // NON-COMPLIANT
5+
_Alignas(float) _Alignas(float) int g5; // NON-COMPLIANT
6+
_Alignas(float) _Alignas(float) _Alignas(float) int g5; // NON-COMPLIANT
7+
8+
struct s {
9+
_Alignas(64) int m1; // COMPLIANT
10+
_Alignas(long) _Alignas(16) int m2; // NON_COMPLIANT
11+
};
12+
13+
void f() {
14+
_Alignas(8) int l1; // COMPLIANT
15+
_Alignas(long) _Alignas(16) int l2; // NON_COMPLIANT
16+
}

0 commit comments

Comments
 (0)