misra: Fix 8.2 false positives (#3309)

* misra: Fix 8.2 false positives

Fix false positives in rule 8.2 that occurred in cases when we have a
function definition and declaration in the same file.

For example, the following code generated false positives before this
commit:

```
void f(uint8_t * const x);
void f(uint8_t * const x)
{ (void)x; }
```

We need to distinguish the declaration and the definition, so the dump
file generation routine was extended to keep token where the definition
of the function. The analysis in the addon also been improved.

Closes Trac issue: https://trac.cppcheck.net/ticket/10219
This commit is contained in:
Georgiy Komarov 2021-06-27 11:51:32 +03:00 committed by GitHub
parent 05df31c12a
commit b89f5fbeff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 21 deletions

View File

@ -415,6 +415,7 @@ class Function:
Attributes Attributes
argument Argument list argument Argument list
token Token in function implementation
tokenDef Token in function definition tokenDef Token in function definition
isVirtual Is this function is virtual isVirtual Is this function is virtual
isImplicitlyVirtual Is this function is virtual this in the base classes isImplicitlyVirtual Is this function is virtual this in the base classes
@ -424,6 +425,8 @@ class Function:
Id = None Id = None
argument = None argument = None
argumentId = None argumentId = None
token = None
tokenId = None
tokenDef = None tokenDef = None
tokenDefId = None tokenDefId = None
name = None name = None
@ -435,6 +438,7 @@ class Function:
def __init__(self, element, nestedIn): def __init__(self, element, nestedIn):
self.Id = element.get('id') self.Id = element.get('id')
self.tokenId = element.get('token')
self.tokenDefId = element.get('tokenDef') self.tokenDefId = element.get('tokenDef')
self.name = element.get('name') self.name = element.get('name')
self.type = element.get('type') self.type = element.get('type')
@ -450,7 +454,7 @@ class Function:
self.argumentId = {} self.argumentId = {}
def __repr__(self): def __repr__(self):
attrs = ["Id", "tokenDefId", "name", "type", "isVirtual", attrs = ["Id", "tokenId", "tokenDefId", "name", "type", "isVirtual",
"isImplicitlyVirtual", "isStatic", "argumentId"] "isImplicitlyVirtual", "isStatic", "argumentId"]
return "{}({})".format( return "{}({})".format(
"Function", "Function",
@ -460,6 +464,7 @@ class Function:
def setId(self, IdMap): def setId(self, IdMap):
for argnr, argid in self.argumentId.items(): for argnr, argid in self.argumentId.items():
self.argument[argnr] = IdMap[argid] self.argument[argnr] = IdMap[argid]
self.token = IdMap.get(self.tokenId, None)
self.tokenDef = IdMap[self.tokenDefId] self.tokenDef = IdMap[self.tokenDefId]

View File

@ -1476,21 +1476,10 @@ class MisraChecker:
if rawToken is None: if rawToken is None:
break break
following.append(rawToken) following.append(rawToken)
return following return following
# Check arguments in function declaration
for func in data.functions:
startCall = func.tokenDef.next
if startCall is None or startCall.str != '(':
continue
endCall = startCall.link
if endCall is None or endCall.str != ')':
continue
# Zero arguments should be in form ( void ) # Zero arguments should be in form ( void )
def checkZeroArguments(func, startCall, endCall):
if (len(func.argument) == 0): if (len(func.argument) == 0):
voidArg = startCall.next voidArg = startCall.next
while voidArg is not endCall: while voidArg is not endCall:
@ -1500,23 +1489,85 @@ class MisraChecker:
if not voidArg.str == 'void': if not voidArg.str == 'void':
self.reportError(func.tokenDef, 8, 2) self.reportError(func.tokenDef, 8, 2)
def checkDeclarationArgumentsViolations(func, startCall, endCall):
# Collect the tokens for the arguments in function definition
argNameTokens = set()
for arg in func.argument:
argument = func.argument[arg]
typeStartToken = argument.typeStartToken
if typeStartToken is None:
continue
nameToken = argument.nameToken
if nameToken is None:
continue
argNameTokens.add(nameToken)
# Check if we have the same number of variables in both the
# declaration and the definition.
#
# TODO: We actually need to check if the names of the arguments are
# the same. But we can't do this because we have no links to
# variables in the arguments in function defintion in the dump file.
foundVariables = 0
while startCall and startCall != endCall:
if startCall.varId:
foundVariables += 1
startCall = startCall.next
if len(argNameTokens) != foundVariables:
self.reportError(func.tokenDef, 8, 2)
def checkDefinitionArgumentsViolations(func, startCall, endCall):
for arg in func.argument: for arg in func.argument:
argument = func.argument[arg] argument = func.argument[arg]
typeStartToken = argument.typeStartToken typeStartToken = argument.typeStartToken
if typeStartToken is None: if typeStartToken is None:
continue continue
nameToken = argument.nameToken
# Arguments should have a name unless variable length arg # Arguments should have a name unless variable length arg
nameToken = argument.nameToken
if nameToken is None and typeStartToken.str != '...': if nameToken is None and typeStartToken.str != '...':
self.reportError(typeStartToken, 8, 2) self.reportError(typeStartToken, 8, 2)
# Type declaration on next line (old style declaration list) is not allowed # Type declaration on next line (old style declaration list) is not allowed
if ((typeStartToken.file == endCall.file) and if ((typeStartToken.linenr > endCall.linenr) or
((typeStartToken.linenr > endCall.linenr) or (typeStartToken.column > endCall.column)):
(typeStartToken.column > endCall.column))):
self.reportError(typeStartToken, 8, 2) self.reportError(typeStartToken, 8, 2)
# Check arguments in function declaration
for func in data.functions:
# Check arguments in function definition
tokenImpl = func.token
if tokenImpl:
startCall = tokenImpl.next
if startCall is None or startCall.str != '(':
continue
endCall = startCall.link
if endCall is None or endCall.str != ')':
continue
checkZeroArguments(func, startCall, endCall)
checkDefinitionArgumentsViolations(func, startCall, endCall)
# Check arguments in function declaration
tokenDef = func.tokenDef
if tokenDef:
startCall = func.tokenDef.next
if startCall is None or startCall.str != '(':
continue
endCall = startCall.link
if endCall is None or endCall.str != ')':
continue
checkZeroArguments(func, startCall, endCall)
if tokenImpl:
checkDeclarationArgumentsViolations(func, startCall, endCall)
else:
# When there is no function definition, we should execute
# its checks for the declaration token. The point is that without
# a known definition we have no Function.argument list required
# for declaration check.
checkDefinitionArgumentsViolations(func, startCall, endCall)
# Check arguments in pointer declarations # Check arguments in pointer declarations
for var in data.variables: for var in data.variables:
if not var.isPointer: if not var.isPointer:

View File

@ -319,6 +319,11 @@ static int misra_8_2_k ( //
void); void);
static int misra_8_2_l ( // 8.2 static int misra_8_2_l ( // 8.2
); );
void misra_8_2_m(uint8_t * const x);
void misra_8_2_m(uint8_t * const x)
{
(void)x;
}
int16_t ( *misra_8_2_p_a ) (); // 8.2 int16_t ( *misra_8_2_p_a ) (); // 8.2
int16_t ( *misra_8_2_p_b ) (void); int16_t ( *misra_8_2_p_b ) (void);
int16_t ( *misra_8_2_p_c ) (int); int16_t ( *misra_8_2_p_c ) (int);
@ -732,8 +737,8 @@ extern uint32_t misra_12_3_fn7(const uint32_t * const, const uint8_t); // 8.2
#define MISRA_12_3_FN3_2(A, B) (misra_12_3_fn3(A, \ #define MISRA_12_3_FN3_2(A, B) (misra_12_3_fn3(A, \
B)) B))
#define MISRA_12_3_FN3_2_MSG(x) x, fflush(stderr) #define MISRA_12_3_FN3_2_MSG(x) x, fflush(stderr)
void misra_12_3(int, int, int); // no warning void misra_12_3(int, int, int); // 8.2
void misra_12_3(int a, int b, int c) { // 8.2 void misra_12_3(int a, int b, int c) {
int a1, a2; // 12.3 int a1, a2; // 12.3
int a3; int a4; // no warning int a3; int a4; // no warning
int a5 = 9, a6; // 12.3 int a5 = 9, a6; // 12.3

View File

@ -3679,7 +3679,10 @@ void SymbolDatabase::printXml(std::ostream &out) const
if (!scope->functionList.empty()) { if (!scope->functionList.empty()) {
out << " <functionList>" << std::endl; out << " <functionList>" << std::endl;
for (std::list<Function>::const_iterator function = scope->functionList.begin(); function != scope->functionList.end(); ++function) { for (std::list<Function>::const_iterator function = scope->functionList.begin(); function != scope->functionList.end(); ++function) {
out << " <function id=\"" << &*function << "\" tokenDef=\"" << function->tokenDef << "\" name=\"" << ErrorLogger::toxml(function->name()) << '\"'; out << " <function id=\"" << &*function
<< "\" token=\"" << function->token
<< "\" tokenDef=\"" << function->tokenDef
<< "\" name=\"" << ErrorLogger::toxml(function->name()) << '\"';
out << " type=\"" << (function->type == Function::eConstructor? "Constructor" : out << " type=\"" << (function->type == Function::eConstructor? "Constructor" :
function->type == Function::eCopyConstructor ? "CopyConstructor" : function->type == Function::eCopyConstructor ? "CopyConstructor" :
function->type == Function::eMoveConstructor ? "MoveConstructor" : function->type == Function::eMoveConstructor ? "MoveConstructor" :