Addons: Adding support in Cppcheck CLI to execute addons

This commit is contained in:
Daniel Marjamäki 2019-04-07 17:01:59 +02:00
parent 64f0744242
commit bf9006737a
6 changed files with 122 additions and 11 deletions

View File

@ -16,13 +16,17 @@ import re
VERIFY = ('-verify' in sys.argv)
VERIFY_EXPECTED = []
VERIFY_ACTUAL = []
CLI = False
def reportError(token, severity, msg, id):
if VERIFY:
VERIFY_ACTUAL.append(str(token.linenr) + ':' + id)
else:
sys.stderr.write(
'[' + token.file + ':' + str(token.linenr) + '] (' + severity + '): ' + msg + ' [' + id + ']\n')
msg = '[' + token.file + ':' + str(token.linenr) + ']: (' + severity + ') ' + msg + ' [' + id + ']'
if CLI:
print(msg)
else:
sys.stderr.write(msg + '\n')
def simpleMatch(token, pattern):
for p in pattern.split(' '):
@ -239,6 +243,9 @@ for arg in sys.argv[1:]:
if arg == '-verify':
VERIFY = True
continue
if arg == '--cli':
CLI = True
continue
print('Checking ' + arg + '...')
data = cppcheckdata.parsedump(arg)

View File

@ -35,6 +35,7 @@ typeBits = {
VERIFY = False
QUIET = False
SHOW_SUMMARY = True
CLI = False # Executed by Cppcheck binary?
def printStatus(*args, **kwargs):
if not QUIET:
@ -1964,6 +1965,9 @@ class MisraChecker:
errorId = id,
suppressions = self.dumpfileSuppressions)
if formattedMsg:
if CLI:
print(formattedMsg)
else:
sys.stderr.write(formattedMsg)
sys.stderr.write('\n')
if not severity in self.violations:
@ -2193,6 +2197,7 @@ parser.add_argument("-generate-table", help=argparse.SUPPRESS, action="store_tru
parser.add_argument("dumpfile", nargs='*', help="Path of dump file from cppcheck")
parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true")
parser.add_argument("-P", "--file-prefix", type=str, help="Prefix to strip when matching suppression file rules")
parser.add_argument("--cli", help="Addon is executed from Cppcheck", action="store_true")
args = parser.parse_args()
checker = MisraChecker()
@ -2215,6 +2220,10 @@ else:
if args.file_prefix:
checker.setFilePrefix(args.file_prefix)
if args.cli:
CLI = True
QUIET = True
SHOW_SUMMARY = False
if args.quiet:
QUIET = True
if args.no_summary:

View File

@ -40,6 +40,8 @@
#include <set>
#include <stdexcept>
#include <vector>
#include <memory>
#include <iostream> // <- TEMPORARY
#ifdef HAVE_RULES
#define PCRE_STATIC
@ -216,9 +218,10 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
// write dump file xml prolog
std::ofstream fdump;
if (mSettings.dump) {
const std::string dumpfile(mSettings.dumpFile.empty() ? (filename + ".dump") : mSettings.dumpFile);
fdump.open(dumpfile);
std::string dumpFile;
if (mSettings.dump || !mSettings.addons.empty()) {
dumpFile = (mSettings.dumpFile.empty()) ? (filename + ".dump") : mSettings.dumpFile;
fdump.open(dumpFile);
if (fdump.is_open()) {
fdump << "<?xml version=\"1.0\"?>" << std::endl;
fdump << "<dumps>" << std::endl;
@ -247,7 +250,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
// Parse comments and then remove them
preprocessor.inlineSuppressions(tokens1);
if (mSettings.dump && fdump.is_open()) {
if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) {
mSettings.nomsg.dump(fdump);
}
tokens1.removeComments();
@ -409,7 +412,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
continue;
// dump xml if --dump
if (mSettings.dump && fdump.is_open()) {
if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) {
fdump << "<dump cfg=\"" << ErrorLogger::toxml(mCurrentConfig) << "\">" << std::endl;
preprocessor.dump(fdump);
mTokenizer.dump(fdump);
@ -504,9 +507,71 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
}
// dumped all configs, close root </dumps> element now
if (mSettings.dump && fdump.is_open())
if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open())
fdump << "</dumps>" << std::endl;
if (!mSettings.addons.empty()) {
fdump.close();
for (const std::string &addon : mSettings.addons) {
const std::string &results = executeAddon(addon, dumpFile);
for (std::string::size_type pos = 0; pos < results.size();) {
const std::string::size_type pos2 = results.find("\n", pos);
if (pos2 == std::string::npos)
break;
const std::string::size_type pos1 = pos;
pos = pos2 + 1;
if (pos1 + 5 > pos2)
continue;
if (results[pos1] != '[')
continue;
if (results[pos2-1] != ']')
continue;
const std::string line = results.substr(pos1, pos2-pos1);
const std::string::size_type loc1 = 1;
const std::string::size_type loc2 = line.find(':');
const std::string::size_type loc3 = line.find("]: (");
if (loc2 == std::string::npos || loc3 == std::string::npos)
continue;
if (!(loc1 < loc2 && loc2 < loc3))
continue;
const std::string::size_type sev1 = loc3 + 4;
const std::string::size_type sev2 = line.find(")", sev1);
if (sev2 == std::string::npos)
continue;
const std::string::size_type id1 = line.rfind("[" + addon + "-");
if (id1 == std::string::npos || id1 < loc3)
continue;
ErrorLogger::ErrorMessage errmsg;
const std::string filename = line.substr(loc1, loc2-loc1);
const int lineNumber = std::atoi(line.c_str() + loc2 + 1);
errmsg._callStack.emplace_back(ErrorLogger::ErrorMessage::FileLocation(filename, lineNumber));
errmsg._id = line.substr(id1+1, line.size()-id1-2);
std::string text = line.substr(loc3 + 11, id1 - loc3 - 11);
if (text[0] == ' ')
text = text.substr(1);
if (endsWith(text, " ", 1))
text = text.erase(text.size() - 1);
errmsg.setmsg(text);
errmsg._severity = Severity::fromString(line.substr(sev1, sev2-sev1));
if (errmsg._severity == Severity::SeverityType::none)
continue;
errmsg.file0 = filename;
reportErr(errmsg);
}
}
}
} catch (const std::runtime_error &e) {
internalError(filename, e.what());
} catch (const std::bad_alloc &e) {
@ -879,6 +944,27 @@ void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &token
#endif
}
std::string CppCheck::executeAddon(const std::string &addon, const std::string &dumpFile)
{
const std::string addonFile = "addons/" + addon + ".py";
#ifdef _WIN32
return "";
#else
const std::string cmd = "python " + addonFile + " --cli " + dumpFile;
char buffer[1024];
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe)
return "";
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) {
result += buffer;
}
return result;
#endif
}
Settings &CppCheck::settings()
{
return mSettings;

View File

@ -179,6 +179,12 @@ private:
*/
void executeRules(const std::string &tokenlist, const Tokenizer &tokenizer);
/**
* @brief Execute a given addon
* @return results in std::string
*/
std::string executeAddon(const std::string &addon, const std::string &dumpFile);
/**
* @brief Errors and warnings are directed here.
*

View File

@ -1017,7 +1017,7 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
else if (strcmp(node->Name(), AnalyzeAllVsConfigsElementName) == 0)
; // FIXME: Write some warning
else if (strcmp(node->Name(), AddonsElementName) == 0)
node->Attribute(AddonElementName); // FIXME: Handle addons
temp.addons = readXmlStringList(node, AddonElementName, nullptr);
else if (strcmp(node->Name(), TagsElementName) == 0)
node->Attribute(TagElementName); // FIXME: Write some warning
else if (strcmp(node->Name(), ToolsElementName) == 0)
@ -1030,6 +1030,7 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
settings->includePaths = temp.includePaths;
settings->userDefines = temp.userDefines;
settings->userUndefs = temp.userUndefs;
settings->addons = temp.addons;
for (const std::string &path : paths)
guiProject.pathNames.push_back(path);
for (const std::string &supp : suppressions)

View File

@ -277,6 +277,8 @@ public:
*/
std::list<Rule> rules;
std::list<std::string> addons;
/** Is the 'configuration checking' wanted? */
bool checkConfiguration;