Library: Better error reporting when loading fails

This commit is contained in:
Daniel Marjamäki 2014-05-26 17:00:41 +02:00
parent 1cb14f687f
commit ed78d3e148
7 changed files with 86 additions and 45 deletions

View File

@ -504,8 +504,35 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[])
// --library
else if (std::strncmp(argv[i], "--library=", 10) == 0) {
if (!_settings->library.load(argv[0], argv[i]+10)) {
PrintMessage("cppcheck: Failed to load library configuration file '" + std::string(argv[i]+10) + "'");
Library::Error err = _settings->library.load(argv[0], argv[i]+10);
std::string errmsg;
switch (err.errorcode) {
case Library::ErrorCode::OK:
break;
case Library::ErrorCode::FILE_NOT_FOUND:
errmsg = "File not found";
break;
case Library::ErrorCode::BAD_XML:
errmsg = "Bad XML";
break;
case Library::ErrorCode::BAD_ELEMENT:
errmsg = "Unexpected element";
break;
case Library::ErrorCode::MISSING_ATTRIBUTE:
errmsg = "Missing attribute";
break;
case Library::ErrorCode::BAD_ATTRIBUTE:
errmsg = "Bad attribute";
break;
case Library::ErrorCode::BAD_ATTRIBUTE_VALUE:
errmsg = "Bad attribute value";
break;
}
if (!err.reason.empty())
errmsg += " '" + err.reason + "'";
if (!errmsg.empty()) {
PrintMessage("cppcheck: Failed to load library configuration file '" + std::string(argv[i]+10) + "'. " + errmsg);
return false;
}
}

View File

@ -480,10 +480,10 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck, int /*argc*/, const cha
{
Settings& settings = cppcheck.settings();
_settings = &settings;
bool std = settings.library.load(argv[0], "std.cfg");
bool std = (settings.library.load(argv[0], "std.cfg").errorcode == Library::ErrorCode::OK);
bool posix = true;
if (settings.standards.posix)
posix = settings.library.load(argv[0], "posix.cfg");
posix = (settings.library.load(argv[0], "posix.cfg").errorcode == Library::ErrorCode::OK);
if (!std || !posix) {
const std::list<ErrorLogger::ErrorMessage::FileLocation> callstack;

View File

@ -518,23 +518,23 @@ bool MainWindow::LoadLibrary(Library *library, QString filename)
// Try to load the library from the project folder..
if (mProject) {
QString path = QFileInfo(mProject->GetProjectFile()->GetFilename()).canonicalPath();
if (library->load(NULL, (path+"/"+filename).toLatin1()))
if (library->load(NULL, (path+"/"+filename).toLatin1()).errorcode == Library::ErrorCode::OK)
return true;
}
// Try to load the library from the application folder..
const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath();
if (library->load(NULL, (appPath+"/"+filename).toLatin1()))
if (library->load(NULL, (appPath+"/"+filename).toLatin1()).errorcode == Library::ErrorCode::OK)
return true;
if (library->load(NULL, (appPath+"/cfg/"+filename).toLatin1()))
if (library->load(NULL, (appPath+"/cfg/"+filename).toLatin1()).errorcode == Library::ErrorCode::OK)
return true;
// Try to load the library from the cfg subfolder..
const QString datadir = mSettings->value("DATADIR", QString()).toString();
if (!datadir.isEmpty()) {
if (library->load(NULL, (datadir+"/"+filename).toLatin1()))
if (library->load(NULL, (datadir+"/"+filename).toLatin1()).errorcode == Library::ErrorCode::OK)
return true;
if (library->load(NULL, (datadir+"/cfg/"+filename).toLatin1()))
if (library->load(NULL, (datadir+"/cfg/"+filename).toLatin1()).errorcode == Library::ErrorCode::OK)
return true;
}

View File

@ -30,19 +30,20 @@ Library::Library() : allocid(0)
{
}
bool Library::load(const char exename[], const char path[])
Library::Error Library::load(const char exename[], const char path[])
{
if (std::strchr(path,',') != nullptr) {
bool ret = true;
std::string p(path);
while (p.find(",") != std::string::npos) {
const std::string::size_type pos = p.find(",");
ret &= load(exename, p.substr(0,pos).c_str());
const Error &e = load(exename, p.substr(0,pos).c_str());
if (e.errorcode != ErrorCode::OK)
return e;
p = p.substr(pos+1);
}
if (!p.empty())
ret &= load(exename, p.c_str());
return ret;
return load(exename, p.c_str());
return Error();
}
// open file..
@ -62,7 +63,7 @@ bool Library::load(const char exename[], const char path[])
const std::string cfgfolder(CFGDIR);
#else
if (!exename)
return false;
return Error(ErrorCode::FILE_NOT_FOUND);
const std::string cfgfolder(Path::fromNativeSeparators(Path::getPathFromFilename(exename)) + "cfg");
#endif
const char *sep = (!cfgfolder.empty() && cfgfolder[cfgfolder.size()-1U]=='/' ? "" : "/");
@ -71,24 +72,24 @@ bool Library::load(const char exename[], const char path[])
}
}
return (error == tinyxml2::XML_NO_ERROR) && load(doc);
return (error == tinyxml2::XML_NO_ERROR) ? load(doc) : Error(ErrorCode::BAD_XML);
}
bool Library::loadxmldata(const char xmldata[], std::size_t len)
{
tinyxml2::XMLDocument doc;
return (tinyxml2::XML_NO_ERROR == doc.Parse(xmldata, len)) && load(doc);
return (tinyxml2::XML_NO_ERROR == doc.Parse(xmldata, len)) && (load(doc).errorcode == ErrorCode::OK);
}
bool Library::load(const tinyxml2::XMLDocument &doc)
Library::Error Library::load(const tinyxml2::XMLDocument &doc)
{
const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
if (rootnode == nullptr)
return false;
return Error(ErrorCode::BAD_XML);
if (strcmp(rootnode->Name(),"def") != 0)
return false;
return Error(ErrorCode::BAD_ELEMENT, rootnode->Name());
for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) {
if (strcmp(node->Name(),"memory")==0 || strcmp(node->Name(),"resource")==0) {
@ -124,17 +125,17 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
else if (strcmp(memorynode->Name(),"use")==0)
use.insert(memorynode->GetText());
else
return false;
return Error(ErrorCode::BAD_ELEMENT, memorynode->Name());
}
}
else if (strcmp(node->Name(),"define")==0) {
const char *name = node->Attribute("name");
if (name == nullptr)
return false;
return Error(ErrorCode::MISSING_ATTRIBUTE, "name");
const char *value = node->Attribute("value");
if (value == nullptr)
return false;
return Error(ErrorCode::MISSING_ATTRIBUTE, "value");
defines.push_back(std::string("#define ") +
name +
" " +
@ -145,7 +146,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
else if (strcmp(node->Name(),"function")==0) {
const char *name = node->Attribute("name");
if (name == nullptr)
return false;
return Error(ErrorCode::MISSING_ATTRIBUTE, "name");
for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) {
if (strcmp(functionnode->Name(),"noreturn")==0)
@ -181,7 +182,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
// Validate the validation expression
const char *p = argnode->GetText();
if (!std::isdigit(*p))
return false;
return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, argnode->GetText());
for (; *p; p++) {
if (std::isdigit(*p))
continue;
@ -189,7 +190,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
continue;
if (*p == ',' && *(p+1) != ',')
continue;
return false;
return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, argnode->GetText());
}
// Set validation expression
@ -197,7 +198,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
}
else
return false;
return Error(ErrorCode::BAD_ATTRIBUTE, argnode->Name());
}
argumentChecks[name][nr].notbool = notbool;
argumentChecks[name][nr].notnull = notnull;
@ -212,18 +213,18 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
const tinyxml2::XMLAttribute* secure = functionnode->FindAttribute("secure");
_formatstr[name] = std::make_pair(scan && scan->BoolValue(), secure && secure->BoolValue());
} else
return false;
return Error(ErrorCode::BAD_ELEMENT, functionnode->Name());
}
}
else if (strcmp(node->Name(), "reflection") == 0) {
for (const tinyxml2::XMLElement *reflectionnode = node->FirstChildElement(); reflectionnode; reflectionnode = reflectionnode->NextSiblingElement()) {
if (strcmp(reflectionnode->Name(), "call") != 0)
return false;
return Error(ErrorCode::BAD_ELEMENT, reflectionnode->Name());
const char * const argString = reflectionnode->Attribute("arg");
if (!argString)
return false;
return Error(ErrorCode::MISSING_ATTRIBUTE, "arg");
_reflection[reflectionnode->GetText()] = atoi(argString);
}
@ -232,7 +233,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
else if (strcmp(node->Name(), "markup") == 0) {
const char * const extension = node->Attribute("ext");
if (!extension)
return false;
return Error(ErrorCode::MISSING_ATTRIBUTE, "ext");
_markupExtensions.insert(extension);
const char * const reporterrors = node->Attribute("reporterrors");
@ -246,18 +247,18 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
if (strcmp(librarynode->Name(), "keyword") == 0)
_keywords[extension].insert(librarynode->Attribute("name"));
else
return false;
return Error(ErrorCode::BAD_ELEMENT, librarynode->Name());
}
}
else if (strcmp(markupnode->Name(), "exported") == 0) {
for (const tinyxml2::XMLElement *exporter = markupnode->FirstChildElement(); exporter; exporter = exporter->NextSiblingElement()) {
if (strcmp(exporter->Name(), "exporter") != 0)
return false;
return Error(ErrorCode::BAD_ELEMENT, exporter->Name());
const char * const prefix = exporter->Attribute("prefix");
if (!prefix)
return false;
return Error(ErrorCode::MISSING_ATTRIBUTE, "prefix");
for (const tinyxml2::XMLElement *e = exporter->FirstChildElement(); e; e = e->NextSiblingElement()) {
if (strcmp(e->Name(), "prefix") == 0)
@ -265,7 +266,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
else if (strcmp(e->Name(), "suffix") == 0)
_exporters[prefix].addSuffix(e->GetText());
else
return false;
return Error(ErrorCode::BAD_ELEMENT, e->Name());
}
}
}
@ -275,7 +276,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
if (strcmp(librarynode->Name(), "importer") == 0)
_importers[extension].insert(librarynode->GetText());
else
return false;
return Error(ErrorCode::BAD_ELEMENT, librarynode->Name());
}
}
@ -297,19 +298,19 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
}
else
return false;
return Error(ErrorCode::BAD_ELEMENT, blocknode->Name());
}
}
else
return false;
return Error(ErrorCode::BAD_ELEMENT, markupnode->Name());
}
}
else
return false;
return Error(ErrorCode::BAD_ELEMENT, node->Name());
}
return true;
return Error(ErrorCode::OK);
}
bool Library::isargvalid(const std::string &functionName, int argnr, const MathLib::bigint argvalue) const

View File

@ -46,9 +46,22 @@ class CPPCHECKLIB Library {
public:
Library();
bool load(const char exename [], const char path []);
enum ErrorCode { OK, FILE_NOT_FOUND, BAD_XML, BAD_ELEMENT, MISSING_ATTRIBUTE, BAD_ATTRIBUTE, BAD_ATTRIBUTE_VALUE };
class Error {
public:
Error() : errorcode(ErrorCode::OK) , reason("") {}
explicit Error(ErrorCode e) : errorcode(e) , reason("") {}
Error(ErrorCode e, const std::string &r) : errorcode(e), reason(r) {}
ErrorCode errorcode;
std::string reason;
};
Error load(const char exename [], const char path []);
Error load(const tinyxml2::XMLDocument &doc);
/** this is primarily meant for unit tests. it only returns true/false */
bool loadxmldata(const char xmldata[], std::size_t len);
bool load(const tinyxml2::XMLDocument &doc);
/** get allocation id for function by name */
int alloc(const char name[]) const {

View File

@ -493,7 +493,7 @@ private:
errout.str("");
Settings settings;
if (!settings.library.load("./testrunner", "../cfg/std.cfg") && !settings.library.load("./testrunner", "cfg/std.cfg")) {
if ((settings.library.load("./testrunner", "../cfg/std.cfg").errorcode != Library::ErrorCode::OK) && (settings.library.load("./testrunner", "cfg/std.cfg").errorcode != Library::ErrorCode::OK)) {
complainMissingLib("std.cfg");
return "";
}

View File

@ -85,9 +85,9 @@ public:
#define REGISTER_TEST( CLASSNAME ) namespace { CLASSNAME instance; }
#ifdef _WIN32
#define LOAD_LIB_2( LIB, NAME ) { if (!(LIB).load("./testrunner", "../cfg/" NAME) && !(LIB).load("./testrunner", "cfg/" NAME)) { complainMissingLib(NAME); return; } }
#define LOAD_LIB_2( LIB, NAME ) { if (((LIB).load("./testrunner", "../cfg/" NAME).errorcode != Library::ErrorCode::OK) && ((LIB).load("./testrunner", "cfg/" NAME).errorcode != Library::ErrorCode::OK)) { complainMissingLib(NAME); return; } }
#else
#define LOAD_LIB_2( LIB, NAME ) { if (!(LIB).load("./testrunner", "cfg/" NAME)) { complainMissingLib(NAME); return; } }
#define LOAD_LIB_2( LIB, NAME ) { if ((LIB).load("./testrunner", "cfg/" NAME).errorcode != Library::ErrorCode::OK) { complainMissingLib(NAME); return; } }
#endif
#define LOAD_LIB( NAME ) { LOAD_LIB_2(_lib, NAME); }