Skip to content

Commit

Permalink
Merge pull request #760 from github/michaelrfairhurst/implement-packa…
Browse files Browse the repository at this point in the history
…ge-alignment

Implement alignment package for MISRA C 2012 amdmt 3
  • Loading branch information
lcartey authored Nov 25, 2024
2 parents 03be7b8 + a490400 commit 3e35c56
Show file tree
Hide file tree
Showing 20 changed files with 488 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* @id c/misra/redeclaration-of-object-with-unmatched-alignment
* @name RULE-8-15: Alignment should match between all declarations of an object
* @description All declarations of an object with an explicit alignment specification shall specify
* the same alignment.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/misra/id/rule-8-15
* external/misra/c/2012/amendment3
* readability
* maintainability
* external/misra/obligation/required
*/

import cpp
import codingstandards.c.misra
import semmle.code.cpp.valuenumbering.HashCons

predicate lexicallyEqual(AttributeArgument a, AttributeArgument b) {
hashCons(a.getValueConstant()) = hashCons(b.getValueConstant()) or
a.getValueType() = b.getValueType()
}

from Attribute alignment, Attribute mismatched, string variable
where
not isExcluded(alignment, AlignmentPackage::redeclarationOfObjectWithUnmatchedAlignmentQuery()) and
alignment.hasName("_Alignas") and
mismatched.hasName("_Alignas") and
exists(Variable v |
v.getAnAttribute() = alignment and v.getAnAttribute() = mismatched and v.getName() = variable
) and
not lexicallyEqual(alignment.getArgument(0), mismatched.getArgument(0))
select alignment,
"Variable " + variable + " declared with lexically different _Alignof() values '$@' and '$@'.",
alignment, alignment.getArgument(0).toString(), mismatched, mismatched.getArgument(0).toString()
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* @id c/misra/redeclaration-of-object-without-alignment
* @name RULE-8-15: Alignment should match between all declarations of an object
* @description An object declared with an explicit alignment shall be explicitly aligned in all
* declarations.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/misra/id/rule-8-15
* external/misra/c/2012/amendment3
* readability
* maintainability
* external/misra/obligation/required
*/

import cpp
import codingstandards.c.misra

/**
* Performance optimization; start query by joining attributes to declarations
* rather than locations.
*
* Including the entry location also speeds up search.
*/
newtype TAttributeDeclLocation =
TAttributeDeclLocationInfo(Attribute attribute, DeclarationEntry entry, Location entryLocation) {
entry.getDeclaration().(Variable).getAnAttribute() = attribute and
entryLocation = entry.getLocation()
}

/**
* Get a DeclarationEntry along with its explicitly declared Attributes.
*
* DeclarationEntry does not have a method for getting Attributes by default,
* because an attribute declared on any DeclarationEntry affects all others,
* and attributes really belong to the declared variable rather than the
* declaration itself.
*
* In order to support this rule, we find for each attribute
* - A declaration entry which
* - corresponds to a variable associated with this attribute
* - is in the same file as this attribute
* - has identifier location after the attribute declaration
* - has no other declaration entry between this one and the attribute.
*
* This should give us a highly reliable means of finding which attributes are
* associated with which `DeclarationEntry`s.
*
* One note of caution: the location of the associated `Variable` must be
* treated with caution, as calls to `getLocation()` on a redeclared `Variable`
* can return multiple results. This class must act on `DeclarationEntry`s to
* deliver reliable results.
*/
class DeclarationEntryAttribute extends Attribute {
DeclarationEntry declarationEntry;
Location location;
Location declLocation;
File file;
TAttributeDeclLocation locInfo;

DeclarationEntryAttribute() {
locInfo = TAttributeDeclLocationInfo(this, declarationEntry, declLocation) and
file = getFile() and
location = getLocation() and
declLocation = declarationEntry.getLocation() and
declarationEntry.getDeclaration().(Variable).getAnAttribute() = this and
declarationEntry.getFile() = file and
location.isBefore(declLocation, _) and
not exists(TAttributeDeclLocation blocInfo, DeclarationEntry betterFit, Location blocation |
blocInfo = TAttributeDeclLocationInfo(this, betterFit, blocation) and
not betterFit = declarationEntry and
blocation = betterFit.getLocation() and
betterFit.getFile() = file and
betterFit.getDeclaration() = declarationEntry.getDeclaration() and
blocation.isBefore(declLocation, _) and
location.isBefore(blocation, _)
)
}

DeclarationEntry getDeclarationEntry() { result = declarationEntry }
}

from DeclarationEntry unaligned, DeclarationEntry aligned, DeclarationEntryAttribute attribute
where
not isExcluded(unaligned, AlignmentPackage::redeclarationOfObjectWithoutAlignmentQuery()) and
attribute.hasName("_Alignas") and
attribute.getDeclarationEntry() = aligned and
aligned.getDeclaration() = unaligned.getDeclaration() and
not exists(DeclarationEntryAttribute matchingAlignment |
matchingAlignment.hasName("_Alignas") and
matchingAlignment.getDeclarationEntry() = unaligned
)
select unaligned,
"Variable " + unaligned.getName() +
" declared without explicit alignment to match $@ with alignment $@.", aligned,
"other definition", attribute, attribute.toString()
24 changes: 24 additions & 0 deletions c/misra/src/rules/RULE-8-16/AlignmentWithSizeZero.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @id c/misra/alignment-with-size-zero
* @name RULE-8-16: The alignment specification of zero should not appear in an object declaration
* @description A declaration shall not have an alignment of size zero.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/misra/id/rule-8-16
* external/misra/c/2012/amendment3
* readability
* maintainability
* external/misra/obligation/advisory
*/

import cpp
import codingstandards.c.misra

from Attribute a, Variable v
where
not isExcluded(a, AlignmentPackage::alignmentWithSizeZeroQuery()) and
a.hasName("_Alignas") and
a.getArgument(0).getValueInt() = 0 and
v.getAnAttribute() = a
select a.getArgument(0), "Invalid alignof() size set to zero for variable $@.", v, v.getName()
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @id c/misra/more-than-one-alignment-specifier-on-declaration
* @name RULE-8-17: At most one explicit alignment specifier should appear in an object declaration
* @description While C permits the usage of multiple alignment specifiers, doing so reduces
* readability and may obscure the intent of the declaration.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/misra/id/rule-8-17
* external/misra/c/2012/amendment3
* readability
* external/misra/obligation/advisory
*/

import cpp
import codingstandards.c.misra

from Variable v, Attribute first, Attribute last
where
not isExcluded(v, AlignmentPackage::moreThanOneAlignmentSpecifierOnDeclarationQuery()) and
first = v.getAnAttribute() and
last = v.getAnAttribute() and
not first = last and
first.hasName("_Alignas") and
last.hasName("_Alignas") and
// Handle double reporting: the first Attribute should really be first, and the last Attribute
// should really be last. This implies the first is before the last. This approach also ensures
// a single result for variables that have more than two alignment specifiers.
not exists(Attribute beforeFirst |
beforeFirst.getLocation().isBefore(first.getLocation(), _) and
v.getAnAttribute() = beforeFirst
) and
not exists(Attribute afterLast |
last.getLocation().isBefore(afterLast.getLocation(), _) and
v.getAnAttribute() = afterLast
)
select v, "Variable " + v.getName() + " contains more than one alignment specifier, $@ and $@",
first, first.toString(), last, last.toString()
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
| 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 |
| 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 |
| 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 |
| 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(...) | ... * ... |
| 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(...) | ... * ... |
| 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(...) | ... * ... |
| 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 |
| 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 |
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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(...) | ... * ... |
| 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(...) | ... * ... |
| 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(...) | ... * ... |
| 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 |
| 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 |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-8-15/RedeclarationOfObjectWithUnmatchedAlignment.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
| 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(...) |
| 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(...) |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-8-15/RedeclarationOfObjectWithoutAlignment.ql
35 changes: 35 additions & 0 deletions c/misra/test/rules/RULE-8-15/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
extern _Alignas(16) int g1; // COMPLIANT
extern _Alignas(16) int g1; // COMPLIANT

extern _Alignas(16) int g2;
extern int g2; // NON_COMPLIANT

extern int g3; // NON_COMPLIANT
extern _Alignas(16) int g3;

// Does not compile on clang:
// extern _Alignas(16) int g4; // COMPLIANT
// extern _Alignas(32) int g4; // COMPLIANT

extern int g5; // COMPLIANT
extern int g5; // COMPLIANT

// Spec says elements must be lexically identical after macro expansion
extern _Alignas(int) int g6; // NON_COMPLIANT
extern _Alignas(4) int g6; // NON_COMPLIANT

#define THIRTY_TWO 32
extern _Alignas(16 * 2) int g7; // NON_COMPLIANT
extern _Alignas(32) int g7; // NON_COMPLIANT

extern _Alignas(THIRTY_TWO) int g8; // COMPLIANT
extern _Alignas(32) int g8; // COMPLIANT

extern _Alignas(16 * 2) int g9; // NON_COMPLIANT
extern _Alignas(2 * 16) int g9; // NON_COMPLIANT

extern _Alignas(int) int g10; // COMPLIANT
extern _Alignas(int) int g10; // COMPLIANT

extern _Alignas(signed int) int g11; // NON_COMPLIANT
extern _Alignas(unsigned int) int g11; // NON_COMPLIANT
35 changes: 35 additions & 0 deletions c/misra/test/rules/RULE-8-15/test.c.gcc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
extern _Alignas(16) int g1; // COMPLIANT
extern _Alignas(16) int g1; // COMPLIANT

extern _Alignas(16) int g2;
extern int g2; // NON_COMPLIANT

extern int g3; // NON_COMPLIANT
extern _Alignas(16) int g3;

// Does not compile on clang:
extern _Alignas(16) int g4; // COMPLIANT
extern _Alignas(32) int g4; // COMPLIANT

extern int g5; // COMPLIANT
extern int g5; // COMPLIANT

// Spec says elements must be lexically identical after macro expansion
extern _Alignas(int) int g6; // NON_COMPLIANT
extern _Alignas(4) int g6; // NON_COMPLIANT

#define THIRTY_TWO 32
extern _Alignas(16 * 2) int g7; // NON_COMPLIANT
extern _Alignas(32) int g7; // NON_COMPLIANT

extern _Alignas(THIRTY_TWO) int g8; // COMPLIANT
extern _Alignas(32) int g8; // COMPLIANT

extern _Alignas(16 * 2) int g9; // NON_COMPLIANT
extern _Alignas(2 * 16) int g9; // NON_COMPLIANT

extern _Alignas(int) int g10; // COMPLIANT
extern _Alignas(int) int g10; // COMPLIANT

extern _Alignas(signed int) int g11; // NON_COMPLIANT
extern _Alignas(unsigned int) int g11; // NON_COMPLIANT
4 changes: 4 additions & 0 deletions c/misra/test/rules/RULE-8-16/AlignmentWithSizeZero.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
| test.c:2:10:2:10 | 0 | Invalid alignof() size set to zero for variable $@. | test.c:2:17:2:18 | g2 | g2 |
| test.c:3:10:3:14 | ... - ... | Invalid alignof() size set to zero for variable $@. | test.c:3:21:3:22 | g3 | g3 |
| test.c:8:12:8:12 | 0 | Invalid alignof() size set to zero for variable $@. | test.c:8:19:8:20 | m2 | m2 |
| test.c:13:12:13:12 | 0 | Invalid alignof() size set to zero for variable $@. | test.c:13:19:13:20 | l2 | l2 |
1 change: 1 addition & 0 deletions c/misra/test/rules/RULE-8-16/AlignmentWithSizeZero.qlref
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-8-16/AlignmentWithSizeZero.ql
14 changes: 14 additions & 0 deletions c/misra/test/rules/RULE-8-16/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
_Alignas(8) int g1; // COMPLIANT
_Alignas(0) int g2; // NON-COMPLIANT
_Alignas(8 - 8) int g3; // NON-COMPLIANT
_Alignas(float) int g4; // COMPLIANT

struct s {
_Alignas(64) int m1; // COMPLIANT
_Alignas(0) int m2; // NON_COMPLIANT
};

void f() {
_Alignas(8) int l1; // COMPLIANT
_Alignas(0) int l2; // NON-COMPLIANT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
| 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(...) |
| 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(...) |
| 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(...) |
| 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(...) |
| 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(...) |
| 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(...) |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-8-17/MoreThanOneAlignmentSpecifierOnDeclaration.ql
16 changes: 16 additions & 0 deletions c/misra/test/rules/RULE-8-17/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
_Alignas(8) int g1; // COMPLIANT
_Alignas(8) _Alignas(16) int g2; // NON-COMPLIANT
_Alignas(8) _Alignas(8) int g3; // NON-COMPLIANT
_Alignas(float) _Alignas(int) int g4; // NON-COMPLIANT
_Alignas(float) _Alignas(float) int g5; // NON-COMPLIANT
_Alignas(float) _Alignas(float) _Alignas(float) int g5; // NON-COMPLIANT

struct s {
_Alignas(64) int m1; // COMPLIANT
_Alignas(long) _Alignas(16) int m2; // NON_COMPLIANT
};

void f() {
_Alignas(8) int l1; // COMPLIANT
_Alignas(long) _Alignas(16) int l2; // NON_COMPLIANT
}
Loading

0 comments on commit 3e35c56

Please sign in to comment.