diff --git a/tools/reduce.cpp b/tools/reduce.cpp
deleted file mode 100644
index 01d78ee7f..000000000
--- a/tools/reduce.cpp
+++ /dev/null
@@ -1,898 +0,0 @@
-/*
- * Cppcheck - A tool for static C/C++ code analysis
- * Copyright (C) 2007-2019 Cppcheck team.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-#include "cppcheck.h"
-#include "mathlib.h"
-#include "path.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-class ReduceSettings : public Settings {
-public:
- ReduceSettings() : filename(0), linenr(0), hang(false), maxtime(0) { }
-
- const char *filename;
- std::size_t linenr;
- bool hang;
- unsigned int maxtime;
-};
-
-class CppcheckExecutor : public ErrorLogger {
-private:
- CppCheck cppcheck;
- std::string pattern;
- bool foundLine;
- std::time_t stopTime;
-
-public:
- explicit CppcheckExecutor(const ReduceSettings & settings)
- : ErrorLogger()
- , cppcheck(*this, false)
- , foundLine(false)
- , stopTime(0) {
-
- if (!settings.hang)
- pattern = ":" + MathLib::toString(settings.linenr) + "]";
-
- cppcheck.settings() = settings;
- }
-
- bool run(const char filename[], unsigned int maxtime) {
- foundLine = false;
- stopTime = std::time(0) + maxtime;
- cppcheck.check(filename);
- return foundLine;
- }
-
- void reportOut(const std::string &/*outmsg*/) override { }
- void reportErr(const ErrorLogger::ErrorMessage &msg) override {
- if (!pattern.empty() && msg.toString(false).find(pattern) != std::string::npos) {
- foundLine = true;
- cppcheck.terminate();
- }
- }
- void reportProgress(const std::string &/*filename*/, const char /*stage*/[], const std::size_t /*value*/) override {
- if (std::time(0) > stopTime) {
- if (pattern.empty())
- foundLine = true;
- else
- std::cerr << "timeout. You might want to use a longer --maxtime timeout" << std::endl;
- std::cout << "terminate" << std::endl;
- cppcheck.terminate();
- }
- }
-};
-
-static bool test(const ReduceSettings &settings, const std::vector &filedata, const std::size_t line1, const std::size_t line2)
-{
- std::string path(settings.filename);
- if (path.find_first_of("\\/") != std::string::npos)
- path = path.erase(1 + path.find_last_of("\\/"));
- else
- path.clear();
-
- const std::string tempfilename(path + "__temp__" + std::strrchr(settings.filename,'.'));
- std::ofstream fout(tempfilename.c_str());
- for (std::size_t i = 0; i < filedata.size(); i++)
- fout << ((i>=line1 && i<=line2) ? "" : filedata[i]) << std::endl;
- fout.close();
-
- CppcheckExecutor cppcheck(settings);
- return cppcheck.run(tempfilename.c_str(), settings.maxtime);
-}
-
-static bool test(const ReduceSettings &settings, const std::vector &filedata, const std::size_t line)
-{
- return test(settings, filedata, line, line);
-}
-
-#ifdef GDB_HELPERS
-static void printstr(const std::vector &filedata, int i1, int i2)
-{
- std::cout << filedata.size();
- for (int i = i1; i < i2; ++i)
- std::cout << i << ":" << filedata[i] << std::endl;
-}
-#endif
-
-static char getEndChar(const std::string &line)
-{
- std::size_t pos = line.find_last_not_of(" \t");
- return (pos == std::string::npos) ? '\0' : line[pos];
-}
-
-static std::vector readfile(const std::string &filename)
-{
- std::vector filedata;
- std::ifstream fin(filename.c_str());
- std::string line;
- bool blockComment = false;
- while (std::getline(fin,line)) {
- // replace various space characters with space
- for (std::string::size_type pos = 0; pos < line.size(); ++pos) {
- if (line[pos] & 0x80 || std::isspace(line[pos]))
- line[pos] = ' ';
- }
-
- // remove block comments TODO: Handle /* inside strings
- if (blockComment) {
- if (line.find("*/") == std::string::npos)
- line.clear();
- else {
- if (line.find("*/") + 2U == line.size())
- line.clear();
- else
- line = line.substr(line.find("*/") + 2U);
- blockComment = false;
- }
- }
- while (!blockComment && line.find("/*") != std::string::npos) {
- std::string::size_type pos = line.find("/*");
- if (line.find("*/",pos) == std::string::npos) {
- blockComment = true;
- if (pos==0)
- line.clear();
- else
- line = line.substr(0,pos);
- } else {
- blockComment = false;
- line = line.erase(pos, 2U + line.find("*/", pos) - pos);
- }
- }
-
- // Remove // comments
- if (line.find("//") != std::string::npos)
- line = line.substr(0, line.find("//"));
-
- // empty line
- if (line.find_first_not_of(" ") == std::string::npos)
- line.clear();
- else {
- const std::string::size_type pos = line.find_first_not_of(" ");
-
- // remove spaces before leading #
- if (line[pos]=='#')
- line = line.substr(pos);
- }
-
- // remove trailing spaces
- while (!line.empty() && std::isspace(line[line.size()-1U]))
- line = line.substr(0, line.size() - 1U);
-
- filedata.push_back(line);
- }
-
- // put function declarations in a single line..
- for (unsigned int linenr = 0U; linenr+1U < filedata.size(); ++linenr) {
- // Does this look like start of a function declaration?
- if (filedata[linenr].empty() ||
- !std::isalpha(filedata[linenr][0U]) ||
- getEndChar(filedata[linenr]) != ',' ||
- filedata[linenr].find("(") == std::string::npos ||
- filedata[linenr].find(")") != std::string::npos)
- continue;
-
- // Where does function declaration end?
- unsigned int linenr2 = linenr + 1U;
- while (linenr2 < filedata.size() &&
- getEndChar(filedata[linenr2]) == ',' &&
- filedata[linenr2].find("(") == std::string::npos &&
- filedata[linenr2].find(")") == std::string::npos)
- ++linenr2;
-
- // If function declaration looks correct.. simplify it
- if (linenr2 < filedata.size() &&
- getEndChar(filedata[linenr2]) == ';' &&
- filedata[linenr2].find("(") == std::string::npos &&
- filedata[linenr2].size() > 2U &&
- filedata[linenr2].find(")") == filedata[linenr2].size() - 2U) {
- std::string code;
- for (unsigned int i = linenr; i <= linenr2; i++) {
- code = code + filedata[i];
- filedata[i].clear();
- }
- filedata[linenr] = code;
- }
- }
-
- // put #define statements in a single line..
- for (unsigned int linenr = 0U; linenr+1U < filedata.size(); ++linenr) {
- // is this a multiline #define statement?
- if (filedata[linenr].compare(0,8,"#define ")!=0 || getEndChar(filedata[linenr])!='\\')
- continue;
-
- // where does statement end?
- unsigned int linenr2 = linenr + 1U;
- while (linenr2 < filedata.size() && getEndChar(filedata[linenr2]) == '\\')
- ++linenr2;
-
- // simplify
- if (linenr2 < filedata.size()) {
- std::string code;
- for (unsigned int i = linenr; i <= linenr2; i++) {
- code = code + filedata[i].substr(0,filedata[i].size() - 1U);
- filedata[i].clear();
- }
- filedata[linenr] = code;
- }
- }
-
- return filedata;
-}
-
-static bool removeMacrosInGlobalScope(const ReduceSettings &settings, std::vector &filedata)
-{
- bool changed = false;
-
- // Remove macros in global scope..
- for (std::size_t i = 0; i < filedata.size(); i++) {
- const std::string line = (i==0U&&filedata.empty()) ? std::string(";") : filedata[i];
- if (line.empty())
- continue;
-
- const char startChar = line[0];
- const char endChar = line[line.size() - 1U];
-
- bool decl = bool(!std::isspace(startChar) && (endChar=='}' || endChar==';'));
- while (decl) {
- decl = false; // might be set to true below
- std::size_t pos = i + 1U;
- while (pos < filedata.size() && filedata[pos].empty())
- ++pos;
- if (pos >= filedata.size())
- break;
- const std::string &s = filedata[pos]; // possible macro : make sure it matches '[A-Z0-9_]+\(.*\)'
- std::string::size_type si = 0;
- while (si < s.size() && ((s[si]>='A' && s[si]<='Z') || (i>0 && s[si]>='0' && s[si]<='9') || s[si]=='_'))
- si++;
- while (si < s.size() && std::isspace(s[si]))
- si++;
- if (si == 0U || si >= s.size() || s[si] != '(')
- break;
- si++;
- unsigned int parlevel = 1;
- while (si < s.size() && parlevel >= 1U) {
- if (s[si] == '(')
- ++parlevel;
- else if (s[si] == ')')
- --parlevel;
- si++;
- }
- if (!(parlevel == 0U && si == s.size()))
- break;
- if (test(settings, filedata, pos)) {
- decl = true;
- filedata[pos].clear();
- std::cout << "removed declaration at line " << pos << std::endl;
- changed = true;
- } else {
- std::cout << "kept declaration at line " << pos << std::endl;
- }
- }
- }
-
- return changed;
-}
-
-static bool removeBlocksOfCode(const ReduceSettings &settings, std::vector &filedata)
-{
- bool changed = false;
-
- // Remove blocks of code..
- for (std::size_t i = 0; i < filedata.size(); i++) {
- const std::string line = (i==0U&&filedata.empty()) ? std::string(";") : filedata[i];
- if (line.empty())
- continue;
-
- const char startChar = line[0];
- const char endChar = line[line.size() - 1U];
-
- // some kind of single line declaration
- if (std::isalpha(startChar) && endChar==';') {
- if (test(settings, filedata, i)) {
- filedata[i].clear();
- std::cout << "removed declaration at line " << i << std::endl;
- changed = true;
- } else {
- std::cout << "kept declaration at line " << i << std::endl;
- }
- }
-
- // remove a function body below a '}'
- bool decl = bool(!std::isspace(startChar) && (endChar=='}' || endChar==';'));
- while (decl) {
- decl = false; // might be set to true below
- std::size_t pos = ++i;
- while (pos < filedata.size() && filedata[pos].empty())
- ++pos;
- if ((pos+2U < filedata.size()) && (std::isalpha(filedata[pos].at(0)))) {
-
- // does code block start with "{"
- std::size_t pos2 = pos;
-
- // struct X { ..
- if (filedata[pos].find_first_of("();}") == std::string::npos && filedata[pos].at(filedata[pos].size()-1U) == '{') {
-
- }
-
- // function declaration ..
- else {
- if (filedata[pos].find("(") != std::string::npos && filedata[pos].find(")")==std::string::npos) {
- ++pos2;
- while (pos2+2U < filedata.size() && !filedata[pos2].empty() && filedata[pos2].find_first_of("(){}") == std::string::npos)
- ++pos2;
- if (filedata[pos2].find_first_of("({}")!=std::string::npos || filedata[pos2].find(")") == std::string::npos)
- break;
- }
- pos2++;
- if (pos2 < filedata.size() && !filedata[pos2].empty() && filedata[pos2].at(filedata[pos2].find_first_not_of(" ")) == ':') {
- pos2++;
- while (pos2 < filedata.size() && !filedata[pos2].empty() && filedata[pos2].at(filedata[pos2].find_first_not_of(" ")) == ',')
- pos2++;
- }
- if (pos2+2U >= filedata.size() || filedata[pos2] != "{")
- break;
- }
- pos2++;
-
- // find end of block..
- int level = 0;
- while ((pos2 < filedata.size()) && (filedata[pos2].empty() || std::isspace(filedata[pos2].at(0)) || (std::isalpha(filedata[pos2].at(0)) && filedata[pos2].at(filedata[pos2].size()-1U) == ':') || filedata[pos2].compare(0,3,"#if")==0 || filedata[pos2].compare(0,3,"#el")==0 || filedata[pos2]=="#endif")) {
- if (filedata[pos2].compare(0,3,"#if") == 0)
- ++level;
- else if (filedata[pos2] == "#endif")
- --level;
- ++pos2;
- }
- if (level != 0)
- break;
-
- // does block of code end with a '}'
- if ((pos2 < filedata.size()) && (filedata[pos2] == "}" || filedata[pos2] == "};")) {
- if (test(settings, filedata, pos, pos2)) {
- for (i = pos; i <= pos2; i++)
- filedata[i].clear();
- std::cout << "removed block of code at lines " << pos << "-" << pos2 << std::endl;
- decl = true;
- changed = true;
- } else {
- std::cout << "kept block of code at lines " << pos << "-" << pos2 << std::endl;
- }
- }
- }
- }
- }
- return changed;
-}
-
-static bool removeClassAndStructMembers(const ReduceSettings &settings, std::vector &filedata)
-{
- bool changed = false;
-
- // remove class and struct members
- for (std::size_t i = 0; i + 2U < filedata.size(); i++) {
- if ((filedata[i].compare(0,6,"class ")==0 || filedata[i].compare(0,7,"struct ")==0) && filedata[i].find(";")==std::string::npos && filedata[i+1]=="{") {
- bool decl = true;
- for (std::size_t pos = i+2U; pos < filedata.size(); pos++) {
- const std::string line = filedata[pos];
- if (line.empty())
- continue;
-
- // count { and }
- unsigned int c1=0, c2=0;
- for (std::string::size_type c = 0; c < line.size(); c++) {
- if (line[c] == '{')
- ++c1;
- else if (line[c] == '}')
- ++c2;
- }
- if (c2>0 && (c1!=1 || c2!=1))
- break;
-
- const char endChar = line[line.size() - 1U];
-
- if (decl && (endChar == ';' || (c1==1 && c2==1 && endChar=='}'))) {
- if (test(settings, filedata, pos)) {
- std::cout << "removed struct/class declaration at line " << pos << std::endl;
- filedata[pos].clear();
- changed = true;
- } else {
- std::cout << "kept struct/class declaration at line " << pos << std::endl;
- }
- }
-
- if (line[0] != '#') {
- if (decl && std::isalpha(line[0]) && endChar == ':') {
- decl = true;
- for (std::string::size_type linepos = 0U; linepos+1U < line.size(); ++linepos)
- decl &= (std::isspace(line[linepos]) || std::isalpha(line[linepos]));
- } else
- decl = bool(endChar == ';' || endChar == '}');
- }
- }
- }
- }
-
- return changed;
-}
-
-static bool removeIfEndIf(const ReduceSettings &settings, std::vector &filedata)
-{
- bool changed = false;
-
- // #if - #endif
- for (std::size_t i = 0; i < filedata.size(); ++i) {
- while (filedata[i].compare(0,3,"#if") == 0) {
- std::size_t pos2 = i + 1;
- while (pos2 < filedata.size() && filedata[pos2].empty())
- ++pos2;
- if (pos2 < filedata.size() && filedata[pos2] == "#endif") {
- if (test(settings, filedata, i, pos2)) {
- std::cout << "Removed #if - #endif block at lines " << i << "-" << pos2 << std::endl;
- filedata[i].clear();
- filedata[pos2].clear();
- i = 0;
- changed = true;
- } else {
- std::cout << "Kept #if - #endif block at lines " << i << "-" << pos2 << std::endl;
- break;
- }
- } else {
- break;
- }
- }
- }
-
- // #ifndef UNUSED_ID
- for (std::size_t i = 0; i < filedata.size(); ++i) {
- if (filedata[i].compare(0,8,"#ifndef ") == 0) {
- bool erase = true;
- bool def = false;
- const std::string id(filedata[i].substr(8));
- for (std::size_t i2 = 0; i2 < filedata.size(); i2++) {
- if (i2 == i)
- continue;
- if (filedata[i2].find(id) != std::string::npos) {
- if (!def && filedata[i2].compare(0,8,"#define ")==0)
- def = true;
- else
- erase = false;
- }
- }
- if (erase) {
- unsigned int level = 0;
- for (std::size_t i2 = i + 1U; i2 < filedata.size(); i2++) {
- if (filedata[i2].compare(0,3,"#if")==0)
- ++level;
- else if (filedata[i2] == "#else")
- break;
- else if (filedata[i2] == "#endif") {
- if (level > 0)
- --level;
- else {
- std::vector filedata2(filedata);
- filedata2[i].clear();
- filedata2[i2].clear();
-
- if (test(settings, filedata2, i)) {
- std::cout << "Removed #ifndef at line " << i << std::endl;
- filedata.swap(filedata2);
- changed = true;
- } else {
- std::cout << "Kept #ifndef at line " << i << std::endl;
- break;
- }
-
- break;
- }
- }
- }
- }
- }
- }
-
- return changed;
-}
-
-static bool removeUnusedDefines(const ReduceSettings &settings, std::vector &filedata)
-{
- bool changed = false;
-
- for (std::size_t i = 0; i < filedata.size(); ++i) {
- if (filedata[i].compare(0,8,"#define ")==0 && filedata[i].find("\\")==std::string::npos) {
- // Try to remove macro..
-
- if (test(settings, filedata, i)) {
- std::cout << "Removed #define at line " << i << std::endl;
- filedata[i].clear();
- changed = true;
- } else {
- std::cout << "Kept #define at line " << i << std::endl;
- }
- }
- }
-
- return changed;
-}
-
-static bool removeSingleLines(const ReduceSettings &settings, std::vector &filedata)
-{
- bool changed = false;
-
- bool decl = true;
- for (std::size_t i = 0; i < filedata.size(); ++i) {
- const std::string line = filedata[i];
- if (line.empty())
- continue;
-
- const char endChar = line[line.size() - 1U];
- if (decl && endChar == ';') {
- if (test(settings, filedata, i)) {
- std::cout << "Removed statement at line " << i << std::endl;
- filedata[i].clear();
- } else {
- std::cout << "Kept statement at line " << i << std::endl;
- }
- } else {
- decl = bool (endChar == ';' || endChar == '{' || endChar == '}');
- }
- }
-
- return changed;
-}
-
-// Try to remove stuff from statements
-static bool cleanupStatements(const ReduceSettings &settings, std::vector &filedata)
-{
- bool changed = false;
-
- for (std::size_t i = 0; i < filedata.size(); ++i) {
- std::string line = filedata[i];
- if (line.empty())
- continue;
-
- for (std::string::size_type pos = 0U; pos < line.size(); ++pos) {
-
- // function parameter..
- if (std::strchr("(,", line[pos])) {
- const std::string::size_type pos1 = (line[pos] == ',') ? pos : (pos + 1U);
- std::string::size_type pos2 = pos + 1;
- while (pos2 < line.size() && std::isspace(line[pos2]))
- ++pos2;
- while (pos2 < line.size() && (std::isalnum(line[pos2]) || line[pos2]=='_'))
- ++pos2;
- while (pos2 < line.size() && std::isspace(line[pos2]))
- ++pos2;
- if (pos2 >= pos+2U && pos2=line.size() || line[pos2]!='(')
- continue;
- pos2++;
- while (pos2 < line.size() && std::isalpha(line[pos2]))
- ++pos2;
- while (pos2 < line.size() && std::isspace(line[pos2]))
- ++pos2;
- if (pos2>=line.size() || line[pos2]!='*')
- continue;
- while (pos2 < line.size() && line[pos2]=='*')
- ++pos2;
- if (pos2> settings.maxConfigs)) {
- std::cerr << "argument to '--max-configs=' is not a number." << std::endl;
- return EXIT_FAILURE;
- }
-
- if (settings.maxConfigs < 1) {
- std::cerr << "argument to '--max-configs=' must be greater than 0." << std::endl;
- return EXIT_FAILURE;
- }
-
- maxconfigs = true;
- } else if (std::strncmp(argv[i], "--library=", 10) == 0) {
- if (!tryLoadLibrary(settings.library, argv[0], argv[i]+10))
- return EXIT_FAILURE;
- } else if (std::strcmp(argv[i], "--std=posix") == 0) {
- settings.standards.posix = true;
- } else if (std::strcmp(argv[i], "--std=c89") == 0) {
- settings.standards.c = Standards::C89;
- } else if (std::strcmp(argv[i], "--std=c99") == 0) {
- settings.standards.c = Standards::C99;
- } else if (std::strcmp(argv[i], "--std=c11") == 0) {
- settings.standards.c = Standards::C11;
- } else if (std::strcmp(argv[i], "--std=c++03") == 0) {
- settings.standards.cpp = Standards::CPP03;
- } else if (std::strcmp(argv[i], "--std=c++11") == 0) {
- settings.standards.cpp = Standards::CPP11;
- } else if (settings.filename==nullptr && strchr(argv[i],'.'))
- settings.filename = argv[i];
- else if (settings.linenr == 0U && MathLib::isInt(argv[i]))
- settings.linenr = std::atoi(argv[i]);
- else {
- std::cerr << "invalid option " << argv[i] << std::endl;
- return EXIT_FAILURE;
- }
- }
-
- if (def && !settings.force && !maxconfigs)
- settings.maxConfigs = 1U;
-
- if (settings.force)
- settings.maxConfigs = ~0U;
-
- if ((!settings.hang && settings.linenr == 0U) || settings.filename == nullptr) {
- std::cerr << "Syntax:" << std::endl
- << argv[0] << " [--stdout] [--cfg=X] [--hang] [--maxtime=60] [-D define] [-I includepath] [--force] [--enable=] [--inconclusive] [--debug-warnings] [--max-configs=] [--platform=] [--library=] [--std=] filename [linenr]" << std::endl;
- return EXIT_FAILURE;
- }
-
- std::cout << "make sure " << (settings.hang ? "hang" : "false positive") << " can be reproduced" << std::endl;
-
- // Execute Cppcheck on the file..
- {
- CppcheckExecutor cppcheck(settings);
- if (!cppcheck.run(settings.filename, settings.maxtime)) {
- std::cerr << "Can't reproduce false positive at line " << settings.linenr << std::endl;
- return EXIT_FAILURE;
- }
- }
-
- // Read file..
- std::vector filedata(readfile(settings.filename));
-
- // Write resulting code..
- if (!test(settings, filedata, ~0)) {
- std::cerr << "Cleanup failed." << std::endl;
- return EXIT_FAILURE;
- }
-
- // Remove includes..
- std::set headers;
- for (std::size_t i = 0; i < filedata.size(); i++) {
- if (filedata[i].compare(0,8,"#include")==0) {
- if (test(settings, filedata, i)) {
- std::cout << "removed #include : " << filedata[i] << std::endl;
- filedata[i].clear();
- } else {
- std::string header = filedata[i];
- header = header.substr(1U + header.find_first_of("\"<"));
- header = header.erase(header.find_last_of("\">"));
- if (headers.find(header) != headers.end()) {
- std::cerr << "Failed to reduce headers" << std::endl;
- return EXIT_FAILURE;
- }
- headers.insert(header);
- std::string path(settings.filename);
- if (path.find_first_of("\\/") != std::string::npos)
- path = path.erase(1 + path.find_last_of("\\/"));
- else
- path.clear();
- std::cout << "expand #include : " << (path+header) << std::endl;
- std::vector data(readfile(path+header));
- if (!data.empty()) {
- filedata[i].clear();
- filedata.insert(filedata.begin()+i, data.begin(), data.end());
- settings.linenr += data.size();
- }
- }
- }
- }
-
- bool changed = true;
- while (changed) {
- changed = false;
- changed |= removeMacrosInGlobalScope(settings,filedata);
- changed |= removeBlocksOfCode(settings,filedata);
- changed |= removeClassAndStructMembers(settings,filedata);
- changed |= removeIfEndIf(settings,filedata);
- changed |= removeUnusedDefines(settings,filedata);
-
- if (settings.hang) {
- changed |= removeSingleLines(settings,filedata);
- changed |= cleanupStatements(settings,filedata);
- }
- }
-
- // Write resulting code..
- {
- const std::string outfilename(std::string("__out__") + std::strrchr(settings.filename,'.'));
- std::ofstream fout;
- if (!print)
- fout.open(outfilename.c_str());
- std::ostream &os = print ? std::cout : fout;
- for (std::size_t i = 0; i < filedata.size(); i++) {
- if (!filedata[i].empty())
- os << filedata[i] << std::endl;
- }
- }
-
- return EXIT_SUCCESS;
-}