MISRA rule 9.2 The initializer for an aggregate or union shall be enclosed in braces (#2899)
This commit is contained in:
parent
dcd7e3a24f
commit
4d3f76b63c
264
addons/misra.py
264
addons/misra.py
|
@ -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)
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue