Merge branch 'master' into project
This commit is contained in:
commit
8070c536d5
|
@ -39,6 +39,30 @@
|
|||
<not-uninit/>
|
||||
</arg>
|
||||
</function>
|
||||
<!-- http://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html -->
|
||||
<!-- int access(const char *pathname, int amode); -->
|
||||
<function name="access">
|
||||
<use-retval/>
|
||||
<noreturn>false</noreturn>
|
||||
<leak-ignore/>
|
||||
<arg nr="1">
|
||||
<not-uninit/>
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="2">
|
||||
<not-uninit/>
|
||||
</arg>
|
||||
</function>
|
||||
<!-- http://man7.org/linux/man-pages/man3/adjtime.3.html -->
|
||||
<!-- int adjtime(const struct timeval *delta, struct timeval *olddelta); -->
|
||||
<function name="adjtime">
|
||||
<noreturn>false</noreturn>
|
||||
<leak-ignore/>
|
||||
<arg nr="1">
|
||||
<not-uninit/>
|
||||
</arg>
|
||||
<arg nr="2"/>
|
||||
</function>
|
||||
<!-- struct group *getgrnam(const char *name); -->
|
||||
<function name="getgrnam">
|
||||
<use-retval/>
|
||||
|
@ -116,6 +140,43 @@
|
|||
<not-null/>
|
||||
</arg>
|
||||
</function>
|
||||
<!-- http://pubs.opengroup.org/onlinepubs/009695399/functions/fdatasync.html -->
|
||||
<!-- int fdatasync(int fildes); -->
|
||||
<function name="fdatasync">
|
||||
<noreturn>false</noreturn>
|
||||
<leak-ignore/>
|
||||
<arg nr="1">
|
||||
<not-uninit/>
|
||||
</arg>
|
||||
</function>
|
||||
<!-- http://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html -->
|
||||
<!-- int fnmatch(const char *pattern, const char *string, int flags); -->
|
||||
<function name="fnmatch">
|
||||
<pure/>
|
||||
<use-retval/>
|
||||
<noreturn>false</noreturn>
|
||||
<leak-ignore/>
|
||||
<arg nr="1">
|
||||
<not-uninit/>
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="2">
|
||||
<not-uninit/>
|
||||
<not-null/>
|
||||
</arg>
|
||||
<arg nr="3">
|
||||
<not-uninit/>
|
||||
</arg>
|
||||
</function>
|
||||
<!-- http://pubs.opengroup.org/onlinepubs/009695399/functions/fsync.html -->
|
||||
<!-- int fsync(int fildes); -->
|
||||
<function name="fsync">
|
||||
<noreturn>false</noreturn>
|
||||
<leak-ignore/>
|
||||
<arg nr="1">
|
||||
<not-uninit/>
|
||||
</arg>
|
||||
</function>
|
||||
<!-- int truncate(const char *path, off_t length); -->
|
||||
<function name="truncate">
|
||||
<noreturn>false</noreturn>
|
||||
|
|
|
@ -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<const Token *> getMacroParameters(const Token *nameToken, bool def) const {
|
||||
std::vector<const Token *> getMacroParameters(const Token *nameToken, bool calledInDefine) const {
|
||||
if (!nameToken->next || nameToken->next->op != '(' || !functionLike())
|
||||
return std::vector<const Token *>();
|
||||
|
||||
std::vector<const Token *> 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<const Token*> parametertokens1(getMacroParameters(nameToken, !expandedmacros1.empty()));
|
||||
const bool calledInDefine = (loc.fileIndex != nameToken->location.fileIndex ||
|
||||
loc.line < nameToken->location.line);
|
||||
|
||||
std::vector<const Token*> 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<std::string, TokenList*> &filedata) {
|
||||
for (std::map<std::string, TokenList*>::iterator it = filedata.begin(); it != filedata.end(); ++it)
|
||||
delete it->second;
|
||||
filedata.clear();
|
||||
}
|
||||
|
|
|
@ -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<std::string, TokenList*> 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<std::string> &files, const std::map<std::string, TokenList*> &filedata, const DUI &dui, OutputList *outputList = 0, std::list<MacroUsage> *macroUsage = 0);
|
||||
|
||||
/**
|
||||
* Deallocate data
|
||||
*/
|
||||
SIMPLECPP_LIB void cleanup(std::map<std::string, TokenList*> &filedata);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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<std::string> &constFunctions)
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<std::string> 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);
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@ namespace tinyxml2 {
|
|||
* @brief Library definitions handling
|
||||
*/
|
||||
class CPPCHECKLIB Library {
|
||||
friend class TestSymbolDatabase; // For testing only
|
||||
|
||||
public:
|
||||
Library();
|
||||
|
||||
|
|
|
@ -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<std::string>::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<simplecpp::MacroUsage>::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<Directive>::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<simplecpp::Location> 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<simplecpp::MacroUsage> ¯oUsageList)
|
||||
{
|
||||
const bool printInformation = _settings.isEnabled("information");
|
||||
|
||||
// fill up "macros" with empty configuration macros
|
||||
std::set<std::string> 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<std::string>::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<std::string> defines;
|
||||
splitcfg(cfg, defines, std::string());
|
||||
for (std::list<std::string>::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<simplecpp::MacroUsage>::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<Directive>::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<ErrorLogger::ErrorMessage::FileLocation> 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 ..
|
||||
}
|
||||
|
||||
|
|
|
@ -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<simplecpp::MacroUsage> ¯oUsageList);
|
||||
void validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string ¯o);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<Scope*>::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<Token *>(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<Type>::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<std::string, const Function *>::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<Token *>(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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
125
lib/tokenize.cpp
125
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<std::string> notstart_c = make_container< std::set<std::st
|
|||
<< "goto" << "NOT" << "return" << "sizeof"<< "typedef";
|
||||
static const std::set<std::string> notstart_cpp = make_container< std::set<std::string> > ()
|
||||
<< 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<b>[" 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
|
||||
|
|
|
@ -695,6 +695,10 @@ Checking test.c...
|
|||
configuration files. It is available in the <literal>View</literal>
|
||||
menu. All settings are not documented in this manual.</para>
|
||||
|
||||
<para>If you have a question about the <literal>.cfg</literal> file
|
||||
format it is recommended you ask in the forum
|
||||
(http://sourceforge.net/p/cppcheck/discussion/).</para>
|
||||
|
||||
<para>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.</para>
|
||||
|
@ -1118,6 +1122,23 @@ Checking minsize.c...
|
|||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>strz</title>
|
||||
|
||||
<para>This setting is not used by Cppcheck currently. But with this
|
||||
you can say that an argument must be a zero-terminated
|
||||
string.</para>
|
||||
|
||||
<para><programlisting><?xml version="1.0"?>
|
||||
<def>
|
||||
<function name="do_something">
|
||||
<arg nr="1">
|
||||
<strz/>
|
||||
</arg>
|
||||
</function>
|
||||
</def></programlisting></para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
@ -1209,11 +1230,19 @@ Checking useretval.c...
|
|||
</section>
|
||||
|
||||
<section>
|
||||
<title>pure</title>
|
||||
<title>pure and const</title>
|
||||
|
||||
<para>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.</para>
|
||||
<para>These correspond to the GCC function attributes pure and
|
||||
const.</para>
|
||||
|
||||
<para>A pure function has no effects except to return a value, and its
|
||||
return value depends only on the parameters and global
|
||||
variables.</para>
|
||||
|
||||
<para>A const function has no effects except to return a value, and
|
||||
its return value depends only on the parameters.</para>
|
||||
|
||||
<para>Here is an example code:</para>
|
||||
|
||||
<programlisting>void f(int x)
|
||||
{
|
||||
|
@ -1224,24 +1253,29 @@ Checking useretval.c...
|
|||
}
|
||||
}</programlisting>
|
||||
|
||||
<para>Cppcheck reports no warning</para>
|
||||
<para>If <literal>calculate()</literal> is a const function then the
|
||||
result of <literal>calculate(x)</literal> will be the same in both
|
||||
conditions, since the same parameter value is used.</para>
|
||||
|
||||
<programlisting># cppcheck pure.c
|
||||
Checking pure.c...</programlisting>
|
||||
<para>Cppcheck normally assumes that the result might be different,
|
||||
and reports no warning for the code:</para>
|
||||
|
||||
<para>If a proper <literal>lib.cfg</literal> is provided, the
|
||||
<programlisting># cppcheck const.c
|
||||
Checking const.c...</programlisting>
|
||||
|
||||
<para>If a proper <literal>const.cfg</literal> is provided, the
|
||||
unreachable code is detected:</para>
|
||||
|
||||
<programlisting># 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.</programlisting>
|
||||
<programlisting># 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.</programlisting>
|
||||
|
||||
<para>Here is a minimal <literal>pure.cfg</literal> file:</para>
|
||||
<para>Here is a minimal <literal>const.cfg</literal> file:</para>
|
||||
|
||||
<programlisting><?xml version="1.0"?>
|
||||
<def>
|
||||
<function name="calculate">
|
||||
<pure/>
|
||||
<const/>
|
||||
<arg nr="1"/>
|
||||
</function>
|
||||
</def></programlisting>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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<std::string> files;
|
||||
std::istringstream istr(filedata);
|
||||
simplecpp::TokenList tokens(istr,files);
|
||||
tokens.removeComments();
|
||||
const std::set<std::string> configs = preprocessor.getConfigs(tokens);
|
||||
std::string ret;
|
||||
for (std::set<std::string>::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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(2, static_cast<unsigned int>(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<std::string, std::string> 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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(3, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(2, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(3, static_cast<unsigned int>(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<std::string, std::string> 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<unsigned int>(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<std::string, std::string> 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<unsigned int>(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<std::string, std::string> 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<unsigned int>(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<std::string, std::string> 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<unsigned int>(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<std::string, std::string> 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<unsigned int>(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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(2, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Expected configurations: "" and "ABC"
|
||||
ASSERT_EQUALS(2, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Expected configurations: "" and "ABC"
|
||||
ASSERT_EQUALS(2, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(1, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(1, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(3, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(3, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(1, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(3, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(3, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(3, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(4, static_cast<unsigned int>(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<std::string, std::string> 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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(3, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(1, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(3, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(1, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(2, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
TODO_ASSERT_EQUALS(2, 1, static_cast<unsigned int>(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<std::string, std::string> actual;
|
||||
preprocess(filedata, actual);
|
||||
|
||||
// Compare results..
|
||||
ASSERT_EQUALS(2, static_cast<unsigned int>(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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> 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<std::string, std::string> 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<std::string> files;
|
||||
std::istringstream istr(filedata);
|
||||
simplecpp::TokenList tokens(istr,files);
|
||||
tokens.removeComments();
|
||||
const std::set<std::string> configs = preprocessor.getConfigs(tokens);
|
||||
std::string ret;
|
||||
for (std::set<std::string>::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<simplecpp::MacroUsage> macroUsageList;
|
||||
std::vector<std::string> 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
|
||||
|
|
|
@ -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<int> l1;\n"
|
||||
" list<int> l2;\n"
|
||||
" list<int>::iterator it = l1.begin();\n"
|
||||
" l2.insert(it, l1.end());\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void iterator4() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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<B, 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));
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<int,X> 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<class T> using X = Y<T>;\n"
|
||||
"X<int> x;";
|
||||
TODO_ASSERT_EQUALS("\nY<int> 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<int *>(code)[0] = 0;"));
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue