Library: The <function> name attribute can now have a comma separated list of names
This commit is contained in:
parent
06110689a6
commit
5b287fc849
15
cfg/std.cfg
15
cfg/std.cfg
|
@ -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">
|
||||
|
|
240
lib/library.cpp
240
lib/library.cpp
|
@ -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();
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -292,25 +292,34 @@ 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());
|
||||
|
||||
{
|
||||
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 {
|
||||
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
|
||||
"<def>\n"
|
||||
|
|
Loading…
Reference in New Issue