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 // --library
else if (std::strncmp(argv[i], "--library=", 10) == 0) { else if (std::strncmp(argv[i], "--library=", 10) == 0) {
if (!_settings->library.load(argv[0], argv[i]+10)) { Library::Error err = _settings->library.load(argv[0], argv[i]+10);
PrintMessage("cppcheck: Failed to load library configuration file '" + std::string(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; return false;
} }
} }

View File

@ -480,10 +480,10 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck, int /*argc*/, const cha
{ {
Settings& settings = cppcheck.settings(); Settings& settings = cppcheck.settings();
_settings = &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; bool posix = true;
if (settings.standards.posix) 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) { if (!std || !posix) {
const std::list<ErrorLogger::ErrorMessage::FileLocation> callstack; 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.. // Try to load the library from the project folder..
if (mProject) { if (mProject) {
QString path = QFileInfo(mProject->GetProjectFile()->GetFilename()).canonicalPath(); 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; return true;
} }
// Try to load the library from the application folder.. // Try to load the library from the application folder..
const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath(); 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; return true;
if (library->load(NULL, (appPath+"/cfg/"+filename).toLatin1())) if (library->load(NULL, (appPath+"/cfg/"+filename).toLatin1()).errorcode == Library::ErrorCode::OK)
return true; return true;
// Try to load the library from the cfg subfolder.. // Try to load the library from the cfg subfolder..
const QString datadir = mSettings->value("DATADIR", QString()).toString(); const QString datadir = mSettings->value("DATADIR", QString()).toString();
if (!datadir.isEmpty()) { if (!datadir.isEmpty()) {
if (library->load(NULL, (datadir+"/"+filename).toLatin1())) if (library->load(NULL, (datadir+"/"+filename).toLatin1()).errorcode == Library::ErrorCode::OK)
return true; return true;
if (library->load(NULL, (datadir+"/cfg/"+filename).toLatin1())) if (library->load(NULL, (datadir+"/cfg/"+filename).toLatin1()).errorcode == Library::ErrorCode::OK)
return true; 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) { if (std::strchr(path,',') != nullptr) {
bool ret = true;
std::string p(path); std::string p(path);
while (p.find(",") != std::string::npos) { while (p.find(",") != std::string::npos) {
const std::string::size_type pos = p.find(","); 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); p = p.substr(pos+1);
} }
if (!p.empty()) if (!p.empty())
ret &= load(exename, p.c_str()); return load(exename, p.c_str());
return ret; return Error();
} }
// open file.. // open file..
@ -62,7 +63,7 @@ bool Library::load(const char exename[], const char path[])
const std::string cfgfolder(CFGDIR); const std::string cfgfolder(CFGDIR);
#else #else
if (!exename) if (!exename)
return false; return Error(ErrorCode::FILE_NOT_FOUND);
const std::string cfgfolder(Path::fromNativeSeparators(Path::getPathFromFilename(exename)) + "cfg"); const std::string cfgfolder(Path::fromNativeSeparators(Path::getPathFromFilename(exename)) + "cfg");
#endif #endif
const char *sep = (!cfgfolder.empty() && cfgfolder[cfgfolder.size()-1U]=='/' ? "" : "/"); 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) bool Library::loadxmldata(const char xmldata[], std::size_t len)
{ {
tinyxml2::XMLDocument doc; 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(); const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
if (rootnode == nullptr) if (rootnode == nullptr)
return false; return Error(ErrorCode::BAD_XML);
if (strcmp(rootnode->Name(),"def") != 0) 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()) { for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) {
if (strcmp(node->Name(),"memory")==0 || strcmp(node->Name(),"resource")==0) { 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) else if (strcmp(memorynode->Name(),"use")==0)
use.insert(memorynode->GetText()); use.insert(memorynode->GetText());
else else
return false; return Error(ErrorCode::BAD_ELEMENT, memorynode->Name());
} }
} }
else if (strcmp(node->Name(),"define")==0) { else if (strcmp(node->Name(),"define")==0) {
const char *name = node->Attribute("name"); const char *name = node->Attribute("name");
if (name == nullptr) if (name == nullptr)
return false; return Error(ErrorCode::MISSING_ATTRIBUTE, "name");
const char *value = node->Attribute("value"); const char *value = node->Attribute("value");
if (value == nullptr) if (value == nullptr)
return false; return Error(ErrorCode::MISSING_ATTRIBUTE, "value");
defines.push_back(std::string("#define ") + defines.push_back(std::string("#define ") +
name + name +
" " + " " +
@ -145,7 +146,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
else if (strcmp(node->Name(),"function")==0) { else if (strcmp(node->Name(),"function")==0) {
const char *name = node->Attribute("name"); const char *name = node->Attribute("name");
if (name == nullptr) if (name == nullptr)
return false; return Error(ErrorCode::MISSING_ATTRIBUTE, "name");
for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) { for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) {
if (strcmp(functionnode->Name(),"noreturn")==0) if (strcmp(functionnode->Name(),"noreturn")==0)
@ -181,7 +182,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
// Validate the validation expression // Validate the validation expression
const char *p = argnode->GetText(); const char *p = argnode->GetText();
if (!std::isdigit(*p)) if (!std::isdigit(*p))
return false; return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, argnode->GetText());
for (; *p; p++) { for (; *p; p++) {
if (std::isdigit(*p)) if (std::isdigit(*p))
continue; continue;
@ -189,7 +190,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
continue; continue;
if (*p == ',' && *(p+1) != ',') if (*p == ',' && *(p+1) != ',')
continue; continue;
return false; return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, argnode->GetText());
} }
// Set validation expression // Set validation expression
@ -197,7 +198,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
} }
else else
return false; return Error(ErrorCode::BAD_ATTRIBUTE, argnode->Name());
} }
argumentChecks[name][nr].notbool = notbool; argumentChecks[name][nr].notbool = notbool;
argumentChecks[name][nr].notnull = notnull; argumentChecks[name][nr].notnull = notnull;
@ -212,18 +213,18 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
const tinyxml2::XMLAttribute* secure = functionnode->FindAttribute("secure"); const tinyxml2::XMLAttribute* secure = functionnode->FindAttribute("secure");
_formatstr[name] = std::make_pair(scan && scan->BoolValue(), secure && secure->BoolValue()); _formatstr[name] = std::make_pair(scan && scan->BoolValue(), secure && secure->BoolValue());
} else } else
return false; return Error(ErrorCode::BAD_ELEMENT, functionnode->Name());
} }
} }
else if (strcmp(node->Name(), "reflection") == 0) { else if (strcmp(node->Name(), "reflection") == 0) {
for (const tinyxml2::XMLElement *reflectionnode = node->FirstChildElement(); reflectionnode; reflectionnode = reflectionnode->NextSiblingElement()) { for (const tinyxml2::XMLElement *reflectionnode = node->FirstChildElement(); reflectionnode; reflectionnode = reflectionnode->NextSiblingElement()) {
if (strcmp(reflectionnode->Name(), "call") != 0) if (strcmp(reflectionnode->Name(), "call") != 0)
return false; return Error(ErrorCode::BAD_ELEMENT, reflectionnode->Name());
const char * const argString = reflectionnode->Attribute("arg"); const char * const argString = reflectionnode->Attribute("arg");
if (!argString) if (!argString)
return false; return Error(ErrorCode::MISSING_ATTRIBUTE, "arg");
_reflection[reflectionnode->GetText()] = atoi(argString); _reflection[reflectionnode->GetText()] = atoi(argString);
} }
@ -232,7 +233,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
else if (strcmp(node->Name(), "markup") == 0) { else if (strcmp(node->Name(), "markup") == 0) {
const char * const extension = node->Attribute("ext"); const char * const extension = node->Attribute("ext");
if (!extension) if (!extension)
return false; return Error(ErrorCode::MISSING_ATTRIBUTE, "ext");
_markupExtensions.insert(extension); _markupExtensions.insert(extension);
const char * const reporterrors = node->Attribute("reporterrors"); const char * const reporterrors = node->Attribute("reporterrors");
@ -246,18 +247,18 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
if (strcmp(librarynode->Name(), "keyword") == 0) if (strcmp(librarynode->Name(), "keyword") == 0)
_keywords[extension].insert(librarynode->Attribute("name")); _keywords[extension].insert(librarynode->Attribute("name"));
else else
return false; return Error(ErrorCode::BAD_ELEMENT, librarynode->Name());
} }
} }
else if (strcmp(markupnode->Name(), "exported") == 0) { else if (strcmp(markupnode->Name(), "exported") == 0) {
for (const tinyxml2::XMLElement *exporter = markupnode->FirstChildElement(); exporter; exporter = exporter->NextSiblingElement()) { for (const tinyxml2::XMLElement *exporter = markupnode->FirstChildElement(); exporter; exporter = exporter->NextSiblingElement()) {
if (strcmp(exporter->Name(), "exporter") != 0) if (strcmp(exporter->Name(), "exporter") != 0)
return false; return Error(ErrorCode::BAD_ELEMENT, exporter->Name());
const char * const prefix = exporter->Attribute("prefix"); const char * const prefix = exporter->Attribute("prefix");
if (!prefix) if (!prefix)
return false; return Error(ErrorCode::MISSING_ATTRIBUTE, "prefix");
for (const tinyxml2::XMLElement *e = exporter->FirstChildElement(); e; e = e->NextSiblingElement()) { for (const tinyxml2::XMLElement *e = exporter->FirstChildElement(); e; e = e->NextSiblingElement()) {
if (strcmp(e->Name(), "prefix") == 0) if (strcmp(e->Name(), "prefix") == 0)
@ -265,7 +266,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
else if (strcmp(e->Name(), "suffix") == 0) else if (strcmp(e->Name(), "suffix") == 0)
_exporters[prefix].addSuffix(e->GetText()); _exporters[prefix].addSuffix(e->GetText());
else 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) if (strcmp(librarynode->Name(), "importer") == 0)
_importers[extension].insert(librarynode->GetText()); _importers[extension].insert(librarynode->GetText());
else else
return false; return Error(ErrorCode::BAD_ELEMENT, librarynode->Name());
} }
} }
@ -297,19 +298,19 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
} }
else else
return false; return Error(ErrorCode::BAD_ELEMENT, blocknode->Name());
} }
} }
else else
return false; return Error(ErrorCode::BAD_ELEMENT, markupnode->Name());
} }
} }
else 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 bool Library::isargvalid(const std::string &functionName, int argnr, const MathLib::bigint argvalue) const

View File

@ -46,9 +46,22 @@ class CPPCHECKLIB Library {
public: public:
Library(); 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 loadxmldata(const char xmldata[], std::size_t len);
bool load(const tinyxml2::XMLDocument &doc);
/** get allocation id for function by name */ /** get allocation id for function by name */
int alloc(const char name[]) const { int alloc(const char name[]) const {

View File

@ -493,7 +493,7 @@ private:
errout.str(""); errout.str("");
Settings settings; 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"); complainMissingLib("std.cfg");
return ""; return "";
} }

View File

@ -85,9 +85,9 @@ public:
#define REGISTER_TEST( CLASSNAME ) namespace { CLASSNAME instance; } #define REGISTER_TEST( CLASSNAME ) namespace { CLASSNAME instance; }
#ifdef _WIN32 #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 #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 #endif
#define LOAD_LIB( NAME ) { LOAD_LIB_2(_lib, NAME); } #define LOAD_LIB( NAME ) { LOAD_LIB_2(_lib, NAME); }