diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 12cdae703..aebe6f8cb 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -414,6 +414,13 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[]) path += '/'; _settings->_includePaths.push_back(path); + } else if (std::strncmp(argv[i], "--include=", 10) == 0) { + std::string path = argv[i] + 10; + + path = Path::fromNativeSeparators(path); + + _settings->userIncludes.push_back(path); + _settings->userDefines += ";"; } else if (std::strncmp(argv[i], "--includes-file=", 16) == 0) { // open this file and read every input file (1 file name per line) AddInclPathsToList(16 + argv[i], _settings->_includePaths); @@ -772,6 +779,10 @@ void CmdLineParser::PrintHelp() " First given path is searched for contained header\n" " files first. If paths are relative to source files,\n" " this is not needed.\n" + " --include=\n" + " Force inclusion of a file. Can be used for example when\n" + " checking the Linux kernel, where autoconf.h needs to be\n" + " included for every file compiled.\n" " -i Give a source file or source file directory to exclude\n" " from the check. This applies only to source files so\n" " header files included by source files are not matched.\n" diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 462e9c53b..f85d3926e 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -796,11 +796,66 @@ void Preprocessor::preprocessWhitespaces(std::string &processedFile) void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processedFile, std::list &resultConfigurations, const std::string &filename, const std::list &includePaths) { + std::map defs; + std::string forcedIncludes; + if (file0.empty()) file0 = filename; processedFile = read(srcCodeStream, filename); + if (_settings && !_settings->userIncludes.empty()) { + for (std::list::iterator it = _settings->userIncludes.begin(); + it != _settings->userIncludes.end(); + it++) { + std::string cur = *it; + + // try to open file + std::ifstream fin; + + fin.open(cur.c_str()); + if (!fin.is_open()) { + if (_settings && !_settings->nomsg.isSuppressed("missingInclude", cur, 1)) { + std::string path = ""; + + std::size_t pos = cur.find_last_of("\\/"); + + if (pos != std::string::npos) + path = cur.substr(0, 1 + pos); + + missingIncludeFlag = true; + missingInclude(Path::toNativeSeparators(path), + 1, + cur, + true); + } + continue; + } + std::string fileData = read(fin, filename); + + fin.close(); + + //handleIncludes("#include \"" + cur + "\"\n", cur, includePaths, defs); + forcedIncludes = + forcedIncludes + + "#file \"" + cur + "\"\n" + + "#line 1\n" + + fileData + "\n" + + "#endfile\n" + ; + } + } + + if (!forcedIncludes.empty()) { + processedFile = + forcedIncludes + + "#file \"" + filename + "\"\n" + + "#line 1\n" + + processedFile + + "#endfile\n" + ; + } + // Remove asm(...) removeAsm(processedFile); @@ -827,8 +882,6 @@ void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processe } if (_settings && !_settings->userDefines.empty()) { - std::map defs; - // TODO: break out this code. There is other similar code. std::string::size_type pos1 = 0; while (pos1 != std::string::npos) { @@ -855,7 +908,7 @@ void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processe } processedFile = handleIncludes(processedFile, filename, includePaths, defs); - if (_settings->userDefines.empty()) // TODO: How can it be empty? + if (_settings->userIncludes.empty()) resultConfigurations = getcfgs(processedFile, filename); } else { @@ -1043,6 +1096,9 @@ std::list Preprocessor::getcfgs(const std::string &filedata, const if (!line.empty() && line.compare(0, 3, "#if") != 0) includeguard = false; + if (line.compare(0, 5, "#line") == 0) + continue; + if (line.empty() || line[0] != '#') continue; @@ -1723,6 +1779,7 @@ std::string Preprocessor::getcode(const std::string &filedata, const std::string } else if (line.compare(0, 7, "#file \"") == 0 || line.compare(0, 8, "#endfile") == 0 || line.compare(0, 8, "#define ") == 0 || + line.compare(0, 6, "#line ") == 0 || line.compare(0, 6, "#undef") == 0) { // We must not remove #file tags or line numbers // are corrupted. File tags are removed by the tokenizer. diff --git a/lib/settings.h b/lib/settings.h index 5cbc83c37..250dab79d 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -168,6 +168,9 @@ public: /** @brief undefines given by the user */ std::set userUndefs; + /** @brief forced includes given by the user */ + std::list userIncludes; + /** @brief --report-progress */ bool reportProgress; diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index b96e28e2d..36219a354 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -287,6 +287,16 @@ bool TokenList::createTokens(std::istream &code, const std::string& file0) if (CurrentToken == "#file") { // Handle this where strings are handled continue; + } else if (CurrentToken == "#line") { + // Read to end of line + std::string line; + + std::getline(code, line); + + // Update the current line number + std::stringstream(line) >> lineno; + CurrentToken.clear(); + continue; } else if (CurrentToken == "#endfile") { if (lineNumbers.empty() || fileIndexes.empty()) { // error deallocateTokens(); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 6986d4090..42477dafd 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -284,6 +284,8 @@ private: TEST_CASE(file2); TEST_CASE(file3); + TEST_CASE(line1); // Ticket #4408 + TEST_CASE(doublesharp); TEST_CASE(macrodoublesharp); @@ -4609,6 +4611,42 @@ private: } + void line1() { + // Test for Ticket #4408 + const char code[] = "#file \"c:\\a.h\"\n" + "first\n" + "#line 5\n" + "second\n" + "#line not-a-number\n" + "third\n" + "#line 100 \"i.h\"\n" + "fourth\n" + "fifth\n" + "#endfile\n"; + + errout.str(""); + + Settings settings; + + TokenList tokenList(&settings); + std::istringstream istr(code); + bool res = tokenList.createTokens(istr, "a.cpp"); + ASSERT_EQUALS(res, true); + + for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { + if (tok->str() == "first") + ASSERT_EQUALS(1, tok->linenr()); + if (tok->str() == "second") + ASSERT_EQUALS(5, tok->linenr()); + if (tok->str() == "third") + TODO_ASSERT_EQUALS(7, 0, tok->linenr()); + if (tok->str() == "fourth") + ASSERT_EQUALS(100, tok->linenr()); + if (tok->str() == "fifth") + ASSERT_EQUALS(101, tok->linenr()); + } + } + void doublesharp() {