import cppcheckdata # Holds information about an array, struct or union's element definition. class ElementDef: def __init__(self, elementType, name, valueType, dimensions = None): self.elementType = elementType # 'array', 'record' or 'value' self.name = str(name) self.valueType = valueType self.children = [] self.dimensions = dimensions self.parent = None self.isDesignated = False self.isPositional = False self.numInits = 0 self.childIndex = -1 self.flexibleToken = None self.isFlexible = False self.structureViolationToken = None def __repr__(self): inits = "" if self.isPositional: inits += 'P' if self.isDesignated: inits += 'D' if not (self.isPositional or self.isDesignated) and self.numInits == 0: inits += '_' if self.numInits > 1: inits += str(self.numInits) attrs = ["childIndex", "elementType", "valueType"] return "{}({}, {}, {})".format( "ElementDef", self.getLongName(), inits, ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) @property def isArray(self): return self.elementType == 'array' @property def isRecord(self): return self.elementType == 'record' @property def isValue(self): return self.elementType == 'value' def getLongName(self): return self.parent.getLongName() + "." + self.name if self.parent else self.name def getInitDump(self): t = [] if self.isPositional: t.append('P') if self.isDesignated: t.append('D') if self.numInits == 0: t.append('_') if self.numInits > 1: t.append(str(self.numInits)) myDump = "".join(t) if len(self.children): childDumps = [] for c in self.children: childDumps.append(c.getInitDump()) if self.structureViolationToken is not None: myDump += "!" myDump += "{ " + ", ".join(childDumps) + " }" return myDump def addChild(self, child): self.children.append(child) child.parent = self def getNextChild(self): self.childIndex += 1 return self.getChildByIndex(self.childIndex) def getChildByIndex(self, index): if self.isFlexible: while len(self.children) <= index: createChild(self, self.flexibleToken, len(self.children), None) return self.children[index] if 0 <= index < len(self.children) else None def getChildByName(self, name): for c in self.children: if c.name == name: return c return None def getNextValueElement(self, root): current = self while current != root: if not current.parent: return None # Get next index of parent i = current.parent.children.index(current) + 1 # Next index of parent exists if i < len(current.parent.children): current = current.parent.children[i] return current.getFirstValueElement() # Next index of parent doesn't exist. Move up current = current.parent return None def getFirstValueElement(self): current = self # Move to first child as long as children exists next_child = current.getChildByIndex(0) while next_child: current = next_child next_child = current.getChildByIndex(0) return current def getLastValueElement(self): current = self # Move to last child as long as children exists while len(current.children) > 0: current = current.children[-1] return current def getChildByValueElement(self, ed): potentialChild = ed while potentialChild and potentialChild not in self.children: potentialChild = potentialChild.parent return self.children[self.children.index(potentialChild)] if potentialChild else None def getEffectiveLevel(self): if self.parent and self.parent.elementType == "array": return self.parent.getEffectiveLevel() + 1 else: return 0 def setInitialized(self, designated=False, positional=False): if designated: self.isDesignated = True if positional or not designated: self.isPositional = True self.numInits += 1 def initializeChildren(self): for child in self.children: child.setInitialized(positional=True) child.initializeChildren() def unset(self): self.isDesignated = False self.isPositional = False # Unset is always recursive for child in self.children: child.unset() def markStuctureViolation(self, token): if self.name == '->': self.children[0].markStuctureViolation(token) elif not self.structureViolationToken: self.structureViolationToken = token def markAsFlexibleArray(self, token): self.flexibleToken = token self.isFlexible = True def markAsCurrent(self): if self.parent: if self.name == '<-': self.parent.childIndex = self.parent.children.index(self.children[0]) else: self.parent.childIndex = self.parent.children.index(self) self.parent.markAsCurrent() def isAllChildrenSet(self): myself = len(self.children) == 0 and (self.isDesignated or self.isPositional) mychildren = len(self.children) > 0 and all([child.isAllChildrenSet() for child in self.children]) return myself or mychildren def isAllSet(self): return all([child.isPositional or child.isDesignated for child in self.children]) def isOnlyDesignated(self): return all([not child.isPositional for child in self.children]) def isMisra92Compliant(self): return self.structureViolationToken is None and all([child.isMisra92Compliant() for child in self.children]) def isMisra93Compliant(self): if self.elementType == 'array': result = self.isAllChildrenSet() or \ ((self.isAllSet() or self.isOnlyDesignated()) and all([not (child.isDesignated or child.isPositional) or child.isMisra93Compliant() for child in self.children])) return result elif self.elementType == 'record': result = all([child.isMisra93Compliant() for child in self.children]) return result else: return True def isMisra94Compliant(self): return self.numInits <= 1 and all([child.isMisra94Compliant() for child in self.children]) def isMisra95Compliant(self): return not self.isFlexible or all([not child.isDesignated for child in self.children]) # Parses the initializers and update the ElementDefs status accordingly class InitializerParser: def __init__(self): self.token = None self.root = None self.ed = None self.rootStack = [] def parseInitializer(self, root, token): self.root = root self.token = token dummyRoot = ElementDef('array', '->', self.root.valueType) dummyRoot.children = [self.root] self.rootStack = [] self.root = dummyRoot self.ed = self.root.getFirstValueElement() isFirstElement = False isDesignated = False while self.token: if self.token.str == ',': self.token = self.token.astOperand1 isFirstElement = False # Designated initializer ( [2]=... or .name=... ) elif self.token.isAssignmentOp and not self.token.valueType: self.popFromStackIfExitElement() self.ed = getElementByDesignator(self.root, self.token.astOperand1) if self.ed: # Update root self.pushToRootStackAndMarkAsDesignated() # Make sure ed points to valueElement self.ed = self.ed.getFirstValueElement() self.token = self.token.astOperand2 isFirstElement = False isDesignated = True elif self.token.isString and self.ed and self.ed.isArray: self.ed.setInitialized(isDesignated) if self.token == self.token.astParent.astOperand1 and self.token.astParent.astOperand2: self.token = self.token.astParent.astOperand2 self.ed.markAsCurrent() self.ed = self.root.getNextChild() else: self.unwindAndContinue() continue elif self.token.str == '{': nextChild = self.root.getNextChild() if self.root is not None else None if nextChild: if nextChild.isArray or nextChild.isRecord: nextChild.unset() nextChild.setInitialized(isDesignated) self.ed = nextChild.getFirstValueElement() isDesignated = False elif nextChild.valueType is None: # No type information available - unable to check structure - assume correct initialization nextChild.setInitialized(isDesignated) self.unwindAndContinue() continue elif self.token.astOperand1: # Create dummy nextChild to represent excess levels in initializer dummyRoot = ElementDef('array', '<-', self.root.valueType) dummyRoot.parent = self.root dummyRoot.childIndex = 0 dummyRoot.children = [nextChild] nextChild.parent = dummyRoot self.root.markStuctureViolation(self.token) # Fake dummy as nextChild (of current root) nextChild = dummyRoot if self.token.astOperand1: self.root = nextChild self.token = self.token.astOperand1 isFirstElement = True else: if self.root: # {} if self.root.name == '<-': self.root.parent.markStuctureViolation(self.token) else: self.root.markStuctureViolation(self.token) self.ed = None self.unwindAndContinue() else: if self.ed and self.ed.isValue: if not isDesignated and len(self.rootStack) > 0 and self.rootStack[-1][1] == self.root: self.rootStack[-1][0].markStuctureViolation(self.token) if isFirstElement and self.token.str == '0' and self.token.next.str == '}': # Zero initializer causes recursive initialization self.root.initializeChildren() elif self.token.isString and self.ed.valueType and self.ed.valueType.pointer > 0: if self.ed.valueType.pointer - self.ed.getEffectiveLevel() == 1: if self.ed.parent != self.root: self.root.markStuctureViolation(self.token) self.ed.setInitialized(isDesignated) elif self.ed.valueType.pointer == self.ed.getEffectiveLevel(): if(self.root.name != '->' and self.ed.parent.parent != self.root) or (self.root.name == '->' and self.root.children[0] != self.ed.parent): self.root.markStuctureViolation(self.token) else: self.ed.parent.setInitialized(isDesignated) self.ed.parent.initializeChildren() else: if self.ed.parent != self.root: # Check if token is correct value type for self.root.children[?] child = self.root.getChildByValueElement(self.ed) if self.token.valueType: if child.elementType != 'record' or self.token.valueType.type != 'record' or child.valueType.typeScope != self.token.valueType.typeScope: self.root.markStuctureViolation(self.token) self.ed.setInitialized(isDesignated) # Mark all elements up to root with positional or designated # (for complex designators, or missing structure) parent = self.ed.parent while parent and parent != self.root: parent.isDesignated = isDesignated if isDesignated and not parent.isPositional else parent.isDesignated parent.isPositional = not isDesignated if not isDesignated and not parent.isDesignated else parent.isPositional parent = parent.parent isDesignated = False if self.token.isString and self.ed.parent.isArray: self.ed = self.ed.parent self.unwindAndContinue() def pushToRootStackAndMarkAsDesignated(self): new = self.ed.parent if new != self.root: # Mark all elements up to self.root root as designated parent = new while parent and parent != self.root: parent.isDesignated = True parent = parent.parent self.rootStack.append((self.root, new)) new.markAsCurrent() new.childIndex = new.children.index(self.ed) - 1 self.root = new def popFromStackIfExitElement(self): if len(self.rootStack) > 0 and self.rootStack[-1][1] == self.root: old = self.rootStack.pop()[0] old.markAsCurrent() self.root = old def unwindAndContinue(self): while self.token: if self.token.astParent.astOperand1 == self.token and self.token.astParent.astOperand2: if self.ed: self.ed.markAsCurrent() self.ed = self.ed.getNextValueElement(self.root) self.token = self.token.astParent.astOperand2 break else: self.token = self.token.astParent if self.token.str == '{': if self.root: self.ed = self.root.getLastValueElement() self.ed.markAsCurrent() # Cleanup if root is dummy node representing excess levels in initializer if self.root.name == '<-': self.root.children[0].parent = self.root.parent self.root = self.root.parent if self.token.astParent is None: self.token = None break def misra_9_x(self, data, rule, rawTokens = None): # If there are arrays with unknown size constants then we need to warn about missing configuration # and bailout has_config_errors = False for var in data.variables: if not var.isArray or var.nameToken is None or not cppcheckdata.simpleMatch(var.nameToken.next,'['): continue tok = var.nameToken.next while tok.str == '[': sz = tok.astOperand2 if sz and sz.getKnownIntValue() is None: has_var = False unknown_constant = False tokens = [sz] while len(tokens) > 0: t = tokens[-1] tokens = tokens[:-1] if t: if t.isName and t.getKnownIntValue() is None: if t.varId or t.variable: has_var = True continue unknown_constant = True cppcheckdata.reportError(sz, 'error', 'Unknown constant {}, please review configuration'.format(t.str), 'misra', 'config') has_config_errors = True if t.isArithmeticalOp: tokens += [t.astOperand1, t.astOperand2] if not unknown_constant and not has_var: cppcheckdata.reportError(sz, 'error', 'Unknown array size, please review configuration', 'misra', 'config') has_config_errors = True tok = tok.link.next if has_config_errors: return parser = InitializerParser() for variable in data.variables: if variable.nameToken is None: 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 # We are only looking for initializers if not eq.isAssignmentOp or eq.astOperand2.isName: continue if variable.isArray or variable.isClass: ed = getElementDef(nameToken, rawTokens) # No need to check non-arrays if valueType is missing, # since we can't say anything useful about the structure # without it. if ed.valueType is None and not variable.isArray: continue parser.parseInitializer(ed, eq.astOperand2) # print(rule, nameToken.str + '=', ed.getInitDump()) if rule == 902 and not ed.isMisra92Compliant(): self.reportError(nameToken, 9, 2) if rule == 903 and not ed.isMisra93Compliant(): # Do not check when variable is pointer type type_token = variable.nameToken while type_token and type_token.isName: type_token = type_token.previous if type_token and type_token.str == '*': continue self.reportError(nameToken, 9, 3) if rule == 904 and not ed.isMisra94Compliant(): self.reportError(nameToken, 9, 4) if rule == 905 and not ed.isMisra95Compliant(): self.reportError(nameToken, 9, 5) def getElementDef(nameToken, rawTokens = None): if nameToken.variable.isArray: ed = ElementDef("array", nameToken.str, nameToken.valueType) createArrayChildrenDefs(ed, nameToken.astParent, nameToken.variable, rawTokens) elif nameToken.variable.isClass: ed = ElementDef("record", nameToken.str, nameToken.valueType) createRecordChildrenDefs(ed, nameToken.variable) else: ed = ElementDef("value", nameToken.str, nameToken.valueType) return ed def createArrayChildrenDefs(ed, token, var, rawTokens = None): if token and token.str == '[': if rawTokens is not None: foundToken = next((rawToken for rawToken in rawTokens if rawToken.file == token.file and rawToken.linenr == token.linenr and rawToken.column == token.column ), None) if foundToken and foundToken.next and foundToken.next.str == ']': ed.markAsFlexibleArray(token) if (token.astOperand2 is not None) and (token.astOperand2.getKnownIntValue() is not None): for i in range(token.astOperand2.getKnownIntValue()): createChild(ed, token, i, var) else: ed.markAsFlexibleArray(token) def createChild(ed, token, name, var): if token.astParent and token.astParent.str == '[': child = ElementDef("array", name, ed.valueType) createArrayChildrenDefs(child, token.astParent, var) else: if ed.valueType and ed.valueType.type == "record": child = ElementDef("record", name, ed.valueType) createRecordChildrenDefs(child, var) else: child = ElementDef("value", name, ed.valueType) ed.addChild(child) def createRecordChildrenDefs(ed, var): valueType = ed.valueType if not valueType or not valueType.typeScope: return if var is None: return typeToken = var.typeEndToken while typeToken and typeToken.isName: typeToken = typeToken.previous if typeToken and typeToken.str == '*': child = ElementDef("pointer", var.nameToken, var.nameToken.valueType) ed.addChild(child) return for variable in valueType.typeScope.varlist: if variable is var: continue child = getElementDef(variable.nameToken) ed.addChild(child) def getElementByDesignator(ed, token): if not token.str in [ '.', '[' ]: return None while token.str in [ '.', '[' ]: token = token.astOperand1 while ed and not token.isAssignmentOp: token = token.astParent if token.str == '[': if not ed.isArray: ed.markStuctureViolation(token) chIndex = -1 if token.astOperand2 is not None: chIndex = token.astOperand2.getKnownIntValue() elif token.astOperand1 is not None: chIndex = token.astOperand1.getKnownIntValue() ed = ed.getChildByIndex(chIndex) if chIndex is not None else None elif token.str == '.': if not ed.isRecord: ed.markStuctureViolation(token) name = "" if token.astOperand2 is not None: name = token.astOperand2.str elif token.astOperand1 is not None: name = token.astOperand1.str ed = ed.getChildByName(name) return ed