MISRA rule 9.2 The initializer for an aggregate or union shall be enclosed in braces (#2899)

This commit is contained in:
kskjerve 2020-11-16 09:27:17 +01:00 committed by GitHub
parent dcd7e3a24f
commit 4d3f76b63c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 320 additions and 14 deletions

View File

@ -1392,7 +1392,7 @@ class MisraChecker:
v = value.getKnownIntValue()
if v is not None and v >= limit:
self.reportError(value, 7, 2)
for token in data.tokenlist:
# Check normal variable assignment
if token.valueType and token.isNumber:
@ -1402,7 +1402,7 @@ class MisraChecker:
# Check use as function parameter
if isFunctionCall(token) and token.astOperand1 and token.astOperand1.function:
functionDeclaration = token.astOperand1.function
if functionDeclaration.tokenDef:
if functionDeclaration.tokenDef is token.astOperand1:
# Token is not a function call, but it is the definition of the function
@ -1436,7 +1436,7 @@ class MisraChecker:
variable = getAssignedVariableToken(token)
if variable:
reportErrorIfVariableIsNotConst(variable, token)
# Check use as return value
function = getFunctionUsingReturnValue(token)
if function:
@ -1447,7 +1447,7 @@ class MisraChecker:
# Check use as function parameter
if isFunctionCall(token) and token.astOperand1 and token.astOperand1.function:
functionDeclaration = token.astOperand1.function
if functionDeclaration.tokenDef:
if functionDeclaration.tokenDef is token.astOperand1:
# Token is not a function call, but it is the definition of the function
@ -1495,6 +1495,250 @@ class MisraChecker:
if token.str == 'restrict':
self.reportError(token, 8, 14)
def misra_9_2(self, data):
# Holds information about a struct or union's element definition.
class ElementDef:
def __init__(self, elementType, name, valueType = None, dimensions = None):
self.elementType = elementType
self.name = name
self.valueType = valueType
self.dimensions = dimensions
# Return an array containing the size of each dimension of an array declaration,
# or coordinates of a designator in an array initializer,
# and the name token's valueType, if it exist.
#
# In the examples below, the ^ indicates the initial token passed to the function.
#
# Ex: int arr[1][2][3] = .....
# ^
# returns: [1,2,3], valueType
#
# Ex: int arr[3][4] = { [1][2] = 5 }
# ^
# returns [1,2], None
def getArrayDimensionsAndValueType(token):
dimensions = []
while token and token.str == '[':
if token.astOperand2 != None:
dimensions.insert(0, token.astOperand2.getKnownIntValue())
token = token.astOperand1
elif token.astOperand1 != None:
dimensions.insert(0, token.astOperand1.getKnownIntValue())
break
else:
dimensions = None
break
valueType = token.valueType if token else None
return dimensions, valueType
# Returns a list of the struct elements as StructElementDef in the order they are declared.
def getRecordElements(valueType):
if not valueType or not valueType.typeScope:
return []
elements = []
for variable in valueType.typeScope.varlist:
if variable.isArray:
dimensions, arrayValueType = getArrayDimensionsAndValueType(variable.nameToken.astParent)
elements.append(ElementDef('array', variable.nameToken.str, arrayValueType, dimensions))
elif variable.isClass:
elements.append(ElementDef('class', variable.nameToken.str, variable.nameToken.valueType))
else:
elements.append(ElementDef('element', variable.nameToken.str))
return elements
# Checks if the initializer conforms to the dimensions of the array declaration
# at a given level.
# Parameters:
# token: root node of the initializer tree
# dimensions: dimension sizes of the array declaration
# valueType: the array type
def checkArrayInitializer(token, dimensions, valueType):
level = 0
levelOffsets = [] # Calculated when designators in initializers are used
elements = getRecordElements(valueType) if valueType.type == 'record' else None
isFirstElement = False
while token:
if token.str == ',':
token = token.astOperand1
isFirstElement = False
continue
if token.isAssignmentOp and not token.valueType:
designator, _ = getArrayDimensionsAndValueType(token.astOperand1)
# Calculate level offset based on designator in initializer
levelOffsets[-1] = len(designator) - 1
token = token.astOperand2
isFirstElement = False
effectiveLevel = sum(levelOffsets) + level
isStringInitializer = token.isString and effectiveLevel == len(dimensions) - 1
isZeroInitializer = (isFirstElement and token.str == '0')
if effectiveLevel == len(dimensions) or isZeroInitializer or isStringInitializer:
if isZeroInitializer or isStringInitializer:
# Zero initializer is ok at any level
# String initializer is ok at one level below value level
pass
else:
isFirstElement = False
if valueType.type == 'record':
if token.isName:
if not token.valueType.typeScope == valueType.typeScope:
self.reportError(token, 9, 2)
return False
else:
if not checkObjectInitializer(token, elements):
return False
elif token.str == '{' or token.isString:
self.reportError(token, 9, 2)
return False
while token:
# Done checking once level is back to 0
if level == 0:
return True
if not token.astParent:
return True
if token.astParent.astOperand1 == token and token.astParent.astOperand2:
token = token.astParent.astOperand2
break
else:
token = token.astParent
if token.str == '{':
level = level - 1
levelOffsets.pop()
effectiveLevel = sum(levelOffsets) + level
elif token.str == '{' :
if not token.astOperand1:
# Empty initializer
self.reportError(token, 9, 2)
return False
token = token.astOperand1
level = level + 1
levelOffsets.append(0)
isFirstElement = True
else:
self.reportError(token, 9, 2)
return False
return True
# Checks if the initializer conforms to the elements of the struct or union
# Parameters:
# token: root node of the initializer tree
# elements: the elements as specified in the declaration
def checkObjectInitializer(token, elements):
if not token:
return True
# Initializer must start with a curly bracket
if not token.str == '{':
self.reportError(token, 9, 2)
return False
# Empty initializer is not ok { }
if not token.astOperand1:
self.reportError(token, 9, 2)
return False
token = token.astOperand1
# Zero initializer is ok { 0 }
if token.str == '0' :
return True
pos = None
while(token):
if token.str == ',':
token = token.astOperand1
else:
if pos == None:
pos = 0
if token.isAssignmentOp:
if token.astOperand1.str == '.':
elementName = token.astOperand1.astOperand1.str
pos = next((i for i, element in enumerate(elements) if element.name == elementName), len(elements))
token = token.astOperand2
if pos >= len(elements):
self.reportError(token, 9, 2)
return False
element = elements[pos]
if element.elementType == 'class':
if token.isName:
if not token.valueType.typeScope == element.valueType.typeScope:
self.reportError(token, 9, 2)
return False
else:
subElements = getRecordElements(element.valueType)
if not checkObjectInitializer(token, subElements):
return False
elif element.elementType == 'array':
if not checkArrayInitializer(token, element.dimensions, element.valueType):
return False
elif token.str == '{':
self.reportError(token, 9, 2)
return False
# The assignment represents the astOperand
if token.astParent.isAssignmentOp:
token = token.astParent
if not token == token.astParent.astOperand2:
pos = pos + 1
token = token.astParent.astOperand2
else:
token = None
return True
# ------
for variable in data.variables:
if not variable.nameToken:
continue
nameToken = variable.nameToken
# Check if declaration and initialization is
# split into two separate statements in ast.
if nameToken.next and nameToken.next.isSplittedVarDeclEq:
nameToken = nameToken.next.next
# Find declarations with initializer assignment
eq = nameToken
while not eq.isAssignmentOp and eq.astParent:
eq = eq.astParent
if not eq.isAssignmentOp:
continue
if variable.isArray :
dimensions, valueType = getArrayDimensionsAndValueType(eq.astOperand1)
if dimensions == None:
continue
checkArrayInitializer(eq.astOperand2, dimensions, valueType)
elif variable.isClass:
if not nameToken.valueType:
continue
valueType = nameToken.valueType
if valueType.type == 'record':
elements = getRecordElements(valueType)
checkObjectInitializer(eq.astOperand2, elements)
def misra_9_5(self, rawTokens):
for token in rawTokens:
if simpleMatch(token, '[ ] = { ['):
@ -1529,7 +1773,7 @@ class MisraChecker:
if op and op.valueType:
if op.valueType.sign in ['unsigned', 'signed']:
return True
return False
return False
def isEssentiallyChar(op):
if op.isName:
@ -1556,7 +1800,7 @@ class MisraChecker:
if token.str == '-':
if not isEssentiallyChar(operand1):
self.reportError(token, 10, 2)
if not isEssentiallyChar(operand2) and not isEssentiallySignedOrUnsigned(operand2):
if not isEssentiallyChar(operand2) and not isEssentiallySignedOrUnsigned(operand2):
self.reportError(token, 10, 2)
def misra_10_4(self, data):
@ -2047,7 +2291,7 @@ class MisraChecker:
self.reportError(token, 15, 3)
break
t = t.next
def misra_15_4(self, data):
# Return a list of scopes affected by a break or goto
def getLoopsAffectedByBreak(knownLoops, scope, isGoto):
@ -2059,7 +2303,7 @@ class MisraChecker:
if not isGoto:
return
getLoopsAffectedByBreak(knownLoops, scope.nestedIn, isGoto)
loopWithBreaks = {}
for token in data.tokenlist:
if token.str not in ['break', 'goto']:
@ -3105,11 +3349,13 @@ class MisraChecker:
self.executeCheck(702, self.misra_7_2, cfg)
if cfgNumber == 0:
self.executeCheck(703, self.misra_7_3, data.rawTokens)
self.executeCheck(704, self.misra_7_4, cfg)
self.executeCheck(704, self.misra_7_4, cfg)
self.executeCheck(811, self.misra_8_11, cfg)
self.executeCheck(812, self.misra_8_12, cfg)
if cfgNumber == 0:
self.executeCheck(814, self.misra_8_14, data.rawTokens)
self.executeCheck(902, self.misra_9_2, cfg)
if cfgNumber == 0:
self.executeCheck(905, self.misra_9_5, data.rawTokens)
self.executeCheck(1001, self.misra_10_1, cfg)
self.executeCheck(1002, self.misra_10_2, cfg)

View File

@ -279,8 +279,8 @@ void misra_7_4()
char *f = 1 + "text f" + 2; // 7.4 18.4
const wchar_t *g = "text_g";
wchar_t *h = "text_h"; // 7.4
misra_7_4_const_call(1, ("text_const_call"));
misra_7_4_const_call(1, ("text_const_call"));
misra_7_4_const_ptr_call(1, ("text_const_call"));
misra_7_4_call(1, "text_call"); // 7.4 11.8
}
@ -295,6 +295,66 @@ enum misra_8_12_e { misra_e1 = sizeof(int), misra_e2}; // no-crash
void misra_8_14(char * restrict str) {(void)str;} // 8.14
void misra_9_2() {
int empty_init[2][2] = { }; // 9.2
int empty_nested_init[2][2] = { { } }; // 9.2
int zero_init_a[5] = { 0 };
int zero_init_b[5][2] = { 0 };
int zero_init_c[2][2] = { { 1, 2 }, { 0 } };
const char string_wrong_level_a[12] = { "Hello world" }; // 9.2
const char string_wrong_level_b[2][20] = "Hello world"; // 9.2
const char string_correct_level_a[] = "Hello world";
const char string_correct_level_b[2][12] = { "Hello world" };
int array_init_incorrect_levels_a[3][2] = { 1, 2, 3, 4, 5, 6 }; // 9.2
int array_init_correct_levels_a[3][2] = { { 1, 2 }, { 3, 4 }, { 5, 6 } };
int array_init_incorrect_levels_b[6] = { { 1, 2 }, { 3, 4 }, { 5, 6 } }; // 9.2
int array_init_correct_levels_b[6] = { 1, 2, 3, 4, 5, 6 };
int array_incorrect_designator_a[1] = { [0][1] = 1 }; // 9.2
int array_incorrect_designator_b[1] = { [0] = { 1, 2 } }; // 9.2
int array_incorrect_designator_c[1][2] = { [0] = 1 }; // 9.2
int array_incorrect_designator_d[2][2] = { { 1, 2 }, [1][0] = {3, 4} }; // 9.2
int array_correct_designator_a[2] = { [0] = 1, 2 };
int array_correct_designator_b[2] = { [1] = 2, [0] = 1 };
int array_correct_designator_c[2][2] = { { 1, 2 }, [1] = {3, 4} };
int array_correct_designator_d[2][2] = { { 1, 2 }, [1][0] = 3, 4};
typedef struct {
int i1;
int i2;
} struct1;
typedef struct {
char c1;
struct1 is1;
char c2[4];
} struct2;
int a;
struct2 struct_empty_init = { }; // 9.2
struct2 struct_zero_init = { 0 };
struct1 struct_missing_brackets = 1; // 9.2
struct2 struct_correct_init = { 1, {2, 3}, {0} };
struct1 struct_array_incorrect_levels[2] = { 1, 2, 3, 4 }; // 9.2
struct1 struct_array_correct_levels[2] = { {1, 2}, {3, 4} };
struct1 struct_correct_designator_a = { .i2 = 2, .i1 = 1 };
struct2 struct_correct_designator_b = { .is1 = {2, 3}, { 4 } };
struct1 struct_correct_designator_c = { a = 1, 2 }; // 13.1
struct2 struct_incorrect_type = { .is1 = struct_correct_designator_b }; // 9.2
struct2 struct_correct_type = { .is1 = struct_correct_designator_a };
struct1 struct_array_incorrect_type[1] = { struct_correct_designator_b }; // 9.2
struct1 struct_array_correct_type[1] = { struct_correct_designator_a };
union misra_9_2_union { // 19.2
char c;
struct1 i;
} u = { 3 }; // 19.2
}
void misra_9_5() {
int x[] = {[0]=23}; // 9.5
}
@ -828,7 +888,7 @@ void misra_15_4() {
}
for (y = 0; y < 42; ++y) { // 15.4
if (y==1) {
break;
break;
}
if (y==2) {
break;
@ -865,7 +925,7 @@ void misra_15_4() {
// Inner loop uses goto
for (x = 0; x < 42; ++x) { // 15.4
if (x==1) {
break;
break;
}
for (y = 0; y < 42; ++y) {
if (y == 1) {
@ -1187,7 +1247,7 @@ void misra_18_8(int x) {
int buf1[10];
int buf2[sizeof(int)];
int vla[x]; // 18.8
static const unsigned char arr18_8_1[] = UNDEFINED_ID;
static const unsigned char arr18_8_1[] = UNDEFINED_ID; // 9.2
static uint32_t enum_test_0[R18_8_ENUM_CONSTANT_0] = {0};
}