Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement alignment package for MISRA C 2012 amdmt 3 #760

Merged
merged 8 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
MichaelRFairhurst marked this conversation as resolved.
Show resolved Hide resolved
*/
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
)
lcartey marked this conversation as resolved.
Show resolved Hide resolved
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
Loading