diff --git a/cfg/posix.cfg b/cfg/posix.cfg index fbd976e4e..52d3d6f33 100644 --- a/cfg/posix.cfg +++ b/cfg/posix.cfg @@ -39,6 +39,30 @@ + + + + + false + + + + + + + + + + + + + false + + + + + + @@ -116,6 +140,43 @@ + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + false diff --git a/externals/simplecpp/simplecpp.cpp b/externals/simplecpp/simplecpp.cpp index 7845cebfe..9c6299dda 100644 --- a/externals/simplecpp/simplecpp.cpp +++ b/externals/simplecpp/simplecpp.cpp @@ -88,6 +88,22 @@ bool endsWith(const std::string &s, const std::string &e) { bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2) { return tok1 && tok2 && tok1->location.sameline(tok2->location); } + + +static bool isAlternativeBinaryOp(const simplecpp::Token *tok, const std::string &alt) { + return (tok->name && + tok->str == alt && + tok->previous && + tok->next && + (tok->previous->number || tok->previous->name || tok->previous->op == ')') && + (tok->next->number || tok->next->name || tok->next->op == '(')); +} + +static bool isAlternativeUnaryOp(const simplecpp::Token *tok, const std::string &alt) { + return ((tok->name && tok->str == alt) && + (!tok->previous || tok->previous->op == '(') && + (tok->next && (tok->next->name || tok->next->number))); +} } void simplecpp::Location::adjust(const std::string &str) { @@ -533,7 +549,12 @@ void simplecpp::TokenList::combineOperators() { } void simplecpp::TokenList::constFoldUnaryNotPosNeg(simplecpp::Token *tok) { + const std::string NOT("not"); for (; tok && tok->op != ')'; tok = tok->next) { + // "not" might be ! + if (isAlternativeUnaryOp(tok, NOT)) + tok->op = '!'; + if (tok->op == '!' && tok->next && tok->next->number) { tok->setstr(tok->next->str == "0" ? "1" : "0"); deleteToken(tok->next); @@ -612,7 +633,12 @@ void simplecpp::TokenList::constFoldAddSub(Token *tok) { } void simplecpp::TokenList::constFoldComparison(Token *tok) { + const std::string NOTEQ("not_eq"); + for (; tok && tok->op != ')'; tok = tok->next) { + if (isAlternativeBinaryOp(tok,NOTEQ)) + tok->setstr("!="); + if (!tok->startsWithOneOf("<>=!")) continue; if (!tok->previous || !tok->previous->number) @@ -655,7 +681,7 @@ void simplecpp::TokenList::constFoldBitwise(Token *tok) else altop = "xor"; for (tok = tok1; tok && tok->op != ')'; tok = tok->next) { - if (tok->op != *op && tok->str != altop) + if (tok->op != *op && !isAlternativeBinaryOp(tok, altop)) continue; if (!tok->previous || !tok->previous->number) continue; @@ -677,8 +703,17 @@ void simplecpp::TokenList::constFoldBitwise(Token *tok) } void simplecpp::TokenList::constFoldLogicalOp(Token *tok) { + const std::string AND("and"); + const std::string OR("or"); + for (; tok && tok->op != ')'; tok = tok->next) { - if (tok->str != "&&" && tok->str != "||" && tok->str != "and" && tok->str != "or") + if (tok->name) { + if (isAlternativeBinaryOp(tok,AND)) + tok->setstr("&&"); + else if (isAlternativeBinaryOp(tok,OR)) + tok->setstr("||"); + } + if (tok->str != "&&" && tok->str != "||") continue; if (!tok->previous || !tok->previous->number) continue; @@ -686,7 +721,7 @@ void simplecpp::TokenList::constFoldLogicalOp(Token *tok) { continue; int result; - if (tok->str == "||" || tok->str == "or") + if (tok->str == "||") result = (stringToLL(tok->previous->str) || stringToLL(tok->next->str)); else /*if (tok->str == "&&")*/ result = (stringToLL(tok->previous->str) && stringToLL(tok->next->str)); @@ -989,14 +1024,14 @@ private: return ~0U; } - std::vector getMacroParameters(const Token *nameToken, bool def) const { + std::vector getMacroParameters(const Token *nameToken, bool calledInDefine) const { if (!nameToken->next || nameToken->next->op != '(' || !functionLike()) return std::vector(); std::vector parametertokens; parametertokens.push_back(nameToken->next); unsigned int par = 0U; - for (const Token *tok = nameToken->next->next; def ? sameline(tok,nameToken) : (tok != NULL); tok = tok->next) { + for (const Token *tok = nameToken->next->next; calledInDefine ? sameline(tok,nameToken) : (tok != NULL); tok = tok->next) { if (tok->op == '(') ++par; else if (tok->op == ')') { @@ -1023,16 +1058,26 @@ private: unsigned int par = 0; const Token *tok = lpar; while (sameline(lpar, tok)) { - if (!expandArg(tokens, tok, tok->location, macros, expandedmacros1, expandedmacros, parametertokens)) - tokens->push_back(new Token(*tok)); - if (tok->op == '(') - ++par; - else if (tok->op == ')') { - --par; - if (par == 0U) - break; + if (tok->op == '#' && sameline(tok,tok->next) && tok->next->op == '#' && sameline(tok,tok->next->next)) { + // A##B => AB + const std::string strB(expandArgStr(tok->next->next, parametertokens)); + if (variadic && strB.empty() && tok->previous->op == ',') + tokens->deleteToken(tokens->back()); + else + tokens->back()->setstr(tokens->back()->str + strB); + tok = tok->next->next->next; + } else { + if (!expandArg(tokens, tok, tok->location, macros, expandedmacros1, expandedmacros, parametertokens)) + tokens->push_back(new Token(*tok)); + if (tok->op == '(') + ++par; + else if (tok->op == ')') { + --par; + if (par == 0U) + break; + } + tok = tok->next; } - tok = tok->next; } return sameline(lpar,tok) ? tok : NULL; } @@ -1056,7 +1101,10 @@ private: return nameToken->next; } - std::vector parametertokens1(getMacroParameters(nameToken, !expandedmacros1.empty())); + const bool calledInDefine = (loc.fileIndex != nameToken->location.fileIndex || + loc.line < nameToken->location.line); + + std::vector parametertokens1(getMacroParameters(nameToken, calledInDefine)); if (functionLike()) { // No arguments => not macro expansion @@ -1144,8 +1192,6 @@ private: if (variadic && strAB == "," && tok->previous->previous->str == "," && args.size() >= 1U && tok->next->str == args[args.size()-1U]) removeComma = true; - tok = tok->next->next; - output->deleteToken(A); if (!removeComma) { @@ -1154,14 +1200,22 @@ private: // TODO: For functionLike macros, push the (...) expandToken(output, loc, tokens.cfront(), macros, expandedmacros1, expandedmacros, parametertokens2); } + + tok = tok->next->next; } else { // #123 => "123" TokenList tokenListHash(files); tok = expandToken(&tokenListHash, loc, tok, macros, expandedmacros1, expandedmacros, parametertokens2); - std::string s; - for (const Token *hashtok = tokenListHash.cfront(); hashtok; hashtok = hashtok->next) - s += hashtok->str; - output->push_back(newMacroToken('\"' + s + '\"', loc, expandedmacros1.empty())); + std::ostringstream ostr; + for (const Token *hashtok = tokenListHash.cfront(); hashtok; hashtok = hashtok->next) { + for (unsigned int i = 0; i < hashtok->str.size(); i++) { + unsigned char c = hashtok->str[i]; + if (c == '\"' || c == '\\' || c == '\'') + ostr << '\\'; + ostr << c; + } + } + output->push_back(newMacroToken('\"' + ostr.str() + '\"', loc, expandedmacros1.empty())); } } @@ -1386,15 +1440,18 @@ void simplifyName(simplecpp::TokenList &expr) { altop.insert("or"); altop.insert("bitand"); altop.insert("bitor"); + altop.insert("not"); + altop.insert("not_eq"); altop.insert("xor"); for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { if (tok->name) { if (altop.find(tok->str) != altop.end()) { - bool alt = true; - if (!tok->previous || !tok->next) - alt = false; - if (!(tok->previous->number || tok->previous->op == ')')) - alt = false; + bool alt; + if (tok->str == "not") { + alt = isAlternativeUnaryOp(tok,tok->str); + } else { + alt = isAlternativeBinaryOp(tok,tok->str); + } if (alt) continue; } @@ -1639,20 +1696,38 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL const bool systemheader = (rawtok->next->str[0] == '<'); const std::string header(rawtok->next->str.substr(1U, rawtok->next->str.size() - 2U)); const std::string header2 = getFileName(filedata, rawtok->location.file(), header, dui, systemheader); - if (!header2.empty() && pragmaOnce.find(header2) == pragmaOnce.end()) { - includetokenstack.push(gotoNextLine(rawtok)); - const TokenList *includetokens = filedata.find(header2)->second; - rawtok = includetokens ? includetokens->cfront() : 0; - continue; - } else { + if (header2.empty()) { simplecpp::Output output(files); - output.type = Output::MISSING_INCLUDE; + output.type = Output::MISSING_HEADER; output.location = rawtok->location; output.msg = "Header not found: " + rawtok->next->str; if (outputList) outputList->push_back(output); + } else if (includetokenstack.size() >= 400) { + simplecpp::Output out(files); + out.type = Output::INCLUDE_NESTED_TOO_DEEPLY; + out.location = rawtok->location; + out.msg = "#include nested too deeply"; + if (outputList) + outputList->push_back(out); + } else if (pragmaOnce.find(header2) == pragmaOnce.end()) { + includetokenstack.push(gotoNextLine(rawtok)); + const TokenList *includetokens = filedata.find(header2)->second; + rawtok = includetokens ? includetokens->cfront() : 0; + continue; } } else if (rawtok->str == IF || rawtok->str == IFDEF || rawtok->str == IFNDEF || rawtok->str == ELIF) { + if (!sameline(rawtok,rawtok->next)) { + simplecpp::Output out(files); + out.type = Output::SYNTAX_ERROR; + out.location = rawtok->location; + out.msg = "Syntax error in #" + rawtok->str; + if (outputList) + outputList->push_back(out); + output.clear(); + return; + } + bool conditionIsTrue; if (ifstates.top() == ALWAYS_FALSE || (ifstates.top() == ELSE_IS_TRUE && rawtok->str != ELIF)) conditionIsTrue = false; @@ -1691,7 +1766,7 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL it->second.expand(&value, tok, macros, files); } catch (Macro::Error &err) { Output out(rawtok->location.files); - out.type = Output::ERROR; + out.type = Output::SYNTAX_ERROR; out.location = err.location; out.msg = "failed to expand \'" + tok->str + "\', " + err.what; if (outputList) @@ -1822,3 +1897,9 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL } } } + +void simplecpp::cleanup(std::map &filedata) { + for (std::map::iterator it = filedata.begin(); it != filedata.end(); ++it) + delete it->second; + filedata.clear(); +} diff --git a/externals/simplecpp/simplecpp.h b/externals/simplecpp/simplecpp.h index d283c0c15..2fc09646b 100644 --- a/externals/simplecpp/simplecpp.h +++ b/externals/simplecpp/simplecpp.h @@ -156,7 +156,9 @@ struct SIMPLECPP_LIB Output { enum Type { ERROR, /* #error */ WARNING, /* #warning */ - MISSING_INCLUDE + MISSING_HEADER, + INCLUDE_NESTED_TOO_DEEPLY, + SYNTAX_ERROR } type; Location location; std::string msg; @@ -275,19 +277,21 @@ SIMPLECPP_LIB std::map load(const TokenList &rawtokens, /** * Preprocess - * - * Preprocessing is done in two steps currently: - * const simplecpp::TokenList tokens1 = simplecpp::TokenList(f); - * const simplecpp::TokenList tokens2 = simplecpp::preprocess(tokens1, defines); - * - * The "tokens1" will contain tokens for comments and for preprocessor directives. And there is no preprocessing done. - * This "tokens1" can be used if you need to see what comments/directives there are. Or what code is hidden in #if. - * - * The "tokens2" will have normal preprocessor output. No comments nor directives are seen. - * * @todo simplify interface + * @param output TokenList that receives the preprocessing output + * @param rawtokens Raw tokenlist for top sourcefile + * @param files internal data of simplecpp + * @param filedata output from simplecpp::load() + * @param dui defines, undefs, and include paths + * @param outputList output: list that will receive output messages + * @param macroUsage output: macro usage */ SIMPLECPP_LIB void preprocess(TokenList &output, const TokenList &rawtokens, std::vector &files, const std::map &filedata, const DUI &dui, OutputList *outputList = 0, std::list *macroUsage = 0); + +/** + * Deallocate data + */ +SIMPLECPP_LIB void cleanup(std::map &filedata); } #endif diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 4defd6abd..861088d31 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -178,35 +178,40 @@ bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2 if (tok1->str() == "(" && tok1->previous() && !tok1->previous()->isName()) { // cast => assert that the casts are equal const Token *t1 = tok1->next(); const Token *t2 = tok2->next(); - while (t1 && t2 && t1->str() == t2->str() && (t1->isName() || t1->str() == "*")) { + while (t1 && t2 && + t1->str() == t2->str() && + t1->isLong() == t2->isLong() && + t1->isUnsigned() == t2->isUnsigned() && + t1->isSigned() == t2->isSigned() && + (t1->isName() || t1->str() == "*")) { t1 = t1->next(); t2 = t2->next(); } if (!t1 || !t2 || t1->str() != ")" || t2->str() != ")") return false; } - bool noncommuative_equals = + bool noncommutativeEquals = isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand1(), constFunctions); - noncommuative_equals = noncommuative_equals && + noncommutativeEquals = noncommutativeEquals && isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand2(), constFunctions); - if (noncommuative_equals) + if (noncommutativeEquals) return true; const bool commutative = tok1->astOperand1() && tok1->astOperand2() && Token::Match(tok1, "%or%|%oror%|+|*|&|&&|^|==|!="); - bool commuative_equals = commutative && + bool commutativeEquals = commutative && isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand1(), constFunctions); - commuative_equals = commuative_equals && + commutativeEquals = commutativeEquals && isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand2(), constFunctions); // in c++, "a"+b might be different to b+"a" - if (cpp && commuative_equals && tok1->str() == "+" && + if (cpp && commutativeEquals && tok1->str() == "+" && (tok1->astOperand1()->tokType() == Token::eString || tok1->astOperand2()->tokType() == Token::eString)) { const Token * const other = tok1->astOperand1()->tokType() != Token::eString ? tok1->astOperand1() : tok1->astOperand2(); return other && astIsIntegral(other,false); } - return commuative_equals; + return commutativeEquals; } bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token * const cond2, const std::set &constFunctions) diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index cd747f8e1..5f7be4af2 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -1777,7 +1777,7 @@ CheckBufferOverrun::ArrayInfo::ArrayInfo(const Variable *var, const SymbolDataba _num.push_back(var->dimension(i)); if (var->typeEndToken()->str() == "*") _element_size = symDb->sizeOfType(var->typeEndToken()); - else if (var->typeStartToken()->str() == "struct") + else if (var->typeStartToken()->strAt(-1) == "struct") _element_size = 100; else { _element_size = symDb->sizeOfType(var->typeEndToken()); diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 65eac6a33..730aad85c 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -1438,15 +1438,17 @@ bool CheckClass::hasAllocation(const Function *func, const Scope* scope) const return true; // check for deallocating memory - const Token *var = nullptr; + const Token *var; if (Token::Match(tok, "free ( %var%")) var = tok->tokAt(2); else if (Token::Match(tok, "delete [ ] %var%")) var = tok->tokAt(3); else if (Token::Match(tok, "delete %var%")) var = tok->next(); + else + continue; // Check for assignment to the deleted pointer (only if its a member of the class) - if (var && isMemberVar(scope, var)) { + if (isMemberVar(scope, var)) { for (const Token *tok1 = var->next(); tok1 && (tok1 != last); tok1 = tok1->next()) { if (Token::Match(tok1, "%varid% =", var->varId())) return true; diff --git a/lib/checkfunctions.cpp b/lib/checkfunctions.cpp index e7fac6dcc..2811658cc 100644 --- a/lib/checkfunctions.cpp +++ b/lib/checkfunctions.cpp @@ -33,7 +33,7 @@ namespace { } static const CWE CWE252(252U); // Unchecked Return Value -static const CWE CWE466(447U); // Use of Obsolete Functions +static const CWE CWE477(477U); // Use of Obsolete Functions static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments @@ -68,7 +68,7 @@ void CheckFunctions::checkProhibitedFunctions() const Library::WarnInfo* wi = _settings->library.getWarnInfo(tok); if (wi) { if (_settings->isEnabled(Severity::toString(wi->severity)) && _settings->standards.c >= wi->standards.c && _settings->standards.cpp >= wi->standards.cpp) { - reportError(tok, wi->severity, tok->str() + "Called", wi->message); + reportError(tok, wi->severity, tok->str() + "Called", wi->message, CWE477, false); } } } diff --git a/lib/checkio.cpp b/lib/checkio.cpp index ecbda6504..8bb531634 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -34,11 +34,13 @@ namespace { } // CVE ID used: +static const CWE CWE119(119U); // Improper Restriction of Operations within the Bounds of a Memory Buffer static const CWE CWE398(398U); // Indicator of Poor Code Quality -static const CWE CWE664(664U); -static const CWE CWE685(685U); -static const CWE CWE687(687U); -static const CWE CWE910(910U); +static const CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime +static const CWE CWE685(685U); // Function Call With Incorrect Number of Arguments +static const CWE CWE686(686U); // Function Call With Incorrect Argument Type +static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value +static const CWE CWE910(910U); // Use of Expired File Descriptor //--------------------------------------------------------------------------- // std::cout << std::cout; @@ -426,8 +428,7 @@ void CheckIO::invalidScanfError(const Token *tok) reportError(tok, Severity::warning, "invalidscanf", fname + "() without field width limits can crash with huge input data.\n" + fname + "() without field width limits can crash with huge input data. Add a field width " - "specifier to fix this problem:\n" - " %s => %20s\n" + "specifier to fix this problem.\n" "\n" "Sample program that can crash:\n" "\n" @@ -443,8 +444,8 @@ void CheckIO::invalidScanfError(const Token *tok) "here is 'scanf(\"%4s\", c);', as the maximum field width does not include the " "terminating null byte.\n" "Source: http://linux.die.net/man/3/scanf\n" - "Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/stdio/scanf.c" - ); + "Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/stdio/scanf.c", + CWE119, false); } //--------------------------------------------------------------------------- @@ -1779,7 +1780,7 @@ void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, cons } else { errmsg << "referencing parameter " << index << " while " << numFunction << " arguments given"; } - reportError(tok, Severity::warning, "wrongPrintfScanfParameterPositionError", errmsg.str()); + reportError(tok, Severity::warning, "wrongPrintfScanfParameterPositionError", errmsg.str(), CWE685, false); } void CheckIO::invalidScanfArgTypeError_s(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) @@ -1795,7 +1796,7 @@ void CheckIO::invalidScanfArgTypeError_s(const Token* tok, unsigned int numForma errmsg << " *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; - reportError(tok, Severity::warning, "invalidScanfArgType_s", errmsg.str()); + reportError(tok, Severity::warning, "invalidScanfArgType_s", errmsg.str(), CWE686, false); } void CheckIO::invalidScanfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned) { @@ -1839,7 +1840,7 @@ void CheckIO::invalidScanfArgTypeError_int(const Token* tok, unsigned int numFor errmsg << " *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; - reportError(tok, Severity::warning, "invalidScanfArgType_int", errmsg.str()); + reportError(tok, Severity::warning, "invalidScanfArgType_int", errmsg.str(), CWE686, false); } void CheckIO::invalidScanfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { @@ -1856,7 +1857,7 @@ void CheckIO::invalidScanfArgTypeError_float(const Token* tok, unsigned int numF errmsg << " *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; - reportError(tok, Severity::warning, "invalidScanfArgType_float", errmsg.str()); + reportError(tok, Severity::warning, "invalidScanfArgType_float", errmsg.str(), CWE686, false); } void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo) @@ -1867,7 +1868,7 @@ void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numForm errmsg << "%s in format string (no. " << numFormat << ") requires \'char *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; - reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str()); + reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str(), CWE686, false); } void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo) { @@ -1877,7 +1878,7 @@ void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numForm errmsg << "%n in format string (no. " << numFormat << ") requires \'int *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; - reportError(tok, Severity::warning, "invalidPrintfArgType_n", errmsg.str()); + reportError(tok, Severity::warning, "invalidPrintfArgType_n", errmsg.str(), CWE686, false); } void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo) { @@ -1887,7 +1888,7 @@ void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numForm errmsg << "%p in format string (no. " << numFormat << ") requires an address but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; - reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str()); + reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str(), CWE686, false); } static void printfFormatType(std::ostream& os, const std::string& specifier, bool isUnsigned) { @@ -1944,7 +1945,7 @@ void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numF errmsg << " but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; - reportError(tok, Severity::warning, "invalidPrintfArgType_uint", errmsg.str()); + reportError(tok, Severity::warning, "invalidPrintfArgType_uint", errmsg.str(), CWE686, false); } void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) @@ -1957,7 +1958,7 @@ void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numF errmsg << " but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; - reportError(tok, Severity::warning, "invalidPrintfArgType_sint", errmsg.str()); + reportError(tok, Severity::warning, "invalidPrintfArgType_sint", errmsg.str(), CWE686, false); } void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { @@ -1970,7 +1971,7 @@ void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int num errmsg << "double\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; - reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str()); + reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str(), CWE686, false); } void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 0d2fa2674..1e6bf5868 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -1472,7 +1472,7 @@ void CheckOther::checkPassByReference() while (tok3 && tok3->str() != "(") { if (tok3->link() && Token::Match(tok3, ")|]|}|>")) tok3 = tok3->link(); - else if (tok->link()) + else if (tok3->link()) break; else if (tok3->str() == ";") break; diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index f8130d9e7..2fca0f994 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -165,6 +165,20 @@ void CheckStl::iterators() if (itTok->previous()->str() == "*") continue; + // inserting iterator range.. + if (tok2->strAt(2) == "insert") { + const Token *par2 = itTok->nextArgument(); + while (par2 && par2->str() != ")") { + if (par2->varId() == container->declarationId()) + break; + if (par2->str() == "(") + par2 = par2->link(); + par2 = par2->next(); + } + if (par2->varId() == container->declarationId()) + continue; + } + // Show error message, mismatching iterator is used. iteratorsError(tok2, container->name(), tok2->str()); } diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index 7fa1a827e..0c9830b81 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -101,6 +101,10 @@ void CheckUninitVar::checkScope(const Scope* scope) if (!tok) continue; + if (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "for (") && + checkLoopBody(tok->astParent()->link()->next(), *i, i->isArray() ? ARRAY : NO_ALLOC, "", true)) + continue; + if (i->isArray()) { Alloc alloc = ARRAY; checkScopeForVariable(tok, *i, nullptr, nullptr, &alloc, ""); @@ -117,12 +121,12 @@ void CheckUninitVar::checkScope(const Scope* scope) if (scope->function) { for (unsigned int i = 0; i < scope->function->argCount(); i++) { const Variable *arg = scope->function->getArgumentVar(i); - if (arg && arg->declarationId() && Token::Match(arg->typeStartToken(), "struct| %type% * %name% [,)]")) { + if (arg && arg->declarationId() && Token::Match(arg->typeStartToken(), "%type% * %name% [,)]")) { // Treat the pointer as initialized until it is assigned by malloc for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "[;{}] %varid% = %name% (", arg->declarationId()) && _settings->library.returnuninitdata.count(tok->strAt(3)) == 1U) { - if (arg->typeStartToken()->str() == "struct") + if (arg->typeStartToken()->strAt(-1) == "struct" || (arg->type() && arg->type()->isStructType())) checkStruct(tok, *arg); else if (arg->typeStartToken()->isStandardType() || arg->typeStartToken()->isEnumType()) { Alloc alloc = NO_ALLOC; @@ -138,8 +142,6 @@ void CheckUninitVar::checkScope(const Scope* scope) void CheckUninitVar::checkStruct(const Token *tok, const Variable &structvar) { const Token *typeToken = structvar.typeStartToken(); - if (typeToken->str() == "struct") - typeToken = typeToken->next(); const SymbolDatabase * symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::size_t j = 0U; j < symbolDatabase->classAndStructScopes.size(); ++j) { const Scope *scope2 = symbolDatabase->classAndStructScopes[j]; @@ -918,9 +920,12 @@ bool CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer, Alloc al if (vartok->previous()->str() != "&" || !Token::Match(vartok->tokAt(-2), "[(,=?:]")) { if (alloc != NO_ALLOC && vartok->previous()->str() == "*") { + // TestUninitVar::isVariableUsageDeref() const Token *parent = vartok->previous()->astParent(); if (parent && parent->str() == "=" && parent->astOperand1() == vartok->previous()) return false; + if (vartok->variable() && vartok->variable()->dimensions().size() >= 2) + return false; return true; } return alloc == NO_ALLOC; @@ -1014,9 +1019,9 @@ int CheckUninitVar::isFunctionParUsage(const Token *vartok, bool pointer, Alloc const Variable *arg = func->getArgumentVar(argumentNumber); if (arg) { const Token *argStart = arg->typeStartToken(); - if (!address && !array && Token::Match(argStart, "struct| %type% %name%| [,)]")) + if (!address && !array && Token::Match(argStart, "%type% %name%| [,)]")) return 1; - if (pointer && !address && alloc == NO_ALLOC && Token::Match(argStart, "struct| %type% * %name% [,)]")) + if (pointer && !address && alloc == NO_ALLOC && Token::Match(argStart, "%type% * %name% [,)]")) return 1; while (argStart->previous() && argStart->previous()->isName()) argStart = argStart->previous(); diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 0c55b32a1..aa69badf4 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1072,6 +1072,11 @@ void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const } else if (Token::Match(tok, "[(,] (") && Token::Match(tok->next()->link(), ") %var% [,)]")) { variables.use(tok->next()->link()->next()->varId(), tok); // use = read + write + } else if (Token::Match(tok, "[(,] *| %var% =")) { + tok = tok->next(); + if (tok->str() == "*") + tok = tok->next(); + variables.use(tok->varId(), tok); } // function @@ -1265,6 +1270,17 @@ void CheckUnusedVar::checkStructMemberUsage() if (Token::findmatch(scope->classEnd, castPattern.c_str())) continue; + // Bail out if struct is used in sizeof.. + for (const Token *tok = scope->classEnd; nullptr != (tok = Token::findsimplematch(tok, "sizeof ("));) { + tok = tok->tokAt(2); + if (Token::Match(tok, ("struct| " + scope->className).c_str())) { + bailout = true; + break; + } + } + if (bailout) + continue; + // Try to prevent false positives when struct members are not used directly. if (Token::findmatch(scope->classEnd, (scope->className + " %type%| *").c_str())) continue; diff --git a/lib/library.cpp b/lib/library.cpp index 25b5cdf9c..711fe44a4 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -74,21 +74,28 @@ Library::Error Library::load(const char exename[], const char path[]) absolute_path = Path::getAbsoluteFilePath(fullfilename); } - if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) { - // Try to locate the library configuration in the installation folder.. + std::list cfgfolders; #ifdef CFGDIR - const std::string cfgfolder(CFGDIR); -#else - if (!exename) - return Error(FILE_NOT_FOUND); - const std::string cfgfolder(Path::fromNativeSeparators(Path::getPathFromFilename(exename)) + "cfg"); + cfgfolders.push_back(CFGDIR); #endif + if (exename) { + const std::string exepath(Path::fromNativeSeparators(Path::getPathFromFilename(exename))); + cfgfolders.push_back(exepath + "cfg"); + cfgfolders.push_back(exepath); + } + + while (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND && !cfgfolders.empty()) { + const std::string cfgfolder(cfgfolders.front()); + cfgfolders.pop_front(); const char *sep = (!cfgfolder.empty() && cfgfolder[cfgfolder.size()-1U]=='/' ? "" : "/"); const std::string filename(cfgfolder + sep + fullfilename); error = doc.LoadFile(filename.c_str()); if (error != tinyxml2::XML_ERROR_FILE_NOT_FOUND) absolute_path = Path::getAbsoluteFilePath(filename); } + + if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) + return Error(FILE_NOT_FOUND); } else absolute_path = Path::getAbsoluteFilePath(path); diff --git a/lib/library.h b/lib/library.h index 1cc07f8fd..02c965365 100644 --- a/lib/library.h +++ b/lib/library.h @@ -44,6 +44,8 @@ namespace tinyxml2 { * @brief Library definitions handling */ class CPPCHECKLIB Library { + friend class TestSymbolDatabase; // For testing only + public: Library(); diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 095ab627a..eab50e8bb 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -496,39 +496,23 @@ std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std return ""; case simplecpp::Output::WARNING: break; - case simplecpp::Output::MISSING_INCLUDE: { + case simplecpp::Output::MISSING_HEADER: { const std::string::size_type pos1 = it->msg.find_first_of("<\""); const std::string::size_type pos2 = it->msg.find_first_of(">\"", pos1 + 1U); if (pos1 < pos2 && pos2 != std::string::npos) missingInclude(it->location.file(), it->location.line, it->msg.substr(pos1+1, pos2-pos1-1), it->msg[pos1] == '\"' ? UserHeader : SystemHeader); } break; + case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: + case simplecpp::Output::SYNTAX_ERROR: + error(it->location.file(), it->location.line, it->msg); + return ""; }; } // ensure that guessed define macros without value are not used in the code - for (std::list::const_iterator defineIt = dui.defines.begin(); defineIt != dui.defines.end(); ++defineIt) { - if (defineIt->find("=") != std::string::npos) - continue; - const std::string macroName = defineIt->substr(0, std::min(defineIt->find("="), defineIt->find("("))); - for (std::list::const_iterator usageIt = macroUsage.begin(); usageIt != macroUsage.end(); ++usageIt) { - const simplecpp::MacroUsage &mu = *usageIt; - if (mu.macroName != macroName) - continue; - bool directiveLocation = false; - for (std::list::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) { - if (mu.useLocation.file() == dirIt->file && mu.useLocation.line == dirIt->linenr) { - directiveLocation = true; - break; - } - } - if (!directiveLocation) { - if (_settings.isEnabled("information")) - validateCfgError(cfg, macroName); - return ""; - } - } - } + if (!validateCfg(cfg, macroUsage)) + return ""; // assembler code locations.. std::set assemblerLocations; @@ -620,11 +604,13 @@ std::string Preprocessor::getcode(const std::string &filedata, const std::string for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) { switch (it->type) { case simplecpp::Output::ERROR: + case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: + case simplecpp::Output::SYNTAX_ERROR: error(it->location.file(), it->location.line, it->msg); return ""; case simplecpp::Output::WARNING: break; - case simplecpp::Output::MISSING_INCLUDE: { + case simplecpp::Output::MISSING_HEADER: { const std::string::size_type pos1 = it->msg.find_first_of("<\""); const std::string::size_type pos2 = it->msg.find_first_of(">\"", pos1 + 1U); if (pos1 < pos2 && pos2 != std::string::npos) @@ -684,72 +670,42 @@ void Preprocessor::missingInclude(const std::string &filename, unsigned int line } } -bool Preprocessor::validateCfg(const std::string &code, const std::string &cfg) +bool Preprocessor::validateCfg(const std::string &cfg, const std::list ¯oUsageList) { - const bool printInformation = _settings.isEnabled("information"); - - // fill up "macros" with empty configuration macros - std::set macros; - for (std::string::size_type pos = 0; pos < cfg.size();) { - const std::string::size_type pos2 = cfg.find_first_of(";=", pos); - if (pos2 == std::string::npos) { - macros.insert(cfg.substr(pos)); - break; - } - if (cfg[pos2] == ';') - macros.insert(cfg.substr(pos, pos2-pos)); - pos = cfg.find(';', pos2); - if (pos != std::string::npos) - ++pos; - } - - // check if any empty macros are used in code - for (std::set::const_iterator it = macros.begin(); it != macros.end(); ++it) { - const std::string ¯o = *it; - std::string::size_type pos = 0; - while ((pos = code.find_first_of(std::string("#\"'")+macro[0], pos)) != std::string::npos) { - const std::string::size_type pos1 = pos; - const std::string::size_type pos2 = pos + macro.size(); - pos++; - - // skip string.. - if (code[pos1] == '\"' || code[pos1] == '\'') { - while (pos < code.size() && code[pos] != code[pos1]) { - if (code[pos] == '\\') - ++pos; - ++pos; + bool ret = true; + std::list defines; + splitcfg(cfg, defines, std::string()); + for (std::list::const_iterator defineIt = defines.begin(); defineIt != defines.end(); ++defineIt) { + if (defineIt->find("=") != std::string::npos) + continue; + const std::string macroName(defineIt->substr(0, defineIt->find("("))); + for (std::list::const_iterator usageIt = macroUsageList.begin(); usageIt != macroUsageList.end(); ++usageIt) { + const simplecpp::MacroUsage &mu = *usageIt; + if (mu.macroName != macroName) + continue; + bool directiveLocation = false; + for (std::list::const_iterator dirIt = directives.begin(); dirIt != directives.end(); ++dirIt) { + if (mu.useLocation.file() == dirIt->file && mu.useLocation.line == dirIt->linenr) { + directiveLocation = true; + break; } - ++pos; } - - // skip preprocessor statement.. - else if (code[pos1] == '#') { - if (pos1 == 0 || code[pos1-1] == '\n') - pos = code.find('\n', pos); - } - - // is macro used in code? - else if (code.compare(pos1,macro.size(),macro) == 0) { - if (pos1 > 0 && (std::isalnum((unsigned char)code[pos1-1U]) || code[pos1-1U] == '_')) - continue; - if (pos2 < code.size() && (std::isalnum((unsigned char)code[pos2]) || code[pos2] == '_')) - continue; - // macro is used in code, return false - if (printInformation) - validateCfgError(cfg, macro); - return false; + if (!directiveLocation) { + if (_settings.isEnabled("information")) + validateCfgError(mu.useLocation.file(), mu.useLocation.line, cfg, macroName); + ret = false; } } } - return true; + return ret; } -void Preprocessor::validateCfgError(const std::string &cfg, const std::string ¯o) +void Preprocessor::validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string ¯o) { const std::string id = "ConfigurationNotChecked"; std::list locationList; - ErrorLogger::ErrorMessage::FileLocation loc(file0, 1); + ErrorLogger::ErrorMessage::FileLocation loc(file, line); locationList.push_back(loc); ErrorLogger::ErrorMessage errmsg(locationList, file0, Severity::information, "Skipping configuration '" + cfg + "' since the value of '" + macro + "' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.", id, false); _errorLogger->reportInfo(errmsg); @@ -762,7 +718,7 @@ void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *se settings2.checkConfiguration=true; preprocessor.missingInclude("", 1, "", UserHeader); preprocessor.missingInclude("", 1, "", SystemHeader); - preprocessor.validateCfgError("X", "X"); + preprocessor.validateCfgError("", 1, "X", "X"); preprocessor.error("", 1, "#error message"); // #error .. } diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 7bcce66b3..a1ba52978 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -153,12 +153,12 @@ public: /** * make sure empty configuration macros are not used in code. the given code must be a single configuration - * @param code The input code * @param cfg configuration + * @param macroUsageList macro usage list * @return true => configuration is valid */ - bool validateCfg(const std::string &code, const std::string &cfg); - void validateCfgError(const std::string &cfg, const std::string ¯o); + bool validateCfg(const std::string &cfg, const std::list ¯oUsageList); + void validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string ¯o); private: diff --git a/lib/settings.h b/lib/settings.h index 13532e351..a406dff0b 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -293,6 +293,23 @@ public: platformType == Win64; } + const char *platformString() const { + switch (platformType) { + case Unix32: + return "unix32"; + case Unix64: + return "unix64"; + case Win32A: + return "win32A"; + case Win32W: + return "win32W"; + case Win64: + return "win64"; + default: + return "unknown"; + } + } + /** * @brief return true if a file is to be excluded from configuration checking * @return true for the file to be excluded. diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 22bc731b8..17a3db837 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -1088,19 +1088,19 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti if (start != end && start->next() != end) { for (Token* tok = start->next(); tok != end; tok = tok->next()) { if (tok->str() == "{") { - bool break2 = false; + bool isEndOfScope = false; for (std::list::const_iterator innerScope = it->nestedList.begin(); innerScope != it->nestedList.end(); ++innerScope) { if (tok == (*innerScope)->classStart) { // Is begin of inner scope tok = tok->link(); if (!tok || tok->next() == end || !tok->next()) { - break2 = true; + isEndOfScope = true; break; } tok = tok->next(); break; } } - if (break2) + if (isEndOfScope) break; } tok->scope(&*it); @@ -1817,9 +1817,9 @@ bool Function::argsMatch(const Scope *scope, const Token *first, const Token *se second = second->next(); // skip "struct" - if (first->str() == "struct") + if (first->str() == "struct" || first->str() == "enum") first = first->next(); - if (second->str() == "struct") + if (second->str() == "struct" || second->str() == "enum") second = second->next(); // skip const on type passed by value @@ -2402,7 +2402,7 @@ void SymbolDatabase::printVariable(const Variable *var, const char *indent) cons std::cout << indent << " isStlType: " << var->isStlType() << std::endl; std::cout << indent << "_type: "; if (var->type()) { - std::cout << var->type()->name(); + std::cout << var->type()->type() << " " << var->type()->name(); std::cout << " " << _tokenizer->list.fileLine(var->type()->classDef); std::cout << " " << var->type() << std::endl; } else @@ -2466,6 +2466,7 @@ void SymbolDatabase::printOut(const char *title) const std::cout << " isOperator: " << func->isOperator() << std::endl; std::cout << " hasLvalRefQual: " << func->hasLvalRefQualifier() << std::endl; std::cout << " hasRvalRefQual: " << func->hasRvalRefQualifier() << std::endl; + std::cout << " isVariadic: " << func->isVariadic() << std::endl; std::cout << " attributes:"; if (func->isAttributeConst()) std::cout << " const "; @@ -2818,10 +2819,19 @@ void Function::addArguments(const SymbolDatabase *symbolDatabase, const Scope *s } while (tok->str() != "," && tok->str() != ")"); } + // skip over stuff before type + while (Token::Match(startTok, "enum|struct|const")) + startTok = startTok->next(); + argumentList.push_back(Variable(nameTok, startTok, endTok, count++, Argument, argType, functionScope, &symbolDatabase->_settings->library)); - if (tok->str() == ")") + if (tok->str() == ")") { + // check for a variadic function + if (Token::simpleMatch(startTok, ". . .")) + isVariadic(true); + break; + } } // count default arguments @@ -3177,6 +3187,10 @@ const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess, con const_cast(typetok)->type(vType); } + // skip "enum" or "struct" + if (Token::Match(typestart, "enum|struct")) + typestart = typestart->next(); + addVariable(vartok, typestart, vartok->previous(), varaccess, vType, this, lib); } @@ -3462,10 +3476,17 @@ const Type* SymbolDatabase::findVariableType(const Scope *start, const Token *ty } } - const Type * type = findVariableTypeInBase(scope, typeTok); + if (scope) { + const Type * type = scope->findType(typeTok->str()); - if (type) - return type; + if (type) + return type; + + type = findVariableTypeInBase(scope, typeTok); + + if (type) + return type; + } } std::list::const_iterator type; @@ -3490,8 +3511,27 @@ const Type* SymbolDatabase::findVariableType(const Scope *start, const Token *ty break; } - if (type->enclosingScope == parent) - return &(*type); + if (type->enclosingScope == parent) { + // check if "enum" specified and type is enum + if (typeTok->strAt(-1) == "enum") { + if (type->isEnumType()) + return &(*type); + else // not an enum + continue; + } + + // check if "struct" specified and type is struct + else if (typeTok->strAt(-1) == "struct") { + if (type->isStructType()) + return &(*type); + else // not a struct + continue; + } + + // "enum" or "struct" not specified so assume match + else + return &(*type); + } } // type has a namespace @@ -3584,7 +3624,9 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst) const const std::size_t args = arguments.size(); for (std::multimap::const_iterator it = functionMap.find(tok->str()); it != functionMap.end() && it->first == tok->str(); ++it) { const Function *func = it->second; - if (args == func->argCount() || (args < func->argCount() && args >= func->minArgCount())) { + if (args == func->argCount() || + (func->isVariadic() && args >= (func->argCount() - 1)) || + (args < func->argCount() && args >= func->minArgCount())) { matches.push_back(func); } } @@ -3598,6 +3640,10 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst) const const Function * func = matches[i]; size_t same = 0; for (std::size_t j = 0; j < args; ++j) { + // don't check variadic arguments + if (func->isVariadic() && j > (func->argCount() - 1)) { + break; + } const Variable *funcarg = func->getArgumentVar(j); // check for a match with a variable if (Token::Match(arguments[j], "%var% ,|)")) { @@ -3610,6 +3656,25 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst) const } } + // check for a match with address of a variable + else if (Token::Match(arguments[j], "& %var% ,|)")) { + const Variable * callarg = check->getVariableFromVarId(arguments[j]->next()->varId()); + if (callarg) { + if (funcarg->typeEndToken()->str() == "*" && + (funcarg->typeStartToken()->str() == "void" || + (callarg->typeStartToken()->str() == funcarg->typeStartToken()->str() && + callarg->typeStartToken()->isUnsigned() == funcarg->typeStartToken()->isUnsigned() && + callarg->typeStartToken()->isLong() == funcarg->typeStartToken()->isLong()))) { + same++; + } else { + // can't match so remove this function from possible matches + matches.erase(matches.begin() + i); + erased = true; + break; + } + } + } + // check for a match with a numeric literal else if (Token::Match(arguments[j], "%num% ,|)")) { if (MathLib::isInt(arguments[j]->str())) { @@ -3682,6 +3747,14 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst) const } } + // check for a match with a string literal + else if (Token::Match(arguments[j], "%str% ,|)") && + funcarg->typeStartToken() != funcarg->typeEndToken() && + ((!arguments[j]->isLong() && Token::simpleMatch(funcarg->typeStartToken(), "char *")) || + (arguments[j]->isLong() && Token::simpleMatch(funcarg->typeStartToken(), "wchar_t *")))) { + same++; + } + // check that function argument type is not mismatching else if (arguments[j]->str() == "&" && funcarg && funcarg->isReference()) { // can't match so remove this function from possible matches @@ -3692,7 +3765,8 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst) const } // check if all arguments matched - if (same == args) { + if ((func->isVariadic() && same == (func->argCount() - 1)) || + (!func->isVariadic() && same == args)) { if (requireConst && func->isConst()) return func; @@ -4099,21 +4173,21 @@ unsigned int SymbolDatabase::sizeOfType(const Token *type) const return size; } -static const Token * parsedecl(const Token *type, ValueType * const valuetype, ValueType::Sign defaultSignedness, const Library* lib); -static void setValueType(Token *tok, const ValueType &valuetype, bool cpp, ValueType::Sign defaultSignedness, const Library* lib); +static const Token * parsedecl(const Token *type, ValueType * const valuetype, ValueType::Sign defaultSignedness, const Settings* settings); +static void setValueType(Token *tok, const ValueType &valuetype, bool cpp, ValueType::Sign defaultSignedness, const Settings* settings); -static void setValueType(Token *tok, const Variable &var, bool cpp, ValueType::Sign defaultSignedness, const Library* lib) +static void setValueType(Token *tok, const Variable &var, bool cpp, ValueType::Sign defaultSignedness, const Settings* settings) { if (var.isStlType()) return; ValueType valuetype; valuetype.pointer = var.dimensions().size(); valuetype.typeScope = var.typeScope(); - if (parsedecl(var.typeStartToken(), &valuetype, defaultSignedness, lib)) - setValueType(tok, valuetype, cpp, defaultSignedness, lib); + if (parsedecl(var.typeStartToken(), &valuetype, defaultSignedness, settings)) + setValueType(tok, valuetype, cpp, defaultSignedness, settings); } -static void setValueType(Token *tok, const Enumerator &enumerator, bool cpp, ValueType::Sign defaultSignedness, const Library* lib) +static void setValueType(Token *tok, const Enumerator &enumerator, bool cpp, ValueType::Sign defaultSignedness, const Settings* settings) { ValueType valuetype; valuetype.typeScope = enumerator.scope; @@ -4135,22 +4209,18 @@ static void setValueType(Token *tok, const Enumerator &enumerator, bool cpp, Val else if (type->str() == "long") valuetype.type = type->isLong() ? ValueType::Type::LONGLONG : ValueType::Type::LONG; else if (type->isStandardType()) { - const Library::PodType* podtype = lib->podtype(type->str()); - if (podtype && (podtype->sign == 's' || podtype->sign == 'u')) { - valuetype.type = ValueType::Type::UNKNOWN_INT; - valuetype.sign = (podtype->sign == 'u') ? ValueType::UNSIGNED : ValueType::SIGNED; - } + valuetype.fromLibraryType(type->str(), settings); } - setValueType(tok, valuetype, cpp, defaultSignedness, lib); + setValueType(tok, valuetype, cpp, defaultSignedness, settings); } else { valuetype.sign = ValueType::SIGNED; valuetype.type = ValueType::INT; - setValueType(tok, valuetype, cpp, defaultSignedness, lib); + setValueType(tok, valuetype, cpp, defaultSignedness, settings); } } -static void setValueType(Token *tok, const ValueType &valuetype, bool cpp, ValueType::Sign defaultSignedness, const Library* lib) +static void setValueType(Token *tok, const ValueType &valuetype, bool cpp, ValueType::Sign defaultSignedness, const Settings* settings) { tok->setValueType(new ValueType(valuetype)); Token *parent = const_cast(tok->astParent()); @@ -4164,32 +4234,32 @@ static void setValueType(Token *tok, const ValueType &valuetype, bool cpp, Value if (vt1 && Token::Match(parent, "<<|>>")) { if (!cpp || (vt2 && vt2->isIntegral())) - setValueType(parent, *vt1, cpp, defaultSignedness, lib); + setValueType(parent, *vt1, cpp, defaultSignedness, settings); return; } if (parent->isAssignmentOp()) { if (vt1) - setValueType(parent, *vt1, cpp, defaultSignedness, lib); + setValueType(parent, *vt1, cpp, defaultSignedness, settings); return; } if (parent->str() == "[" && (!cpp || parent->astOperand1() == tok) && valuetype.pointer > 0U) { ValueType vt(valuetype); vt.pointer -= 1U; - setValueType(parent, vt, cpp, defaultSignedness, lib); + setValueType(parent, vt, cpp, defaultSignedness, settings); return; } if (parent->str() == "*" && !parent->astOperand2() && valuetype.pointer > 0U) { ValueType vt(valuetype); vt.pointer -= 1U; - setValueType(parent, vt, cpp, defaultSignedness, lib); + setValueType(parent, vt, cpp, defaultSignedness, settings); return; } if (parent->str() == "&" && !parent->astOperand2()) { ValueType vt(valuetype); vt.pointer += 1U; - setValueType(parent, vt, cpp, defaultSignedness, lib); + setValueType(parent, vt, cpp, defaultSignedness, settings); return; } @@ -4209,7 +4279,7 @@ static void setValueType(Token *tok, const ValueType &valuetype, bool cpp, Value } } if (var) - setValueType(parent, *var, cpp, defaultSignedness, lib); + setValueType(parent, *var, cpp, defaultSignedness, settings); return; } @@ -4224,33 +4294,33 @@ static void setValueType(Token *tok, const ValueType &valuetype, bool cpp, Value if (ternary || parent->isArithmeticalOp() || parent->tokType() == Token::eIncDecOp) { if (vt1->pointer != 0U && vt2 && vt2->pointer == 0U) { - setValueType(parent, *vt1, cpp, defaultSignedness, lib); + setValueType(parent, *vt1, cpp, defaultSignedness, settings); return; } if (vt1->pointer == 0U && vt2 && vt2->pointer != 0U) { - setValueType(parent, *vt2, cpp, defaultSignedness, lib); + setValueType(parent, *vt2, cpp, defaultSignedness, settings); return; } if (vt1->pointer != 0U) { if (ternary || parent->tokType() == Token::eIncDecOp) // result is pointer - setValueType(parent, *vt1, cpp, defaultSignedness, lib); + setValueType(parent, *vt1, cpp, defaultSignedness, settings); else // result is pointer diff - setValueType(parent, ValueType(ValueType::Sign::SIGNED, ValueType::Type::INT, 0U, 0U, "ptrdiff_t"), cpp, defaultSignedness, lib); + setValueType(parent, ValueType(ValueType::Sign::SIGNED, ValueType::Type::INT, 0U, 0U, "ptrdiff_t"), cpp, defaultSignedness, settings); return; } if (vt1->type == ValueType::Type::LONGDOUBLE || (vt2 && vt2->type == ValueType::Type::LONGDOUBLE)) { - setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::LONGDOUBLE, 0U), cpp, defaultSignedness, lib); + setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::LONGDOUBLE, 0U), cpp, defaultSignedness, settings); return; } if (vt1->type == ValueType::Type::DOUBLE || (vt2 && vt2->type == ValueType::Type::DOUBLE)) { - setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::DOUBLE, 0U), cpp, defaultSignedness, lib); + setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::DOUBLE, 0U), cpp, defaultSignedness, settings); return; } if (vt1->type == ValueType::Type::FLOAT || (vt2 && vt2->type == ValueType::Type::FLOAT)) { - setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::FLOAT, 0U), cpp, defaultSignedness, lib); + setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::FLOAT, 0U), cpp, defaultSignedness, settings); return; } } @@ -4284,12 +4354,12 @@ static void setValueType(Token *tok, const ValueType &valuetype, bool cpp, Value vt.originalTypeName.clear(); } - setValueType(parent, vt, cpp, defaultSignedness, lib); + setValueType(parent, vt, cpp, defaultSignedness, settings); return; } } -static const Token * parsedecl(const Token *type, ValueType * const valuetype, ValueType::Sign defaultSignedness, const Library* lib) +static const Token * parsedecl(const Token *type, ValueType * const valuetype, ValueType::Sign defaultSignedness, const Settings* settings) { const unsigned int pointer0 = valuetype->pointer; while (Token::Match(type->previous(), "%name%")) @@ -4333,13 +4403,8 @@ static const Token * parsedecl(const Token *type, ValueType * const valuetype, V return nullptr; else if (type->str() == "*") valuetype->pointer++; - else if (type->isStandardType()) { - const Library::PodType* podtype = lib->podtype(type->str()); - if (podtype && (podtype->sign == 's' || podtype->sign == 'u')) { - valuetype->type = ValueType::Type::UNKNOWN_INT; - valuetype->sign = (podtype->sign == 'u') ? ValueType::UNSIGNED : ValueType::SIGNED; - } - } + else if (type->isStandardType()) + valuetype->fromLibraryType(type->str(), settings); if (!type->originalName().empty()) valuetype->originalTypeName = type->originalName(); type = type->next(); @@ -4386,12 +4451,12 @@ static const Function *getOperatorFunction(const Token * const tok) return nullptr; } -void SymbolDatabase::setValueTypeInTokenList(Token *tokens, bool cpp, char defaultSignedness, const Library* lib) +void SymbolDatabase::setValueTypeInTokenList(Token *tokens, bool cpp, const Settings* settings) { ValueType::Sign defsign; - if (defaultSignedness == 's' || defaultSignedness == 'S') + if (settings->defaultSign == 's' || settings->defaultSign == 'S') defsign = ValueType::SIGNED; - else if (defaultSignedness == 'u' || defaultSignedness == 'U') + else if (settings->defaultSign == 'u' || settings->defaultSign == 'U') defsign = ValueType::UNSIGNED; else defsign = ValueType::UNKNOWN_SIGN; @@ -4406,7 +4471,7 @@ void SymbolDatabase::setValueTypeInTokenList(Token *tokens, bool cpp, char defau const char suffix = tok->str()[tok->str().size() - 1U]; if (suffix == 'f' || suffix == 'F') type = ValueType::Type::FLOAT; - ::setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, type, 0U), cpp, defsign, lib); + ::setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, type, 0U), cpp, defsign, settings); } else if (MathLib::isInt(tok->str())) { ValueType::Sign sign = ValueType::Sign::SIGNED; ValueType::Type type = ValueType::Type::INT; @@ -4423,71 +4488,117 @@ void SymbolDatabase::setValueTypeInTokenList(Token *tokens, bool cpp, char defau pos -= 2; } else break; } - ::setValueType(tok, ValueType(sign, type, 0U), cpp, defsign, lib); + ::setValueType(tok, ValueType(sign, type, 0U), cpp, defsign, settings); } } else if (tok->isComparisonOp() || tok->tokType() == Token::eLogicalOp) { if (cpp && tok->isComparisonOp() && (getClassScope(tok->astOperand1()) || getClassScope(tok->astOperand2()))) { const Function *function = getOperatorFunction(tok); if (function) { ValueType vt; - parsedecl(function->retDef, &vt, defsign, lib); - ::setValueType(tok, vt, cpp, defsign, lib); + parsedecl(function->retDef, &vt, defsign, settings); + ::setValueType(tok, vt, cpp, defsign, settings); continue; } } - ::setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U), cpp, defsign, lib); + ::setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U), cpp, defsign, settings); } else if (tok->tokType() == Token::eChar) - ::setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, 0U), cpp, defsign, lib); + ::setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, 0U), cpp, defsign, settings); else if (tok->tokType() == Token::eString) { ValueType valuetype(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, 1U, 1U); if (tok->isLong()) { valuetype.originalTypeName = "wchar_t"; valuetype.type = ValueType::Type::SHORT; } - ::setValueType(tok, valuetype, cpp, defsign, lib); + ::setValueType(tok, valuetype, cpp, defsign, settings); } else if (tok->str() == "(") { // cast if (!tok->astOperand2() && Token::Match(tok, "( %name%")) { ValueType valuetype; - if (Token::simpleMatch(parsedecl(tok->next(), &valuetype, defsign, lib), ")")) - ::setValueType(tok, valuetype, cpp, defsign, lib); + if (Token::simpleMatch(parsedecl(tok->next(), &valuetype, defsign, settings), ")")) + ::setValueType(tok, valuetype, cpp, defsign, settings); } // C++ cast if (tok->astOperand2() && Token::Match(tok->astOperand1(), "static_cast|const_cast|dynamic_cast|reinterpret_cast < %name%") && tok->astOperand1()->linkAt(1)) { ValueType valuetype; - if (Token::simpleMatch(parsedecl(tok->astOperand1()->tokAt(2), &valuetype, defsign, lib), ">")) - ::setValueType(tok, valuetype, cpp, defsign, lib); + if (Token::simpleMatch(parsedecl(tok->astOperand1()->tokAt(2), &valuetype, defsign, settings), ">")) + ::setValueType(tok, valuetype, cpp, defsign, settings); } // function else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->retDef) { ValueType valuetype; - if (parsedecl(tok->previous()->function()->retDef, &valuetype, defsign, lib)) - ::setValueType(tok, valuetype, cpp, defsign, lib); + if (parsedecl(tok->previous()->function()->retDef, &valuetype, defsign, settings)) + ::setValueType(tok, valuetype, cpp, defsign, settings); } else if (Token::simpleMatch(tok->previous(), "sizeof (")) { // TODO: use specified size_t type ValueType valuetype(ValueType::Sign::UNSIGNED, ValueType::Type::LONG, 0U); valuetype.originalTypeName = "size_t"; - setValueType(tok, valuetype, cpp, defsign, lib); + setValueType(tok, valuetype, cpp, defsign, settings); if (Token::Match(tok, "( %type% %type%| *| *| )")) { ValueType vt; - if (parsedecl(tok->next(), &vt, defsign, lib)) { - setValueType(tok->next(), vt, cpp, defsign, lib); + if (parsedecl(tok->next(), &vt, defsign, settings)) { + setValueType(tok->next(), vt, cpp, defsign, settings); } } } } else if (tok->variable()) { - setValueType(tok, *tok->variable(), cpp, defsign, lib); + setValueType(tok, *tok->variable(), cpp, defsign, settings); } else if (tok->enumerator()) { - setValueType(tok, *tok->enumerator(), cpp, defsign, lib); + setValueType(tok, *tok->enumerator(), cpp, defsign, settings); } } } +bool ValueType::fromLibraryType(const std::string &typestr, const Settings *settings) +{ + const Library::PodType* podtype = settings->library.podtype(typestr); + if (podtype && (podtype->sign == 's' || podtype->sign == 'u')) { + if (podtype->size == 1) + type = ValueType::Type::CHAR; + else if (podtype->size == settings->sizeof_int) + type = ValueType::Type::INT; + else if (podtype->size == settings->sizeof_short) + type = ValueType::Type::SHORT; + else if (podtype->size == settings->sizeof_long) + type = ValueType::Type::LONG; + else if (podtype->size == settings->sizeof_long_long) + type = ValueType::Type::LONGLONG; + else + type = ValueType::Type::UNKNOWN_INT; + sign = (podtype->sign == 'u') ? ValueType::UNSIGNED : ValueType::SIGNED; + return true; + } + + const Library::PlatformType *platformType = settings->library.platform_type(typestr, settings->platformString()); + if (platformType) { + if (platformType->_type == "char") + type = ValueType::Type::CHAR; + else if (platformType->_type == "short") + type = ValueType::Type::SHORT; + else if (platformType->_type == "int") + type = platformType->_long ? ValueType::Type::LONG : ValueType::Type::INT; + else if (platformType->_type == "long") + type = platformType->_long ? ValueType::Type::LONGLONG : ValueType::Type::LONG; + if (platformType->_signed) + sign = ValueType::SIGNED; + else if (platformType->_unsigned) + sign = ValueType::UNSIGNED; + if (platformType->_pointer) + pointer = 1; + if (platformType->_ptr_ptr) + pointer = 2; + if (platformType->_const_ptr) + constness = 1; + return true; + } + + return false; +} + std::string ValueType::str() const { std::string ret; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 5713c5d8a..f33f105b2 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -109,10 +109,22 @@ public: const std::string& name() const; + const std::string& type() const { + return classDef ? classDef->str() : emptyString; + } + + bool isClassType() const { + return classDef && classDef->str() == "class"; + } + bool isEnumType() const { return classDef && classDef->str() == "enum"; } + bool isStructType() const { + return classDef && classDef->str() == "struct"; + } + const Token *initBaseInfo(const Token *tok, const Token *tok1); const Function* getFunction(const std::string& funcName) const; @@ -628,7 +640,8 @@ class CPPCHECKLIB Function { fIsThrow = (1 << 13), /** @brief is throw */ fIsOperator = (1 << 14), /** @brief is operator */ fHasLvalRefQual = (1 << 15), /** @brief has & lvalue ref-qualifier */ - fHasRvalRefQual = (1 << 16) /** @brief has && rvalue ref-qualifier */ + fHasRvalRefQual = (1 << 16), /** @brief has && rvalue ref-qualifier */ + fIsVariadic = (1 << 17) /** @brief is variadic */ }; /** @@ -766,6 +779,9 @@ public: bool hasRvalRefQualifier() const { return getFlag(fHasRvalRefQual); } + bool isVariadic() const { + return getFlag(fIsVariadic); + } void hasBody(bool state) { setFlag(fHasBody, state); @@ -818,6 +834,9 @@ public: void hasRvalRefQualifier(bool state) { setFlag(fHasRvalRefQual, state); } + void isVariadic(bool state) { + setFlag(fIsVariadic, state); + } const Token *tokenDef; // function name token in class definition const Token *argDef; // function argument start '(' in class definition @@ -1071,7 +1090,7 @@ public: void validateVariables() const; /** Set valuetype in provided tokenlist */ - static void setValueTypeInTokenList(Token *tokens, bool cpp, char defaultSignedness, const Library* lib); + static void setValueTypeInTokenList(Token *tokens, bool cpp, const Settings *settings); /** * Calculates sizeof value for given type. @@ -1129,6 +1148,8 @@ public: return (type >= ValueType::Type::BOOL && type <= ValueType::Type::UNKNOWN_INT); } + bool fromLibraryType(const std::string &typestr, const Settings *settings); + std::string str() const; }; diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 2a8b81f1e..650420b87 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -1720,7 +1720,7 @@ bool Tokenizer::simplifyTokens1(const std::string &configuration) } } - SymbolDatabase::setValueTypeInTokenList(list.front(), isCPP(), _settings->defaultSign, &_settings->library); + SymbolDatabase::setValueTypeInTokenList(list.front(), isCPP(), _settings); ValueFlow::setValues(&list, _symbolDatabase, _errorLogger, _settings); printDebugOutput(1); @@ -2548,7 +2548,7 @@ static const std::set notstart_c = make_container< std::set notstart_cpp = make_container< std::set > () << notstart_c - << "delete" << "friend" << "new" << "throw" << "using" << "virtual" << "explicit" << "const_cast" << "dynamic_cast" << "reinterpret_cast" << "static_cast" ; + << "delete" << "friend" << "new" << "throw" << "using" << "virtual" << "explicit" << "const_cast" << "dynamic_cast" << "reinterpret_cast" << "static_cast" << "template"; void Tokenizer::setVarIdPass1() { @@ -3049,7 +3049,7 @@ void Tokenizer::createLinks2() } else if (token->str() == ">") { if (type.empty() || type.top()->str() != "<") // < and > don't match. continue; - if (token->next() && !Token::Match(token->next(), "%name%|>|&|*|::|,|(|)|{|;|[|:")) + if (token->next() && !Token::Match(token->next(), "%name%|>|&|*|::|,|(|)|{|}|;|[|:")) continue; // if > is followed by [ .. "new a[" is expected @@ -3767,7 +3767,7 @@ bool Tokenizer::simplifyTokenList2() // Create symbol database and then remove const keywords createSymbolDatabase(); - SymbolDatabase::setValueTypeInTokenList(list.front(), isCPP(), _settings->defaultSign, &_settings->library); + SymbolDatabase::setValueTypeInTokenList(list.front(), isCPP(), _settings); ValueFlow::setValues(&list, _symbolDatabase, _errorLogger, _settings); @@ -5207,6 +5207,9 @@ void Tokenizer::simplifyFunctionPointers() else if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|,|(|public:|protected:|private:")) continue; + if (Token::Match(tok, "delete|else|return|throw|typedef")) + continue; + while (Token::Match(tok, "%type%|:: %type%|::")) tok = tok->next(); @@ -5579,17 +5582,14 @@ void Tokenizer::simplifyPlatformTypes() enum { isLongLong, isLong, isInt } type; /** @todo This assumes a flat address space. Not true for segmented address space (FAR *). */ - if (_settings->sizeof_size_t == 8) { - if (_settings->sizeof_long == 8) - type = isLong; - else - type = isLongLong; - } else if (_settings->sizeof_size_t == 4) { - if (_settings->sizeof_long == 4) - type = isLong; - else - type = isInt; - } else + + if (_settings->sizeof_size_t == _settings->sizeof_long) + type = isLong; + else if (_settings->sizeof_size_t == _settings->sizeof_long_long) + type = isLongLong; + else if (_settings->sizeof_size_t == _settings->sizeof_int) + type = isInt; + else return; for (Token *tok = list.front(); tok; tok = tok->next()) { @@ -5636,53 +5636,50 @@ void Tokenizer::simplifyPlatformTypes() } } - if (_settings->isWindowsPlatform()) { - std::string platform_type = _settings->platformType == Settings::Win32A ? "win32A" : - _settings->platformType == Settings::Win32W ? "win32W" : "win64"; + const std::string platform_type(_settings->platformString()); - for (Token *tok = list.front(); tok; tok = tok->next()) { - if (tok->tokType() != Token::eType && tok->tokType() != Token::eName) - continue; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->tokType() != Token::eType && tok->tokType() != Token::eName) + continue; - const Library::PlatformType * const platformtype = _settings->library.platform_type(tok->str(), platform_type); + const Library::PlatformType * const platformtype = _settings->library.platform_type(tok->str(), platform_type); - if (platformtype) { - // check for namespace - if (tok->strAt(-1) == "::") { - const Token * tok1 = tok->tokAt(-2); - // skip when non-global namespace defined - if (tok1 && tok1->tokType() == Token::eName) - continue; - tok = tok->tokAt(-1); - tok->deleteThis(); - } - Token *typeToken; - if (platformtype->_const_ptr) { - tok->str("const"); - tok->insertToken("*"); - tok->insertToken(platformtype->_type); - typeToken = tok; - } else if (platformtype->_pointer) { - tok->str(platformtype->_type); - typeToken = tok; - tok->insertToken("*"); - } else if (platformtype->_ptr_ptr) { - tok->str(platformtype->_type); - typeToken = tok; - tok->insertToken("*"); - tok->insertToken("*"); - } else { - tok->originalName(tok->str()); - tok->str(platformtype->_type); - typeToken = tok; - } - if (platformtype->_signed) - typeToken->isSigned(true); - if (platformtype->_unsigned) - typeToken->isUnsigned(true); - if (platformtype->_long) - typeToken->isLong(true); + if (platformtype) { + // check for namespace + if (tok->strAt(-1) == "::") { + const Token * tok1 = tok->tokAt(-2); + // skip when non-global namespace defined + if (tok1 && tok1->tokType() == Token::eName) + continue; + tok = tok->tokAt(-1); + tok->deleteThis(); } + Token *typeToken; + if (platformtype->_const_ptr) { + tok->str("const"); + tok->insertToken("*"); + tok->insertToken(platformtype->_type); + typeToken = tok; + } else if (platformtype->_pointer) { + tok->str(platformtype->_type); + typeToken = tok; + tok->insertToken("*"); + } else if (platformtype->_ptr_ptr) { + tok->str(platformtype->_type); + typeToken = tok; + tok->insertToken("*"); + tok->insertToken("*"); + } else { + tok->originalName(tok->str()); + tok->str(platformtype->_type); + typeToken = tok; + } + if (platformtype->_signed) + typeToken->isSigned(true); + if (platformtype->_unsigned) + typeToken->isUnsigned(true); + if (platformtype->_long) + typeToken->isLong(true); } } } @@ -7576,10 +7573,14 @@ void Tokenizer::syntaxError(const Token *tok) const void Tokenizer::syntaxError(const Token *tok, char c) const { printDebugOutput(0); - throw InternalError(tok, - std::string("Invalid number of character '") + c + "' " + - "when these macros are defined: '" + _configuration + "'.", - InternalError::SYNTAX); + if (_configuration.empty()) + throw InternalError(tok, + std::string("Invalid number of character '") + c + "' when no macros are defined.", + InternalError::SYNTAX); + else + throw InternalError(tok, + std::string("Invalid number of character '") + c + "' when these macros are defined: '" + _configuration + "'.", + InternalError::SYNTAX); } void Tokenizer::unhandled_macro_class_x_y(const Token *tok) const diff --git a/man/manual.docbook b/man/manual.docbook index 2c0e2013d..81da3fd30 100644 --- a/man/manual.docbook +++ b/man/manual.docbook @@ -695,6 +695,10 @@ Checking test.c... configuration files. It is available in the View menu. All settings are not documented in this manual. + If you have a question about the .cfg file + format it is recommended you ask in the forum + (http://sourceforge.net/p/cppcheck/discussion/). + The command line cppcheck will try to load custom .cfg files from the working path - execute cppcheck from the path where the .cfg files are. @@ -1118,6 +1122,23 @@ Checking minsize.c... + +
+ strz + + This setting is not used by Cppcheck currently. But with this + you can say that an argument must be a zero-terminated + string. + + <?xml version="1.0"?> +<def> + <function name="do_something"> + <arg nr="1"> + <strz/> + </arg> + </function> +</def> +
@@ -1209,11 +1230,19 @@ Checking useretval.c...
- pure + pure and const - A function that is pure will calculate a return value and has no - side effects. If the same parameters are given twice then the same - return value will be calculated twice. + These correspond to the GCC function attributes pure and + const. + + A pure function has no effects except to return a value, and its + return value depends only on the parameters and global + variables. + + A const function has no effects except to return a value, and + its return value depends only on the parameters. + + Here is an example code: void f(int x) { @@ -1224,24 +1253,29 @@ Checking useretval.c... } } - Cppcheck reports no warning + If calculate() is a const function then the + result of calculate(x) will be the same in both + conditions, since the same parameter value is used. - # cppcheck pure.c -Checking pure.c... + Cppcheck normally assumes that the result might be different, + and reports no warning for the code: - If a proper lib.cfg is provided, the + # cppcheck const.c +Checking const.c... + + If a proper const.cfg is provided, the unreachable code is detected: - # cppcheck --enable=style --library=pure pure.c -Checking pure.c... -[pure.c:7]: (style) Expression is always false because 'else if' condition matches previous condition at line 5. + # cppcheck --enable=style --library=const const.c +Checking const.c... +[const.c:7]: (style) Expression is always false because 'else if' condition matches previous condition at line 5. - Here is a minimal pure.cfg file: + Here is a minimal const.cfg file: <?xml version="1.0"?> <def> <function name="calculate"> - <pure/> + <const/> <arg nr="1"/> </function> </def> diff --git a/samples/syntaxError/out.txt b/samples/syntaxError/out.txt index 059c1c86c..8f0626044 100644 --- a/samples/syntaxError/out.txt +++ b/samples/syntaxError/out.txt @@ -1 +1 @@ -[samples\syntaxError\bad.c:2]: (error) Invalid number of character '{' when these macros are defined: ''. +[samples\syntaxError\bad.c:2]: (error) Invalid number of character '{' when no macros are defined. diff --git a/test/testgarbage.cpp b/test/testgarbage.cpp index 4a1b10432..4f7087ad9 100644 --- a/test/testgarbage.cpp +++ b/test/testgarbage.cpp @@ -1073,7 +1073,7 @@ private: tokenizer.tokenize(istr, "test.cpp"); assertThrowFail(__FILE__, __LINE__); } catch (InternalError& e) { - ASSERT_EQUALS("Invalid number of character '(' when these macros are defined: ''.", e.errorMessage); + ASSERT_EQUALS("Invalid number of character '(' when no macros are defined.", e.errorMessage); ASSERT_EQUALS("syntaxError", e.id); ASSERT_EQUALS(2, e.token->linenr()); } diff --git a/test/testother.cpp b/test/testother.cpp index e62758c93..821088a50 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -3815,6 +3815,11 @@ private: " return A ? x : z;\n" "}"); ASSERT_EQUALS("", errout.str()); + + check("void f(unsigned char c) {\n" + " x = y ? (signed char)c : (unsigned char)c;\n" + "}"); + ASSERT_EQUALS("", errout.str()); } void checkSignOfUnsignedVariable() { diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 6c68c0766..e298c79ed 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -128,7 +128,6 @@ private: TEST_CASE(if_cond12); TEST_CASE(if_cond13); TEST_CASE(if_cond14); - TEST_CASE(if_cond15); // #4456 - segfault TEST_CASE(if_or_1); TEST_CASE(if_or_2); @@ -267,6 +266,22 @@ private: } } + std::string getConfigsStr(const char filedata[], const char *u1=NULL) { + Settings settings; + if (u1) + settings.userUndefs.insert(u1); + Preprocessor preprocessor(settings, this); + std::vector files; + std::istringstream istr(filedata); + simplecpp::TokenList tokens(istr,files); + tokens.removeComments(); + const std::set configs = preprocessor.getConfigs(tokens); + std::string ret; + for (std::set::const_iterator it = configs.begin(); it != configs.end(); ++it) + ret += *it + '\n'; + return ret; + } + void Bug2190219() { const char filedata[] = "#ifdef __cplusplus\n" "cpp\n" @@ -304,14 +319,7 @@ private: " qwerty\n" "#endif \n"; - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(2, static_cast(actual.size())); - ASSERT_EQUALS("\n\n\nqwerty", actual[""]); - ASSERT_EQUALS("\nabcdef", actual["WIN32"]); + ASSERT_EQUALS("\nWIN32\n", getConfigsStr(filedata)); } void test2() { @@ -320,15 +328,7 @@ private: " # else \n" " qwerty\n" " # endif \n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(2U, actual.size()); - ASSERT_EQUALS("\n\" # ifdef WIN32\"", actual[""]); - ASSERT_EQUALS("\n\n\nqwerty", actual["WIN32"]); + ASSERT_EQUALS("\nWIN32\n", getConfigsStr(filedata)); } void test3() { @@ -340,15 +340,7 @@ private: "c\n" "#endif\n"; - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(3, static_cast(actual.size())); - ASSERT_EQUALS("", actual[""]); - ASSERT_EQUALS("\na\n\n\n\nc", actual["ABC"]); - ASSERT_EQUALS("\na\n\nb\n\nc", actual["ABC;DEF"]); + ASSERT_EQUALS("\nABC\nABC;DEF\n", getConfigsStr(filedata)); } void test4() { @@ -358,15 +350,7 @@ private: "#ifdef ABC\n" "A\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(2, static_cast(actual.size())); - ASSERT_EQUALS("", actual[""]); - ASSERT_EQUALS("\nA\n\n\nA", actual["ABC"]); + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void test5() { @@ -378,16 +362,7 @@ private: "C\n" "#endif\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(3, static_cast(actual.size())); - ASSERT_EQUALS("\n\n\nB", actual[""]); - ASSERT_EQUALS("\nA", actual["ABC"]); - ASSERT_EQUALS("\n\n\nB\n\nC", actual["DEF"]); + ASSERT_EQUALS("\nABC\nDEF\n", getConfigsStr(filedata)); } void test7() { @@ -397,20 +372,7 @@ private: "B\n" "#endif\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Make sure an error message is written.. - TODO_ASSERT_EQUALS("[file.c:3]: (error) ABC is already guaranteed to be defined\n", - "", - errout.str()); - - // Compare results.. - ASSERT_EQUALS(2, static_cast(actual.size())); - ASSERT_EQUALS("", actual[""]); - ASSERT_EQUALS("\nA\n\nB", actual["ABC"]); + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); test7a(); test7b(); @@ -425,17 +387,7 @@ private: "B\n" "#endif\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Make sure an error message is written.. - TODO_ASSERT_EQUALS("[file.c:3]: (error) ABC is already guaranteed NOT to be defined\n", - "", errout.str()); - - // Compare results.. - // TODO Preprocessor::getConfigs ASSERT_EQUALS(2, static_cast(actual.size())); + ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void test7b() { @@ -445,17 +397,7 @@ private: "B\n" "#endif\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Make sure an error message is written.. - TODO_ASSERT_EQUALS("[file.c:3]: (error) ABC is already guaranteed NOT to be defined\n", - "", errout.str()); - - // Compare results.. - ASSERT_EQUALS(2, static_cast(actual.size())); + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void test7c() { @@ -465,18 +407,7 @@ private: "B\n" "#endif\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Make sure an error message is written.. - TODO_ASSERT_EQUALS("[file.c:3]: (error) ABC is already guaranteed to be defined\n", - "", - errout.str()); - - // Compare results.. - ASSERT_EQUALS(2, static_cast(actual.size())); + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void test7d() { @@ -486,18 +417,7 @@ private: "B\n" "#endif\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Make sure an error message is written.. - TODO_ASSERT_EQUALS("[file.c:3]: (error) ABC is already guaranteed to be defined\n", - "", - errout.str()); - - // Compare results.. - ASSERT_EQUALS(2, static_cast(actual.size())); + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void test7e() { @@ -510,49 +430,21 @@ private: "#endif\n" "#endfile\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Make sure an error message is written.. - ASSERT_EQUALS("", errout.str()); - - // Compare results.. - ASSERT_EQUALS(2U, actual.size()); + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void test8() { const char filedata[] = "#if A == 1\n" "1\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // No error.. - ASSERT_EQUALS("", errout.str()); - - // Compare results.. - ASSERT_EQUALS(2U, actual.size()); - ASSERT_EQUALS("", actual[""]); - ASSERT_EQUALS("\n1", actual["A=1"]); + ASSERT_EQUALS("\nA=1\n", getConfigsStr(filedata)); } void test9() { const char filedata[] = "#if\n" "#else\n" "#endif\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Settings settings; - settings.maxConfigs = 1; - settings.userDefines = "X"; - Preprocessor preprocessor(settings, this); - preprocessor.preprocess(istr, actual, "file.c"); // <- don't crash + getConfigsStr(filedata); // <- don't crash } void test10() { // Ticket #5139 @@ -561,10 +453,7 @@ private: "#define baz bar+0\n" "#if 0\n" "#endif"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); + ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void error1() { @@ -573,16 +462,7 @@ private: "#else\n" "#error abcd\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(2, static_cast(actual.size())); - ASSERT_EQUALS("", actual[""]); - ASSERT_EQUALS("\n;", actual["A"]); - + ASSERT_EQUALS("\nA\n", getConfigsStr(filedata)); } void error3() { @@ -665,13 +545,7 @@ private: "#endfile\n" "#ifdef ABC\n" "#endif"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Expected configurations: "" and "ABC" - ASSERT_EQUALS(2, static_cast(actual.size())); + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void includeguard2() { @@ -682,15 +556,7 @@ private: "\n" "#endif\n" "#endfile\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Expected configurations: "" and "ABC" - ASSERT_EQUALS(2, static_cast(actual.size())); - ASSERT_EQUALS(true, actual.find("") != actual.end()); - ASSERT_EQUALS(true, actual.find("ABC") != actual.end()); + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } @@ -718,28 +584,14 @@ private: "#ifdef WIN32\n" "#endif\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(1, static_cast(actual.size())); - ASSERT_EQUALS("", actual[""]); + ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if1() { const char filedata[] = " # if /* comment */ 1 // comment\n" "ABC\n" " # endif \n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(1, static_cast(actual.size())); - ASSERT_EQUALS("\nABC", actual[""]); + ASSERT_EQUALS("\n", getConfigsStr(filedata)); } @@ -750,16 +602,7 @@ private: "#elif DEF2\n" "DEF\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(3, static_cast(actual.size())); - ASSERT_EQUALS("", actual[""]); - ASSERT_EQUALS("\nABC", actual["DEF1"]); - ASSERT_EQUALS("\n\n\nDEF", actual["DEF2"]); + ASSERT_EQUALS("\nDEF1\nDEF2\n", getConfigsStr(filedata)); } { @@ -770,16 +613,7 @@ private: "#else\n" "GHI\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(3, static_cast(actual.size())); - ASSERT_EQUALS("\n\n\n\n\nGHI", actual[""]); - ASSERT_EQUALS("\nABC", actual["DEF1"]); - ASSERT_EQUALS("\n\n\nDEF", actual["DEF2"]); + ASSERT_EQUALS("\nDEF1\nDEF2\n", getConfigsStr(filedata)); } } @@ -789,18 +623,7 @@ private: "#else\n" " B\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(1, static_cast(actual.size())); - ASSERT_EQUALS("\n" - "\n" - "\n" - "B", actual[""]); - TODO_ASSERT_EQUALS("A", "", actual["LIBVER=101"]); + TODO_ASSERT_EQUALS("\nLIBVER=101\n", "\n", getConfigsStr(filedata)); } void if_cond2() { @@ -810,16 +633,8 @@ private: "#if defined(A) && defined(B)\n" "ab\n" "#endif\n"; + ASSERT_EQUALS("\nA\nA;B\n", getConfigsStr(filedata)); - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(3, static_cast(actual.size())); - ASSERT_EQUALS("", actual[""]); - ASSERT_EQUALS("\na", actual["A"]); - ASSERT_EQUALS("\na\n\n\nab", actual["A;B"]); if_cond2b(); if_cond2c(); if_cond2d(); @@ -835,16 +650,7 @@ private: "#else\n" "a\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(3, static_cast(actual.size())); - ASSERT_EQUALS("\n! a", actual[""]); - ASSERT_EQUALS("\n\n\n\n\n\na", actual["A"]); - ASSERT_EQUALS("\n! a\n\nb", actual["B"]); + TODO_ASSERT_EQUALS("\nA;B\n", "\nA\nB\n", getConfigsStr(filedata)); } void if_cond2c() { @@ -858,16 +664,7 @@ private: "#else\n" "a\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(3, static_cast(actual.size())); - ASSERT_EQUALS("\n! a\n\n\n\n! b", actual[""]); - ASSERT_EQUALS("\n\n\n\n\n\n\n\na", actual["A"]); - ASSERT_EQUALS("\n! a\n\nb", actual["B"]); + TODO_ASSERT_EQUALS("\nA\nA;B\n", "\nA\nB\n", getConfigsStr(filedata)); } void if_cond2d() { @@ -886,17 +683,7 @@ private: "!b\n" "#endif\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(4, static_cast(actual.size())); - ASSERT_EQUALS("\n! a\n\n\n\n! b", actual[""]); - ASSERT_EQUALS("\n\n\n\n\n\n\n\na\n\n\n\n! b", actual["A"]); - ASSERT_EQUALS("\n\n\n\n\n\n\n\na\n\nb", actual["A;B"]); - ASSERT_EQUALS("\n! a\n\nb", actual["B"]); + ASSERT_EQUALS("\nA\nA;B\nB\n", getConfigsStr(filedata)); } void if_cond2e() { @@ -905,22 +692,7 @@ private: "#elif !defined(B)\n" "!b\n" "#endif\n"; - - // Preprocess => actual result.. - errout.str(""); - std::istringstream istr(filedata); - std::map actual; - Settings settings; - settings.debug = settings.debugwarnings = true; - Preprocessor preprocessor(settings, this); - preprocessor.preprocess(istr, actual, "file.c"); - - // Compare results.. - TODO_ASSERT_EQUALS(3U, 1U, actual.size()); - ASSERT_EQUALS("\n! a", actual[""]); - TODO_ASSERT_EQUALS("\n\n\n! b", "", actual["A"]); - ASSERT_EQUALS("", actual["A;B"]); - ASSERT_EQUALS("", errout.str()); + TODO_ASSERT_EQUALS("\nA\nA;B", "\n", getConfigsStr(filedata)); } void if_cond3() { @@ -930,16 +702,7 @@ private: "abc\n" "#endif\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(3, static_cast(actual.size())); - ASSERT_EQUALS("", actual[""]); - ASSERT_EQUALS("\na", actual["A"]); - ASSERT_EQUALS("\na\n\nabc", actual["A;B;C"]); + ASSERT_EQUALS("\nA\nA;B;C\n", getConfigsStr(filedata)); } void if_cond4() { @@ -949,14 +712,7 @@ private: "#if defined A || defined B\n" "ab\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(1, static_cast(actual.size())); - ASSERT_EQUALS("\n\n\nab", actual[""]); + ASSERT_EQUALS("\n", getConfigsStr(filedata)); } { @@ -967,16 +723,7 @@ private: "#endif\n" "}\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(3, static_cast(actual.size())); - ASSERT_EQUALS("", actual[""]); - ASSERT_EQUALS("\n{\n\n\n\n}", actual["A"]); - ASSERT_EQUALS("\n{\n\nfoo ( ) ;\n\n}", actual["A;B"]); + ASSERT_EQUALS("\nA\nA;B\n", getConfigsStr(filedata)); } { @@ -985,43 +732,21 @@ private: "#if (defined A) || defined (B)\n" "ab\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(1, static_cast(actual.size())); - ASSERT_EQUALS("\n\n\nab", actual[""]); + ASSERT_EQUALS("\n", getConfigsStr(filedata)); } { const char filedata[] = "#if (A)\n" "foo();\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(2, static_cast(actual.size())); - ASSERT_EQUALS("", actual[""]); - ASSERT_EQUALS("\nfoo ( ) ;", actual["A"]); + ASSERT_EQUALS("\nA\n", getConfigsStr(filedata)); } { const char filedata[] = "#if! A\n" "foo();\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - TODO_ASSERT_EQUALS(2, 1, static_cast(actual.size())); - ASSERT_EQUALS("\nfoo ( ) ;", actual[""]); + ASSERT_EQUALS("\n", getConfigsStr(filedata)); } } @@ -1033,41 +758,20 @@ private: "#if defined(B) && defined(A)\n" "ef\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(2, static_cast(actual.size())); - ASSERT_EQUALS("\n\n\ncd", actual[""]); - ASSERT_EQUALS("\nab\n\ncd\n\nef", actual["A;B"]); + ASSERT_EQUALS("\nA;B\n", getConfigsStr(filedata)); } void if_cond6() { const char filedata[] = "\n" "#if defined(A) && defined(B))\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - // TODO ASSERT_EQUALS("[file.c:2]: (error) mismatching number of '(' and ')' in this line: defined(A)&&defined(B))\n", errout.str()); + ASSERT_EQUALS("\nA;B\n", getConfigsStr(filedata)); } void if_cond8() { const char filedata[] = "#if defined(A) + defined(B) + defined(C) != 1\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - // TODO Preprocessor::getConfig ASSERT_EQUALS(1U, actual.size()); - ASSERT_EQUALS("", actual[""]); + TODO_ASSERT_EQUALS("\nA\n", "\nA;B;C\n", getConfigsStr(filedata)); } @@ -1075,14 +779,7 @@ private: const char filedata[] = "#if !defined _A\n" "abc\n" "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); - - // Compare results.. - ASSERT_EQUALS(1, (int)actual.size()); - ASSERT_EQUALS("\nabc", actual[""]); + ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if_cond10() { @@ -1112,32 +809,21 @@ private: "#if A == 1\n" ";\n" "#endif\n"; - ASSERT_EQUALS("\n\n;", preprocessor0.getcode(filedata,"","")); + ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if_cond13() { const char filedata[] = "#if ('A' == 0x41)\n" "123\n" "#endif\n"; - ASSERT_EQUALS("\n123", preprocessor0.getcode(filedata,"","")); + ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if_cond14() { const char filedata[] = "#if !(A)\n" "123\n" "#endif\n"; - ASSERT_EQUALS("\n123", preprocessor0.getcode(filedata,"","")); - } - - void if_cond15() { // #4456 - segmentation fault - const char filedata[] = "#if ((A >= B) && (C != D))\n" - "#if (E < F(1))\n" - "#endif\n" - "#endif\n"; - - // Preprocess => actual result.. - std::map actual; - preprocess(filedata, actual); // <- don't crash in Preprocessor::getcfgs -> Tokenize -> number of template parameters + ASSERT_EQUALS("\n", getConfigsStr(filedata)); } @@ -1146,39 +832,18 @@ private: const char filedata[] = "#if defined(DEF_10) || defined(DEF_11)\n" "a1;\n" "#endif\n"; - - errout.str(""); - output.str(""); - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Settings settings; - settings.debug = settings.debugwarnings = true; - settings.addEnabled("missingInclude"); - Preprocessor preprocessor(settings, this); - preprocessor.preprocess(istr, actual, "file.c"); - - // Compare results.. - ASSERT_EQUALS(2U, actual.size()); - ASSERT_EQUALS("", actual[""]); - - // the "defined(DEF_10) || defined(DEF_11)" are not handled correctly.. - ASSERT_EQUALS(2U, actual.size()); - ASSERT_EQUALS("\na1 ;", actual["DEF_10;DEF_11"]); - + ASSERT_EQUALS("\nDEF_10;DEF_11\n", getConfigsStr(filedata)); } void if_or_2() { - const std::string code("#if X || Y\n" - "a1;\n" - "#endif\n"); - ASSERT_EQUALS("\na1 ;", preprocessor0.getcode(code, "X", "test.c")); - ASSERT_EQUALS("\na1 ;", preprocessor0.getcode(code, "Y", "test.c")); + const char filedata[] = "#if X || Y\n" + "a1;\n" + "#endif\n"; + TODO_ASSERT_EQUALS("\nX;Y\n", "\n", getConfigsStr(filedata)); } void if_macro_eq_macro() { - const char* code = "#define A B\n" + const char *code = "#define A B\n" "#define B 1\n" "#define C 1\n" "#if A == C\n" @@ -1186,10 +851,7 @@ private: "#else\n" "Betty\n" "#endif\n"; - std::map actual; - preprocess(code, actual); - - ASSERT_EQUALS("\n\n\n\nWilma", actual[""]); + ASSERT_EQUALS("\n", getConfigsStr(code)); } void ticket_3675() { @@ -1784,7 +1446,7 @@ private: void stringify5() const { const char filedata[] = "#define A(x) a(#x,x)\n" "A(foo(\"\\\"\"))\n"; - ASSERT_EQUALS("\na ( \"foo(\"\\\"\")\" , foo ( \"\\\"\" ) )", OurPreprocessor::expandMacros(filedata)); + ASSERT_EQUALS("\na ( \"foo(\\\"\\\\\\\"\\\")\" , foo ( \"\\\"\" ) )", OurPreprocessor::expandMacros(filedata)); } void pragma() { @@ -2422,22 +2084,6 @@ private: ASSERT_EQUALS("", actual); } - std::string getConfigsStr(const char filedata[], const char *u1=NULL) { - Settings settings; - if (u1) - settings.userUndefs.insert(u1); - Preprocessor preprocessor(settings, this); - std::vector files; - std::istringstream istr(filedata); - simplecpp::TokenList tokens(istr,files); - tokens.removeComments(); - const std::set configs = preprocessor.getConfigs(tokens); - std::string ret; - for (std::set::const_iterator it = configs.begin(); it != configs.end(); ++it) - ret += *it + '\n'; - return ret; - } - void undef1() { const char filedata[] = "#ifdef X\n" "#endif\n"; @@ -2468,37 +2114,20 @@ private: Settings settings; Preprocessor preprocessor(settings, this); - ASSERT_EQUALS(true, preprocessor.validateCfg("", "X=42")); // don't hang when parsing cfg - ASSERT_EQUALS(false, preprocessor.validateCfg("int y=Y;", "X=42;Y")); - ASSERT_EQUALS(false, preprocessor.validateCfg("int x=X;", "X")); - ASSERT_EQUALS(false, preprocessor.validateCfg("X=1;", "X")); - ASSERT_EQUALS(true, preprocessor.validateCfg("int x=X;", "Y")); - ASSERT_EQUALS(true, preprocessor.validateCfg("FOO_DEBUG()", "DEBUG")); - ASSERT_EQUALS(true, preprocessor.validateCfg("\"DEBUG()\"", "DEBUG")); - ASSERT_EQUALS(true, preprocessor.validateCfg("\"\\\"DEBUG()\"", "DEBUG")); - ASSERT_EQUALS(false, preprocessor.validateCfg("\"DEBUG()\" DEBUG", "DEBUG")); - ASSERT_EQUALS(true, preprocessor.validateCfg("#undef DEBUG", "DEBUG")); + std::list macroUsageList; + std::vector files; + files.push_back("test.c"); + simplecpp::MacroUsage macroUsage(files); + macroUsage.useLocation.fileIndex = 0; + macroUsage.useLocation.line = 1; + macroUsage.macroName = "X"; + macroUsageList.push_back(macroUsage); - // #4301: - // #ifdef A - // int a = A; // <- using macro. must use -D so "A" will get a proper value - errout.str(""); - settings.addEnabled("all"); - preprocessor.setFile0("test.c"); - ASSERT_EQUALS(false, preprocessor.validateCfg("int a=A;", "A")); - ASSERT_EQUALS("[test.c:1]: (information) Skipping configuration 'A' since the value of 'A' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.\n", errout.str()); - - // #4949: - // #ifdef A - // a |= A; // <- using macro. must use -D so "A" will get a proper value - errout.str(""); - Settings settings1; - settings = settings1; - ASSERT_EQUALS("", preprocessor.getcode("if (x) a|=A;", "A", "test.c")); - ASSERT_EQUALS("", errout.str()); - settings.addEnabled("information"); - ASSERT_EQUALS("", preprocessor.getcode("if (x) a|=A;", "A", "test.c")); - ASSERT_EQUALS("[test.c:1]: (information) Skipping configuration 'A' since the value of 'A' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.\n", errout.str()); + ASSERT_EQUALS(true, preprocessor.validateCfg("", macroUsageList)); + ASSERT_EQUALS(false, preprocessor.validateCfg("X",macroUsageList)); + ASSERT_EQUALS(false, preprocessor.validateCfg("A=42;X", macroUsageList)); + ASSERT_EQUALS(true, preprocessor.validateCfg("X=1", macroUsageList)); + ASSERT_EQUALS(true, preprocessor.validateCfg("Y", macroUsageList)); } void if_sizeof() { // #4071 diff --git a/test/teststl.cpp b/test/teststl.cpp index 1e0a5b09e..e1a12db46 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -201,6 +201,14 @@ private: " l2.insert(it, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Same iterator is used with different containers 'l1' and 'l2'.\n", errout.str()); + + check("void foo() {\n" // #5803 + " list l1;\n" + " list l2;\n" + " list::iterator it = l1.begin();\n" + " l2.insert(it, l1.end());\n" + "}"); + ASSERT_EQUALS("", errout.str()); } void iterator4() { diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 936ec0cd5..67ad7f565 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -182,6 +182,10 @@ private: TEST_CASE(functionArgs2); TEST_CASE(functionArgs3); TEST_CASE(functionArgs4); + TEST_CASE(functionArgs5); // #7650 + TEST_CASE(functionArgs6); // #7651 + TEST_CASE(functionArgs7); // #7652 + TEST_CASE(functionArgs8); // #7653 TEST_CASE(namespaces1); TEST_CASE(namespaces2); @@ -294,6 +298,10 @@ private: TEST_CASE(executableScopeWithUnknownFunction); TEST_CASE(valuetype); + + TEST_CASE(variadic1); // #7453 + TEST_CASE(variadic2); // #7649 + TEST_CASE(variadic3); // #7387 } void array() { @@ -1571,6 +1579,159 @@ private: } } + void functionArgs5() { // #7650 + GET_SYMBOL_DB("class ABC {};\n" + "class Y {\n" + " enum ABC {A,B,C};\n" + " void f(enum ABC abc) {}\n" + "};"); + ASSERT_EQUALS(true, db != nullptr); + if (db) { + const Token *f = Token::findsimplematch(tokenizer.tokens(), "f ( enum"); + ASSERT_EQUALS(true, f && f->function()); + if (f && f->function()) { + const Function *func = f->function(); + ASSERT_EQUALS(true, func->argumentList.size() == 1 && func->argumentList.front().type()); + if (func->argumentList.size() == 1 && func->argumentList.front().type()) { + const Type * type = func->argumentList.front().type(); + ASSERT_EQUALS(true, type->isEnumType()); + } + } + } + } + + void functionArgs6() { // #7651 + GET_SYMBOL_DB("class ABC {};\n" + "class Y {\n" + " enum ABC {A,B,C};\n" + " void f(ABC abc) {}\n" + "};"); + ASSERT_EQUALS(true, db != nullptr); + if (db) { + const Token *f = Token::findsimplematch(tokenizer.tokens(), "f ( ABC"); + ASSERT_EQUALS(true, f && f->function()); + if (f && f->function()) { + const Function *func = f->function(); + ASSERT_EQUALS(true, func->argumentList.size() == 1 && func->argumentList.front().type()); + if (func->argumentList.size() == 1 && func->argumentList.front().type()) { + const Type * type = func->argumentList.front().type(); + ASSERT_EQUALS(true, type->isEnumType()); + } + } + } + } + + void functionArgs7() { // #7652 + { + GET_SYMBOL_DB("struct AB { int a; int b; };\n" + "int foo(struct AB *ab);\n" + "void bar() {\n" + " struct AB ab;\n" + " foo(&ab); \n" + "};"); + ASSERT_EQUALS(true, db != nullptr); + if (db) { + const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( & ab"); + ASSERT_EQUALS(true, f && f->function()); + if (f && f->function()) { + const Function *func = f->function(); + ASSERT_EQUALS(true, func->tokenDef->linenr() == 2 && func->argumentList.size() == 1 && func->argumentList.front().type()); + if (func->argumentList.size() == 1 && func->argumentList.front().type()) { + const Type * type = func->argumentList.front().type(); + ASSERT_EQUALS(true, type->classDef->linenr() == 1); + } + } + } + } + { + GET_SYMBOL_DB("struct AB { int a; int b; };\n" + "int foo(AB *ab);\n" + "void bar() {\n" + " struct AB ab;\n" + " foo(&ab); \n" + "};"); + ASSERT_EQUALS(true, db != nullptr); + if (db) { + const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( & ab"); + ASSERT_EQUALS(true, f && f->function()); + if (f && f->function()) { + const Function *func = f->function(); + ASSERT_EQUALS(true, func->tokenDef->linenr() == 2 && func->argumentList.size() == 1 && func->argumentList.front().type()); + if (func->argumentList.size() == 1 && func->argumentList.front().type()) { + const Type * type = func->argumentList.front().type(); + ASSERT_EQUALS(true, type->classDef->linenr() == 1); + } + } + } + } + { + GET_SYMBOL_DB("struct AB { int a; int b; };\n" + "int foo(struct AB *ab);\n" + "void bar() {\n" + " AB ab;\n" + " foo(&ab); \n" + "};"); + ASSERT_EQUALS(true, db != nullptr); + if (db) { + const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( & ab"); + ASSERT_EQUALS(true, f && f->function()); + if (f && f->function()) { + const Function *func = f->function(); + ASSERT_EQUALS(true, func->tokenDef->linenr() == 2 && func->argumentList.size() == 1 && func->argumentList.front().type()); + if (func->argumentList.size() == 1 && func->argumentList.front().type()) { + const Type * type = func->argumentList.front().type(); + ASSERT_EQUALS(true, type->classDef->linenr() == 1); + } + } + } + } + { + GET_SYMBOL_DB("struct AB { int a; int b; };\n" + "int foo(AB *ab);\n" + "void bar() {\n" + " AB ab;\n" + " foo(&ab); \n" + "};"); + ASSERT_EQUALS(true, db != nullptr); + if (db) { + const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( & ab"); + ASSERT_EQUALS(true, f && f->function()); + if (f && f->function()) { + const Function *func = f->function(); + ASSERT_EQUALS(true, func->tokenDef->linenr() == 2 && func->argumentList.size() == 1 && func->argumentList.front().type()); + if (func->argumentList.size() == 1 && func->argumentList.front().type()) { + const Type * type = func->argumentList.front().type(); + ASSERT_EQUALS(true, type->classDef->linenr() == 1); + } + } + } + } + } + + void functionArgs8() { // #7653 + GET_SYMBOL_DB("struct A { int i; };\n" + "struct B { double d; };\n" + "int foo(struct A a);\n" + "double foo(struct B b);\n" + "void bar() {\n" + " struct B b;\n" + " foo(b);\n" + "}"); + ASSERT_EQUALS(true, db != nullptr); + if (db) { + const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( b"); + ASSERT_EQUALS(true, f && f->function()); + if (f && f->function()) { + const Function *func = f->function(); + ASSERT_EQUALS(true, func->tokenDef->linenr() == 4 && func->argumentList.size() == 1 && func->argumentList.front().type()); + if (func->argumentList.size() == 1 && func->argumentList.front().type()) { + const Type * type = func->argumentList.front().type(); + ASSERT_EQUALS(true, type->isStructType()); + } + } + } + } + void namespaces1() { GET_SYMBOL_DB("namespace fred {\n" " namespace barney {\n" @@ -3670,6 +3831,118 @@ private: // Pointer to unknown type ASSERT_EQUALS("*", typeOf("Bar* b;", "b")); + + // Library types + { + // PodType + Settings s; + s.platformType = Settings::Win64; + const Library::PodType u32 = { 4, 'u' }; + s.library.podtypes["u32"] = u32; + ValueType vt; + ASSERT_EQUALS(true, vt.fromLibraryType("u32", &s)); + ASSERT_EQUALS(ValueType::Type::INT, vt.type); + } + { + // PlatformType + Settings s; + s.platformType = Settings::Unix32; + Library::PlatformType s32; + s32._type = "int"; + s.library.platforms[s.platformString()]._platform_types["s32"] = s32; + ValueType vt; + ASSERT_EQUALS(true, vt.fromLibraryType("s32", &s)); + ASSERT_EQUALS(ValueType::Type::INT, vt.type); + } + } + + void variadic1() { // #7453 + { + GET_SYMBOL_DB("CBase* create(const char *c1, ...);\n" + "int create(COther& ot, const char *c1, ...);\n" + "int foo(COther & ot)\n" + "{\n" + " CBase* cp1 = create(\"AAAA\", 44, (char*)0);\n" + " CBase* cp2 = create(ot, \"AAAA\", 44, (char*)0);\n" + "}"); + + const Token *f = Token::findsimplematch(tokenizer.tokens(), "create ( \"AAAA\""); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 1); + f = Token::findsimplematch(tokenizer.tokens(), "create ( ot"); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); + } + { + GET_SYMBOL_DB("int create(COther& ot, const char *c1, ...);\n" + "CBase* create(const char *c1, ...);\n" + "int foo(COther & ot)\n" + "{\n" + " CBase* cp1 = create(\"AAAA\", 44, (char*)0);\n" + " CBase* cp2 = create(ot, \"AAAA\", 44, (char*)0);\n" + "}"); + + const Token *f = Token::findsimplematch(tokenizer.tokens(), "create ( \"AAAA\""); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); + f = Token::findsimplematch(tokenizer.tokens(), "create ( ot"); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 1); + } + } + + void variadic2() { // #7649 + { + GET_SYMBOL_DB("CBase* create(const char *c1, ...);\n" + "CBase* create(const wchar_t *c1, ...);\n" + "int foo(COther & ot)\n" + "{\n" + " CBase* cp1 = create(\"AAAA\", 44, (char*)0);\n" + " CBase* cp2 = create(L\"AAAA\", 44, (char*)0);\n" + "}"); + + const Token *f = Token::findsimplematch(tokenizer.tokens(), "cp1 = create ("); + ASSERT_EQUALS(true, db && f && f->tokAt(2) && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 1); + f = Token::findsimplematch(tokenizer.tokens(), "cp2 = create ("); + ASSERT_EQUALS(true, db && f && f->tokAt(2) && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 2); + } + { + GET_SYMBOL_DB("CBase* create(const wchar_t *c1, ...);\n" + "CBase* create(const char *c1, ...);\n" + "int foo(COther & ot)\n" + "{\n" + " CBase* cp1 = create(\"AAAA\", 44, (char*)0);\n" + " CBase* cp2 = create(L\"AAAA\", 44, (char*)0);\n" + "}"); + + const Token *f = Token::findsimplematch(tokenizer.tokens(), "cp1 = create ("); + ASSERT_EQUALS(true, db && f && f->tokAt(2) && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 2); + f = Token::findsimplematch(tokenizer.tokens(), "cp2 = create ("); + ASSERT_EQUALS(true, db && f && f->tokAt(2) && f->tokAt(2)->function() && f->tokAt(2)->function()->tokenDef->linenr() == 1); + } + } + + void variadic3() { // #7387 + { + GET_SYMBOL_DB("int zdcalc(const XYZ & per, short rs = 0);\n" + "double zdcalc(long& length, const XYZ * per);\n" + "long mycalc( ) {\n" + " long length;\n" + " XYZ per;\n" + " zdcalc(length, &per);\n" + "}"); + + const Token *f = Token::findsimplematch(tokenizer.tokens(), "zdcalc ( length"); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); + } + { + GET_SYMBOL_DB("double zdcalc(long& length, const XYZ * per);\n" + "int zdcalc(const XYZ & per, short rs = 0);\n" + "long mycalc( ) {\n" + " long length;\n" + " XYZ per;\n" + " zdcalc(length, &per);\n" + "}"); + + const Token *f = Token::findsimplematch(tokenizer.tokens(), "zdcalc ( length"); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 1); + } } }; diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 2e940887b..0294f5658 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -292,6 +292,7 @@ private: TEST_CASE(simplifyStdType); // #4947, #4950, #4951 TEST_CASE(createLinks); + TEST_CASE(createLinks2); TEST_CASE(signed1); TEST_CASE(simplifyString); @@ -307,6 +308,7 @@ private: TEST_CASE(functionpointer6); TEST_CASE(functionpointer7); TEST_CASE(functionpointer8); // #7410 - throw + TEST_CASE(functionpointer9); // #6113 - function call with function pointer TEST_CASE(removeRedundantAssignment); @@ -4523,6 +4525,20 @@ private: } } + void createLinks2() { + { + // #7158 + const char code[] = "enum { value = boost::mpl::at_c };"; + errout.str(""); + Tokenizer tokenizer(&settings0, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "<"); + ASSERT_EQUALS(true, tok->link() == tok->tokAt(4)); + ASSERT_EQUALS(true, tok->linkAt(4) == tok); + } + } + void simplifyString() { errout.str(""); Tokenizer tokenizer(&settings0, this); @@ -4681,6 +4697,20 @@ private: ASSERT_EQUALS(expected1, tokenizeDebugListing(code1, false)); } + void functionpointer9() { // function call with function pointer + const char code1[] = "int f() { (*f)(); }"; + const char expected1[] = "1: int f ( ) { ( * f ) ( ) ; }\n"; + ASSERT_EQUALS(expected1, tokenizeDebugListing(code1, false)); + + const char code2[] = "int f() { return (*f)(); }"; + const char expected2[] = "1: int f ( ) { return ( * f ) ( ) ; }\n"; + ASSERT_EQUALS(expected2, tokenizeDebugListing(code2, false)); + + const char code3[] = "int f() { throw (*f)(); }"; + const char expected3[] = "1: int f ( ) { throw ( * f ) ( ) ; }\n"; + ASSERT_EQUALS(expected3, tokenizeDebugListing(code3, false)); + } + void removeRedundantAssignment() { ASSERT_EQUALS("void f ( ) { }", tokenizeAndStringify("void f() { int *p, *q; p = q; }", true)); ASSERT_EQUALS("void f ( ) { }", tokenizeAndStringify("void f() { int *p = 0, *q; p = q; }", true)); diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 7d96b2251..265227385 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -73,6 +73,8 @@ private: TEST_CASE(syntax_error); // Ticket #5073 TEST_CASE(trac_5970); + TEST_CASE(isVariableUsageDeref); // *p + // dead pointer TEST_CASE(deadPointer); } @@ -537,13 +539,6 @@ private: "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); - checkUninitVar("void f()\n" - "{\n" - " char *s = malloc(100);\n" - " *s += 10;\n" - "}"); - ASSERT_EQUALS("[test.cpp:4]: (error) Memory is allocated but not initialized: s\n", errout.str()); - checkUninitVar("void f()\n" "{\n" " int a[10];\n" @@ -1236,18 +1231,6 @@ private: "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", "", errout.str()); - checkUninitVar("int f() {\n" - " char a[10];\n" - " char c = *a;\n" - "}"); - ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); - - checkUninitVar("int f() {\n" - " char a[SIZE+10];\n" - " char c = *a;\n" - "}"); - ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); - checkUninitVar("int f()\n" "{\n" " char a[10];\n" @@ -3558,6 +3541,19 @@ private: " }\n" "}"); ASSERT_EQUALS("", errout.str()); + + // #6646 - init in for loop + checkUninitVar("void f() {\n" // No FP + " for (int i;;i++)\n" + " dostuff(&i);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f() {\n" // No FN + " for (int i;;i++)\n" + " a=i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Uninitialized variable: i\n", errout.str()); } void uninitvar2_4494() { @@ -3783,6 +3779,33 @@ private: check.deadPointer(); } + void isVariableUsageDeref() { + // *p + checkUninitVar("void f() {\n" + " char a[10];\n" + " char c = *a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); + + checkUninitVar("void f() {\n" + " char a[SIZE+10];\n" + " char c = *a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); + + checkUninitVar("void f() {\n" + " char a[10];\n" + " *a += 10;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str()); + + checkUninitVar("void f() {\n" + " int a[10][10];\n" + " dostuff(*a);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + void deadPointer() { checkDeadPointer("void f() {\n" " int *p = p1;\n" diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index c6bd4dde7..8c08c8c84 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -48,6 +48,7 @@ private: TEST_CASE(structmember10); TEST_CASE(structmember11); // #4168 - initialization with {} / passed by address to unknown function TEST_CASE(structmember12); // #7179 - FP unused structmember + TEST_CASE(structmember_sizeof); TEST_CASE(localvar1); TEST_CASE(localvar2); @@ -461,6 +462,22 @@ private: ASSERT_EQUALS("[test.cpp:3]: (style) struct member 'AB::a' is never used.\n", errout.str()); } + void structmember_sizeof() { + checkStructMemberUsage("struct Header {\n" + " uint8_t message_type;\n" + "}\n" + "\n" + "input.skip(sizeof(Header));"); + ASSERT_EQUALS("", errout.str()); + + checkStructMemberUsage("struct Header {\n" + " uint8_t message_type;\n" + "}\n" + "\n" + "input.skip(sizeof(struct Header));"); + ASSERT_EQUALS("", errout.str()); + } + void functionVariableUsage(const char code[], const char filename[]="test.cpp") { // Clear the error buffer.. errout.str(""); @@ -1779,6 +1796,25 @@ private: " x(a, b=2);\n" // <- if param2 is passed-by-reference then b might be used in x "}"); ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int foo() {\n" // ticket #6147 + " int a = 0;\n" + " bar(a=a+2);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int foo() {\n" // ticket #6147 + " int a = 0;\n" + " bar(a=2);\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout.str()); + + functionVariableUsage("void bar(int);\n" + "int foo() {\n" + " int a = 0;\n" + " bar(a=a+2);\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout.str()); } void localvar37() { // ticket #3078 diff --git a/test/testvarid.cpp b/test/testvarid.cpp index 0d375b339..8f9958a9d 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -134,6 +134,7 @@ private: TEST_CASE(varid_templateNamespaceFuncPtr); // #4172 TEST_CASE(varid_templateArray); TEST_CASE(varid_templateParameter); // #7046 set varid for "X": std::array Y; + TEST_CASE(varid_templateUsing); // #5781 #7273 TEST_CASE(varid_cppcast); // #6190 TEST_CASE(varid_variadicFunc); TEST_CASE(varid_typename); // #4644 @@ -2026,6 +2027,15 @@ private: tokenize(code)); } + void varid_templateUsing() { // #5781 #7273 + const char code[] = "template using X = Y;\n" + "X x;"; + TODO_ASSERT_EQUALS("\nY x@1;\n", + "1: template < class T > using X ; X = Y < T > ;\n" + "2: X < int > x@1 ;\n", + tokenize(code)); + } + void varid_cppcast() { ASSERT_EQUALS("1: const_cast < int * > ( code ) [ 0 ] = 0 ;\n", tokenize("const_cast(code)[0] = 0;")); diff --git a/tools/daca2.py b/tools/daca2.py index ef6d2d86c..80c939ad1 100644 --- a/tools/daca2.py +++ b/tools/daca2.py @@ -177,11 +177,17 @@ def scanarchive(filepath, jobs): FOLDER = None JOBS = '-j1' REV = None +SKIP = [] +WORKDIR = os.path.expanduser('~/daca2'); for arg in sys.argv[1:]: if arg[:6] == '--rev=': REV = arg[6:] elif arg[:2] == '-j': JOBS = arg + elif arg[:7] == '--skip=': + SKIP.append(arg[7:]) + elif arg[:10] == '--workdir=': + WORKDIR = arg[10:] else: FOLDER = arg @@ -189,6 +195,10 @@ if not FOLDER: print('no folder given') sys.exit(1) +if not os.path.isdir(WORKDIR): + print('workdir \'' + WORKDIR + '\' is not a folder') + sys.exit(1) + archives = getpackages(FOLDER) if len(archives) == 0: print('failed to load packages') @@ -197,12 +207,13 @@ if len(archives) == 0: print('Sleep for 10 seconds..') time.sleep(10) -workdir = os.path.expanduser('~/daca2/') +if not WORKDIR.endswith('/'): + WORKDIR = WORKDIR + '/' print('~/daca2/' + FOLDER) -if not os.path.isdir(workdir + FOLDER): - os.makedirs(workdir + FOLDER) -os.chdir(workdir + FOLDER) +if not os.path.isdir(WORKDIR + FOLDER): + os.makedirs(WORKDIR + FOLDER) +os.chdir(WORKDIR + FOLDER) try: results = open('results.txt', 'wt') @@ -214,6 +225,11 @@ try: results.close() for archive in archives: + if len(SKIP) > 0: + a = archive[:archive.rfind('/')] + a = a[a.rfind('/')+1:] + if a in SKIP: + continue scanarchive(archive, JOBS) results = open('results.txt', 'at') diff --git a/webreport.sh b/webreport.sh index f47269182..dcc5ef332 100755 --- a/webreport.sh +++ b/webreport.sh @@ -17,3 +17,5 @@ cd .. # Detect duplicate code.. ~/pmd-4.2.6/bin/cpd.sh lib/ > devinfo/cpd.txt +java -jar ~/simian-2.4.0/bin/simian-2.4.0.jar -language=c++ -reportDuplicateText -threshold=10 lib/*.cpp lib/*.h > devinfo/simian.txt +