Library: The <function> name attribute can now have a comma separated list of names

This commit is contained in:
Daniel Marjamäki 2015-08-09 21:27:57 +02:00
parent 06110689a6
commit 5b287fc849
4 changed files with 150 additions and 132 deletions

View File

@ -4507,20 +4507,7 @@
</arg>
</function>
<!-- char *strcpy(char *desstr, const char *srcstr); -->
<function name="strcpy">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1">
<not-null/>
<minsize type="strlen" arg="2"/>
</arg>
<arg nr="2">
<not-null/>
<not-uninit/>
<strz/>
</arg>
</function>
<function name="std::strcpy">
<function name="strcpy,std::strcpy">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1">

View File

@ -178,119 +178,17 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
if (name_char == nullptr)
return Error(MISSING_ATTRIBUTE, "name");
std::string name = name_char;
for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) {
const std::string functionnodename = functionnode->Name();
if (functionnodename == "noreturn")
_noreturn[name] = (strcmp(functionnode->GetText(), "true") == 0);
else if (functionnodename == "pure")
functionpure.insert(name);
else if (functionnodename == "const") {
functionconst.insert(name);
functionpure.insert(name); // a constant function is pure
} else if (functionnodename == "leak-ignore")
leakignore.insert(name);
else if (functionnodename == "use-retval")
useretval.insert(name);
else if (functionnodename == "arg" && functionnode->Attribute("nr") != nullptr) {
const bool bAnyArg = strcmp(functionnode->Attribute("nr"),"any")==0;
const int nr = (bAnyArg) ? -1 : atoi(functionnode->Attribute("nr"));
bool notbool = false;
bool notnull = false;
bool notuninit = false;
bool formatstr = false;
bool strz = false;
std::string& valid = argumentChecks[name][nr].valid;
std::list<ArgumentChecks::MinSize>& minsizes = argumentChecks[name][nr].minsizes;
for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) {
const std::string argnodename = argnode->Name();
if (argnodename == "not-bool")
notbool = true;
else if (argnodename == "not-null")
notnull = true;
else if (argnodename == "not-uninit")
notuninit = true;
else if (argnodename == "formatstr")
formatstr = true;
else if (argnodename == "strz")
strz = true;
else if (argnodename == "valid") {
// Validate the validation expression
const char *p = argnode->GetText();
bool error = false;
bool range = false;
for (; *p; p++) {
if (std::isdigit(*p))
error |= (*(p+1) == '-');
else if (*p == ':')
error |= range;
else if (*p == '-')
error |= (!std::isdigit(*(p+1)));
else if (*p == ',')
range = false;
else
error = true;
range |= (*p == ':');
}
if (error)
return Error(BAD_ATTRIBUTE_VALUE, argnode->GetText());
// Set validation expression
valid = argnode->GetText();
}
else if (argnodename == "minsize") {
const char *typeattr = argnode->Attribute("type");
if (!typeattr)
return Error(MISSING_ATTRIBUTE, "type");
ArgumentChecks::MinSize::Type type;
if (strcmp(typeattr,"strlen")==0)
type = ArgumentChecks::MinSize::STRLEN;
else if (strcmp(typeattr,"argvalue")==0)
type = ArgumentChecks::MinSize::ARGVALUE;
else if (strcmp(typeattr,"sizeof")==0)
type = ArgumentChecks::MinSize::SIZEOF;
else if (strcmp(typeattr,"mul")==0)
type = ArgumentChecks::MinSize::MUL;
else
return Error(BAD_ATTRIBUTE_VALUE, typeattr);
const char *argattr = argnode->Attribute("arg");
if (!argattr)
return Error(MISSING_ATTRIBUTE, "arg");
if (strlen(argattr) != 1 || argattr[0]<'0' || argattr[0]>'9')
return Error(BAD_ATTRIBUTE_VALUE, argattr);
minsizes.push_back(ArgumentChecks::MinSize(type,argattr[0]-'0'));
if (type == ArgumentChecks::MinSize::MUL) {
const char *arg2attr = argnode->Attribute("arg2");
if (!arg2attr)
return Error(MISSING_ATTRIBUTE, "arg2");
if (strlen(arg2attr) != 1 || arg2attr[0]<'0' || arg2attr[0]>'9')
return Error(BAD_ATTRIBUTE_VALUE, arg2attr);
minsizes.back().arg2 = arg2attr[0] - '0';
}
}
else
unknown_elements.insert(argnodename);
}
argumentChecks[name][nr].notbool = notbool;
argumentChecks[name][nr].notnull = notnull;
argumentChecks[name][nr].notuninit = notuninit;
argumentChecks[name][nr].formatstr = formatstr;
argumentChecks[name][nr].strz = strz;
} else if (functionnodename == "ignorefunction") {
_ignorefunction.insert(name);
} else if (functionnodename == "formatstr") {
const tinyxml2::XMLAttribute* scan = functionnode->FindAttribute("scan");
const tinyxml2::XMLAttribute* secure = functionnode->FindAttribute("secure");
_formatstr[name] = std::make_pair(scan && scan->BoolValue(), secure && secure->BoolValue());
} else
unknown_elements.insert(functionnodename);
while (name.find(",") != std::string::npos) {
const std::string::size_type pos = name.find(",");
const Error &err = loadFunction(node, name.substr(0,pos), unknown_elements);
if (err.errorcode != ErrorCode::OK)
return err;
name.erase(0,pos+1);
}
const Error &err = loadFunction(node, name, unknown_elements);
if (err.errorcode != ErrorCode::OK)
return err;
}
else if (nodename == "reflection") {
@ -581,6 +479,126 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
return Error(OK);
}
Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set<std::string> &unknown_elements)
{
if (name.empty())
return Error(OK);
for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) {
const std::string functionnodename = functionnode->Name();
if (functionnodename == "noreturn")
_noreturn[name] = (strcmp(functionnode->GetText(), "true") == 0);
else if (functionnodename == "pure")
functionpure.insert(name);
else if (functionnodename == "const") {
functionconst.insert(name);
functionpure.insert(name); // a constant function is pure
} else if (functionnodename == "leak-ignore")
leakignore.insert(name);
else if (functionnodename == "use-retval")
useretval.insert(name);
else if (functionnodename == "arg" && functionnode->Attribute("nr") != nullptr) {
const bool bAnyArg = strcmp(functionnode->Attribute("nr"),"any")==0;
const int nr = (bAnyArg) ? -1 : atoi(functionnode->Attribute("nr"));
bool notbool = false;
bool notnull = false;
bool notuninit = false;
bool formatstr = false;
bool strz = false;
std::string& valid = argumentChecks[name][nr].valid;
std::list<ArgumentChecks::MinSize>& minsizes = argumentChecks[name][nr].minsizes;
for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) {
const std::string argnodename = argnode->Name();
if (argnodename == "not-bool")
notbool = true;
else if (argnodename == "not-null")
notnull = true;
else if (argnodename == "not-uninit")
notuninit = true;
else if (argnodename == "formatstr")
formatstr = true;
else if (argnodename == "strz")
strz = true;
else if (argnodename == "valid") {
// Validate the validation expression
const char *p = argnode->GetText();
bool error = false;
bool range = false;
for (; *p; p++) {
if (std::isdigit(*p))
error |= (*(p+1) == '-');
else if (*p == ':')
error |= range;
else if (*p == '-')
error |= (!std::isdigit(*(p+1)));
else if (*p == ',')
range = false;
else
error = true;
range |= (*p == ':');
}
if (error)
return Error(BAD_ATTRIBUTE_VALUE, argnode->GetText());
// Set validation expression
valid = argnode->GetText();
}
else if (argnodename == "minsize") {
const char *typeattr = argnode->Attribute("type");
if (!typeattr)
return Error(MISSING_ATTRIBUTE, "type");
ArgumentChecks::MinSize::Type type;
if (strcmp(typeattr,"strlen")==0)
type = ArgumentChecks::MinSize::STRLEN;
else if (strcmp(typeattr,"argvalue")==0)
type = ArgumentChecks::MinSize::ARGVALUE;
else if (strcmp(typeattr,"sizeof")==0)
type = ArgumentChecks::MinSize::SIZEOF;
else if (strcmp(typeattr,"mul")==0)
type = ArgumentChecks::MinSize::MUL;
else
return Error(BAD_ATTRIBUTE_VALUE, typeattr);
const char *argattr = argnode->Attribute("arg");
if (!argattr)
return Error(MISSING_ATTRIBUTE, "arg");
if (strlen(argattr) != 1 || argattr[0]<'0' || argattr[0]>'9')
return Error(BAD_ATTRIBUTE_VALUE, argattr);
minsizes.push_back(ArgumentChecks::MinSize(type,argattr[0]-'0'));
if (type == ArgumentChecks::MinSize::MUL) {
const char *arg2attr = argnode->Attribute("arg2");
if (!arg2attr)
return Error(MISSING_ATTRIBUTE, "arg2");
if (strlen(arg2attr) != 1 || arg2attr[0]<'0' || arg2attr[0]>'9')
return Error(BAD_ATTRIBUTE_VALUE, arg2attr);
minsizes.back().arg2 = arg2attr[0] - '0';
}
}
else
unknown_elements.insert(argnodename);
}
argumentChecks[name][nr].notbool = notbool;
argumentChecks[name][nr].notnull = notnull;
argumentChecks[name][nr].notuninit = notuninit;
argumentChecks[name][nr].formatstr = formatstr;
argumentChecks[name][nr].strz = strz;
} else if (functionnodename == "ignorefunction") {
_ignorefunction.insert(name);
} else if (functionnodename == "formatstr") {
const tinyxml2::XMLAttribute* scan = functionnode->FindAttribute("scan");
const tinyxml2::XMLAttribute* secure = functionnode->FindAttribute("secure");
_formatstr[name] = std::make_pair(scan && scan->BoolValue(), secure && secure->BoolValue());
} else
unknown_elements.insert(functionnodename);
}
return Error(OK);
}
bool Library::isargvalid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const
{
const ArgumentChecks *ac = getarg(ftok, argnr);

View File

@ -33,6 +33,7 @@
class TokenList;
namespace tinyxml2 {
class XMLDocument;
class XMLElement;
}
/// @addtogroup Core
@ -379,6 +380,9 @@ public:
}
private:
// load a <function> xml node
Error loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set<std::string> &unknown_elements);
class ExportedFunctions {
public:
void addPrefix(const std::string& prefix) {

View File

@ -292,23 +292,32 @@ private:
void function_namespace() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"Foo::foo\">\n"
" <function name=\"Foo::foo,bar\">\n"
" <noreturn>false</noreturn>\n"
" </function>\n"
"</def>";
tinyxml2::XMLDocument doc;
doc.Parse(xmldata, sizeof(xmldata));
TokenList tokenList(nullptr);
std::istringstream istr("Foo::foo();");
tokenList.createTokens(istr);
Library library;
library.load(doc);
ASSERT(library.use.empty());
ASSERT(library.leakignore.empty());
ASSERT(library.argumentChecks.empty());
ASSERT(library.isnotnoreturn(tokenList.front()->tokAt(2)));
{
TokenList tokenList(nullptr);
std::istringstream istr("Foo::foo();");
tokenList.createTokens(istr);
ASSERT(library.isnotnoreturn(tokenList.front()->tokAt(2)));
}
{
TokenList tokenList(nullptr);
std::istringstream istr("bar();");
tokenList.createTokens(istr);
ASSERT(library.isnotnoreturn(tokenList.front()));
}
}
void memory() const {