Improved static string comparision check: Implemented #3214
Fixed false negative on argument count of fnprintf/snprintf when first variable argument is a string. (#3655) Uncommented call of virtualDestructorError in getErrorMessages in checkclass.h Refactorizations: - Rearranged code in checkother.h to make ordering more consistent and to increase encapsulation of private data - Replaced some single-token-patterns
This commit is contained in:
parent
3f1ab5af9b
commit
6f164de609
|
@ -131,7 +131,7 @@ private:
|
||||||
c.unusedPrivateFunctionError(0, "classname", "funcname");
|
c.unusedPrivateFunctionError(0, "classname", "funcname");
|
||||||
c.memsetError(0, "memfunc", "classname", "class");
|
c.memsetError(0, "memfunc", "classname", "class");
|
||||||
c.operatorEqReturnError(0, "class");
|
c.operatorEqReturnError(0, "class");
|
||||||
//c.virtualDestructorError(0, "Base", "Derived");
|
c.virtualDestructorError(0, "Base", "Derived");
|
||||||
c.thisSubtractionError(0);
|
c.thisSubtractionError(0);
|
||||||
c.operatorEqRetRefThisError(0);
|
c.operatorEqRetRefThisError(0);
|
||||||
c.operatorEqToSelfError(0);
|
c.operatorEqToSelfError(0);
|
||||||
|
@ -175,10 +175,10 @@ private:
|
||||||
Usage() : assign(false), init(false) { }
|
Usage() : assign(false), init(false) { }
|
||||||
|
|
||||||
/** @brief has this variable been assigned? */
|
/** @brief has this variable been assigned? */
|
||||||
bool assign;
|
bool assign;
|
||||||
|
|
||||||
/** @brief has this variable been initialized? */
|
/** @brief has this variable been initialized? */
|
||||||
bool init;
|
bool init;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isBaseClassFunc(const Token *tok, const Scope *scope);
|
bool isBaseClassFunc(const Token *tok, const Scope *scope);
|
||||||
|
|
|
@ -477,7 +477,7 @@ void CheckOther::checkSizeofForArrayParameter()
|
||||||
while (declTok->str() == "[") {
|
while (declTok->str() == "[") {
|
||||||
declTok = declTok->link()->next();
|
declTok = declTok->link()->next();
|
||||||
}
|
}
|
||||||
if (!(Token::Match(declTok, "= %str%")) && !(Token::simpleMatch(declTok, "= {")) && !(Token::simpleMatch(declTok, ";"))) {
|
if (!(Token::Match(declTok, "= %str%")) && !(Token::simpleMatch(declTok, "= {")) && declTok->str() != ";") {
|
||||||
if (declTok->str() == ",") {
|
if (declTok->str() == ",") {
|
||||||
while (declTok->str() != ";") {
|
while (declTok->str() != ";") {
|
||||||
if (declTok->str() == ")") {
|
if (declTok->str() == ")") {
|
||||||
|
@ -1344,8 +1344,7 @@ void CheckOther::checkWrongPrintfScanfArguments()
|
||||||
if (Token::Match(formatStringTok, "%str% ,")) {
|
if (Token::Match(formatStringTok, "%str% ,")) {
|
||||||
argListTok = formatStringTok->nextArgument(); // Find fourth parameter (first argument of va_args)
|
argListTok = formatStringTok->nextArgument(); // Find fourth parameter (first argument of va_args)
|
||||||
formatString = formatStringTok->str();
|
formatString = formatStringTok->str();
|
||||||
}
|
} else if (Token::Match(formatStringTok, "%str% )")) {
|
||||||
if (Token::Match(formatStringTok, "%str% )")) {
|
|
||||||
argListTok = 0; // Find fourth parameter (first argument of va_args)
|
argListTok = 0; // Find fourth parameter (first argument of va_args)
|
||||||
formatString = formatStringTok->str();
|
formatString = formatStringTok->str();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1401,7 +1400,7 @@ void CheckOther::checkWrongPrintfScanfArguments()
|
||||||
numFormat++;
|
numFormat++;
|
||||||
|
|
||||||
// Perform type checks
|
// Perform type checks
|
||||||
if (_settings->isEnabled("style") && Token::Match(argListTok, "%any% ,|)")) { // We can currently only check the type of arguments matching this simple pattern.
|
if (_settings->isEnabled("style") && argListTok && Token::Match(argListTok->next(), "[,)]")) { // We can currently only check the type of arguments matching this simple pattern.
|
||||||
const Variable* variableInfo = symbolDatabase->getVariableFromVarId(argListTok->varId());
|
const Variable* variableInfo = symbolDatabase->getVariableFromVarId(argListTok->varId());
|
||||||
const Token* varTypeTok = variableInfo ? variableInfo->typeStartToken() : NULL;
|
const Token* varTypeTok = variableInfo ? variableInfo->typeStartToken() : NULL;
|
||||||
if (varTypeTok && varTypeTok->str() == "static")
|
if (varTypeTok && varTypeTok->str() == "static")
|
||||||
|
@ -1906,7 +1905,7 @@ void CheckOther::lookupVar(const Token *tok1, const std::string &varname)
|
||||||
if (indentlevel == 0)
|
if (indentlevel == 0)
|
||||||
return;
|
return;
|
||||||
used1 = true;
|
used1 = true;
|
||||||
if (for_or_while && !Token::simpleMatch(tok->next(), "="))
|
if (for_or_while && tok->strAt(1) != "=")
|
||||||
used2 = true;
|
used2 = true;
|
||||||
if (used1 && used2)
|
if (used1 && used2)
|
||||||
return;
|
return;
|
||||||
|
@ -1916,7 +1915,7 @@ void CheckOther::lookupVar(const Token *tok1, const std::string &varname)
|
||||||
// %unknown% ( %any% ) {
|
// %unknown% ( %any% ) {
|
||||||
// If %unknown% is anything except if, we assume
|
// If %unknown% is anything except if, we assume
|
||||||
// that it is a for or while loop or a macro hiding either one
|
// that it is a for or while loop or a macro hiding either one
|
||||||
if (Token::simpleMatch(tok->next(), "(") &&
|
if (tok->strAt(1) == "(" &&
|
||||||
Token::simpleMatch(tok->next()->link(), ") {")) {
|
Token::simpleMatch(tok->next()->link(), ") {")) {
|
||||||
if (tok->str() != "if")
|
if (tok->str() != "if")
|
||||||
for_or_while = true;
|
for_or_while = true;
|
||||||
|
@ -2971,35 +2970,45 @@ void CheckOther::checkAlwaysTrueOrFalseStringCompare()
|
||||||
|
|
||||||
const Token *tok = _tokenizer->tokens();
|
const Token *tok = _tokenizer->tokens();
|
||||||
while (tok && (tok = Token::findmatch(tok, pattern1)) != NULL) {
|
while (tok && (tok = Token::findmatch(tok, pattern1)) != NULL) {
|
||||||
alwaysTrueFalseStringCompareError(tok, tok->strAt(2), tok->strAt(4));
|
const std::string &str1 = tok->strAt(2);
|
||||||
|
const std::string &str2 = tok->strAt(4);
|
||||||
|
alwaysTrueFalseStringCompareError(tok, str1, str2, str1==str2);
|
||||||
tok = tok->tokAt(5);
|
tok = tok->tokAt(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
tok = _tokenizer->tokens();
|
tok = _tokenizer->tokens();
|
||||||
while (tok && (tok = Token::findmatch(tok, pattern2)) != NULL) {
|
while (tok && (tok = Token::findmatch(tok, pattern2)) != NULL) {
|
||||||
alwaysTrueFalseStringCompareError(tok, tok->strAt(4), tok->strAt(6));
|
const std::string &str1 = tok->strAt(4);
|
||||||
|
const std::string &str2 = tok->strAt(6);
|
||||||
|
alwaysTrueFalseStringCompareError(tok, str1, str2, str1==str2);
|
||||||
tok = tok->tokAt(7);
|
tok = tok->tokAt(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
tok = _tokenizer->tokens();
|
tok = _tokenizer->tokens();
|
||||||
while (tok && (tok = Token::findmatch(tok, pattern3)) != NULL) {
|
while (tok && (tok = Token::findmatch(tok, pattern3)) != NULL) {
|
||||||
const Token *var1 = tok->tokAt(2);
|
const std::string &str1 = tok->strAt(2);
|
||||||
const Token *var2 = tok->tokAt(4);
|
const std::string &str2 = tok->strAt(4);
|
||||||
const std::string &str1 = var1->str();
|
|
||||||
const std::string &str2 = var2->str();
|
|
||||||
if (str1 == str2)
|
if (str1 == str2)
|
||||||
alwaysTrueStringVariableCompareError(tok, str1, str2);
|
alwaysTrueStringVariableCompareError(tok, str1, str2);
|
||||||
tok = tok->tokAt(5);
|
tok = tok->tokAt(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tok = _tokenizer->tokens();
|
||||||
|
while (tok && (tok = Token::findmatch(tok, "!!+ %str% ==|!= %str% !!+")) != NULL) {
|
||||||
|
const std::string &str1 = tok->strAt(1);
|
||||||
|
const std::string &str2 = tok->strAt(3);
|
||||||
|
alwaysTrueFalseStringCompareError(tok, str1, str2, str1==str2);
|
||||||
|
tok = tok->tokAt(5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckOther::alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2)
|
void CheckOther::alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2, bool warning)
|
||||||
{
|
{
|
||||||
const std::size_t stringLen = 10;
|
const std::size_t stringLen = 10;
|
||||||
const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) + "..");
|
const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) + "..");
|
||||||
const std::string string2 = (str2.size() < stringLen) ? str2 : (str2.substr(0, stringLen-2) + "..");
|
const std::string string2 = (str2.size() < stringLen) ? str2 : (str2.substr(0, stringLen-2) + "..");
|
||||||
|
|
||||||
if (str1 == str2) {
|
if (warning) {
|
||||||
reportError(tok, Severity::warning, "staticStringCompare",
|
reportError(tok, Severity::warning, "staticStringCompare",
|
||||||
"Comparison of always identical static strings.\n"
|
"Comparison of always identical static strings.\n"
|
||||||
"The compared strings, '" + string1 + "' and '" + string2 + "', are always identical. "
|
"The compared strings, '" + string1 + "' and '" + string2 + "', are always identical. "
|
||||||
|
|
|
@ -112,11 +112,9 @@ public:
|
||||||
|
|
||||||
/** @brief Clarify calculation for ".. a * b ? .." */
|
/** @brief Clarify calculation for ".. a * b ? .." */
|
||||||
void clarifyCalculation();
|
void clarifyCalculation();
|
||||||
void clarifyCalculationError(const Token *tok, const std::string &op);
|
|
||||||
|
|
||||||
/** @brief Suspicious condition (assignment+comparison) */
|
/** @brief Suspicious condition (assignment+comparison) */
|
||||||
void clarifyCondition();
|
void clarifyCondition();
|
||||||
void clarifyConditionError(const Token *tok, bool assign, bool boolop);
|
|
||||||
|
|
||||||
/** @brief Are there C-style pointer casts in a c++ file? */
|
/** @brief Are there C-style pointer casts in a c++ file? */
|
||||||
void warningOldStylePointerCast();
|
void warningOldStylePointerCast();
|
||||||
|
@ -167,28 +165,15 @@ public:
|
||||||
|
|
||||||
/** @brief %Check for 'sizeof sizeof ..' */
|
/** @brief %Check for 'sizeof sizeof ..' */
|
||||||
void sizeofsizeof();
|
void sizeofsizeof();
|
||||||
void sizeofsizeofError(const Token *tok);
|
|
||||||
|
|
||||||
/** @brief %Check for calculations inside sizeof */
|
/** @brief %Check for calculations inside sizeof */
|
||||||
void sizeofCalculation();
|
void sizeofCalculation();
|
||||||
void sizeofCalculationError(const Token *tok);
|
|
||||||
|
|
||||||
/** @brief scanf can crash if width specifiers are not used */
|
/** @brief scanf can crash if width specifiers are not used */
|
||||||
void invalidScanf();
|
void invalidScanf();
|
||||||
void invalidScanfError(const Token *tok);
|
|
||||||
|
|
||||||
/** @brief %Checks type and number of arguments given to functions like printf or scanf*/
|
/** @brief %Checks type and number of arguments given to functions like printf or scanf*/
|
||||||
void checkWrongPrintfScanfArguments();
|
void checkWrongPrintfScanfArguments();
|
||||||
void wrongPrintfScanfArgumentsError(const Token* tok,
|
|
||||||
const std::string &function,
|
|
||||||
unsigned int numFormat,
|
|
||||||
unsigned int numFunction);
|
|
||||||
void invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat);
|
|
||||||
void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat);
|
|
||||||
void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat);
|
|
||||||
void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat);
|
|
||||||
void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c);
|
|
||||||
void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c);
|
|
||||||
|
|
||||||
/** @brief %Check for assigning to the same variable twice in a switch statement*/
|
/** @brief %Check for assigning to the same variable twice in a switch statement*/
|
||||||
void checkRedundantAssignmentInSwitch();
|
void checkRedundantAssignmentInSwitch();
|
||||||
|
@ -265,7 +250,23 @@ public:
|
||||||
/** @brief %Check for double free or double close operations */
|
/** @brief %Check for double free or double close operations */
|
||||||
void checkDoubleFree();
|
void checkDoubleFree();
|
||||||
|
|
||||||
|
private:
|
||||||
// Error messages..
|
// Error messages..
|
||||||
|
void clarifyCalculationError(const Token *tok, const std::string &op);
|
||||||
|
void clarifyConditionError(const Token *tok, bool assign, bool boolop);
|
||||||
|
void sizeofsizeofError(const Token *tok);
|
||||||
|
void sizeofCalculationError(const Token *tok);
|
||||||
|
void invalidScanfError(const Token *tok);
|
||||||
|
void wrongPrintfScanfArgumentsError(const Token* tok,
|
||||||
|
const std::string &function,
|
||||||
|
unsigned int numFormat,
|
||||||
|
unsigned int numFunction);
|
||||||
|
void invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat);
|
||||||
|
void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat);
|
||||||
|
void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat);
|
||||||
|
void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat);
|
||||||
|
void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c);
|
||||||
|
void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c);
|
||||||
void cstyleCastError(const Token *tok);
|
void cstyleCastError(const Token *tok);
|
||||||
void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to);
|
void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to);
|
||||||
void dangerousUsageStrtolError(const Token *tok);
|
void dangerousUsageStrtolError(const Token *tok);
|
||||||
|
@ -301,7 +302,7 @@ public:
|
||||||
void duplicateIfError(const Token *tok1, const Token *tok2);
|
void duplicateIfError(const Token *tok1, const Token *tok2);
|
||||||
void duplicateBranchError(const Token *tok1, const Token *tok2);
|
void duplicateBranchError(const Token *tok1, const Token *tok2);
|
||||||
void duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op);
|
void duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op);
|
||||||
void alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2);
|
void alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2, bool warning);
|
||||||
void alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2);
|
void alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2);
|
||||||
void duplicateBreakError(const Token *tok);
|
void duplicateBreakError(const Token *tok);
|
||||||
void unreachableCodeError(const Token* tok);
|
void unreachableCodeError(const Token* tok);
|
||||||
|
@ -360,7 +361,7 @@ public:
|
||||||
c.duplicateIfError(0, 0);
|
c.duplicateIfError(0, 0);
|
||||||
c.duplicateBranchError(0, 0);
|
c.duplicateBranchError(0, 0);
|
||||||
c.duplicateExpressionError(0, 0, "&&");
|
c.duplicateExpressionError(0, 0, "&&");
|
||||||
c.alwaysTrueFalseStringCompareError(0, "str1", "str2");
|
c.alwaysTrueFalseStringCompareError(0, "str1", "str2", true);
|
||||||
c.alwaysTrueStringVariableCompareError(0, "varname1", "varname2");
|
c.alwaysTrueStringVariableCompareError(0, "varname1", "varname2");
|
||||||
c.duplicateBreakError(0);
|
c.duplicateBreakError(0);
|
||||||
c.unreachableCodeError(0);
|
c.unreachableCodeError(0);
|
||||||
|
@ -438,8 +439,6 @@ public:
|
||||||
"* optimisation: detect post increment/decrement\n";
|
"* optimisation: detect post increment/decrement\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Used in warningRedundantCode()
|
* @brief Used in warningRedundantCode()
|
||||||
* Iterates through the %var% tokens in a fully qualified name and concatenates them.
|
* Iterates through the %var% tokens in a fully qualified name and concatenates them.
|
||||||
|
@ -452,7 +451,7 @@ private:
|
||||||
*tok = (*tok)->tokAt(2);
|
*tok = (*tok)->tokAt(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Token::Match(*tok, "%var%"))
|
if ((*tok)->isName())
|
||||||
varname.append((*tok)->str());
|
varname.append((*tok)->str());
|
||||||
|
|
||||||
return varname;
|
return varname;
|
||||||
|
|
|
@ -2207,7 +2207,7 @@ private:
|
||||||
" fprintf(stderr,\"%u%s\");\n"
|
" fprintf(stderr,\"%u%s\");\n"
|
||||||
" snprintf(str,10,\"%u%s\");\n"
|
" snprintf(str,10,\"%u%s\");\n"
|
||||||
" sprintf(string1, \"%-*.*s\", 32, string2);\n" // #3364
|
" sprintf(string1, \"%-*.*s\", 32, string2);\n" // #3364
|
||||||
" sprintf(string1, \"%*\", 32);\n" // #3364
|
" snprintf(a, 9, \"%s%d\", \"11223344\");\n" // #3655
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:2]: (error) printf format string has 1 parameters but only 0 are given\n"
|
ASSERT_EQUALS("[test.cpp:2]: (error) printf format string has 1 parameters but only 0 are given\n"
|
||||||
"[test.cpp:3]: (error) printf format string has 2 parameters but only 1 are given\n"
|
"[test.cpp:3]: (error) printf format string has 2 parameters but only 1 are given\n"
|
||||||
|
@ -2216,7 +2216,8 @@ private:
|
||||||
"[test.cpp:6]: (error) printf format string has 3 parameters but only 2 are given\n"
|
"[test.cpp:6]: (error) printf format string has 3 parameters but only 2 are given\n"
|
||||||
"[test.cpp:7]: (error) fprintf format string has 2 parameters but only 0 are given\n"
|
"[test.cpp:7]: (error) fprintf format string has 2 parameters but only 0 are given\n"
|
||||||
"[test.cpp:8]: (error) snprintf format string has 2 parameters but only 0 are given\n"
|
"[test.cpp:8]: (error) snprintf format string has 2 parameters but only 0 are given\n"
|
||||||
"[test.cpp:9]: (error) sprintf format string has 3 parameters but only 2 are given\n", errout.str());
|
"[test.cpp:9]: (error) sprintf format string has 3 parameters but only 2 are given\n"
|
||||||
|
"[test.cpp:10]: (error) snprintf format string has 2 parameters but only 1 are given\n", errout.str());
|
||||||
|
|
||||||
check("void foo(char *str) {\n"
|
check("void foo(char *str) {\n"
|
||||||
" printf(\"\", 0);\n"
|
" printf(\"\", 0);\n"
|
||||||
|
@ -2239,6 +2240,7 @@ private:
|
||||||
" fprintf(stderr, \"error: %m\n\");\n" // #3339
|
" fprintf(stderr, \"error: %m\n\");\n" // #3339
|
||||||
" printf(\"string: %.*s\n\", len, string);\n" // #3311
|
" printf(\"string: %.*s\n\", len, string);\n" // #3311
|
||||||
" fprintf(stderr, \"%*cText.\n\", indent, ' ');\n" // #3313
|
" fprintf(stderr, \"%*cText.\n\", indent, ' ');\n" // #3313
|
||||||
|
" sprintf(string1, \"%*\", 32);\n" // #3364
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
@ -4138,6 +4140,30 @@ private:
|
||||||
" }"
|
" }"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (warning) Comparison of identical string variables.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:3]: (warning) Comparison of identical string variables.\n", errout.str());
|
||||||
|
|
||||||
|
check_preprocess_suppress(
|
||||||
|
"int main() {\n"
|
||||||
|
" if (\"str\" == \"str\") {\n"
|
||||||
|
" std::cout << \"Equal\n\"\n"
|
||||||
|
" }\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of always identical static strings.\n", errout.str());
|
||||||
|
|
||||||
|
check_preprocess_suppress(
|
||||||
|
"int main() {\n"
|
||||||
|
" if (\"str\" != \"str\") {\n"
|
||||||
|
" std::cout << \"Equal\n\"\n"
|
||||||
|
" }\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of always identical static strings.\n", errout.str());
|
||||||
|
|
||||||
|
check_preprocess_suppress(
|
||||||
|
"int main() {\n"
|
||||||
|
" if (a+\"str\" != \"str\"+b) {\n"
|
||||||
|
" std::cout << \"Equal\n\"\n"
|
||||||
|
" }\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkStrncmpSizeof() {
|
void checkStrncmpSizeof() {
|
||||||
|
|
Loading…
Reference in New Issue