diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index ad9d87b5c..32b67ac0d 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -942,7 +942,9 @@ void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processe std::map defs(getcfgmap(_settings ? _settings->userDefines : std::string(""))); if (_settings && _settings->_maxConfigs == 1U) { - processedFile = handleIncludes(processedFile, filename, includePaths, defs); + std::list pragmaOnce; + std::list includes; + processedFile = handleIncludes(processedFile, filename, includePaths, defs, pragmaOnce, includes); resultConfigurations = getcfgs(processedFile, filename, defs); } else { handleIncludes(processedFile, filename, includePaths); @@ -1932,7 +1934,7 @@ static bool openHeader(std::string &filename, const std::list &incl } -std::string Preprocessor::handleIncludes(const std::string &code, const std::string &filePath, const std::list &includePaths, std::map &defs, std::list includes) +std::string Preprocessor::handleIncludes(const std::string &code, const std::string &filePath, const std::list &includePaths, std::map &defs, std::list &pragmaOnce, std::list includes) { const std::string path(filePath.substr(0, 1 + filePath.find_last_of("\\/"))); @@ -1975,7 +1977,9 @@ std::string Preprocessor::handleIncludes(const std::string &code, const std::str std::stack::reference elseIsTrue = elseIsTrueStack.top(); - if (line.compare(0,7,"#ifdef ") == 0) { + if (line == "#pragma once") { + pragmaOnce.push_back(filePath); + } else if (line.compare(0,7,"#ifdef ") == 0) { if (indent == indentmatch) { const std::string tag = getdef(line,true); if (defs.find(tag) != defs.end()) { @@ -2115,8 +2119,14 @@ std::string Preprocessor::handleIncludes(const std::string &code, const std::str includes.push_back(filename); + // Don't include header if it's already included and contains #pragma once + if (std::find(pragmaOnce.begin(), pragmaOnce.end(), filename) != pragmaOnce.end()) { + ostr << std::endl; + continue; + } + ostr << "#file \"" << filename << "\"\n" - << handleIncludes(read(fin, filename), filename, includePaths, defs, includes) << std::endl + << handleIncludes(read(fin, filename), filename, includePaths, defs, pragmaOnce, includes) << std::endl << "#endfile\n"; continue; } diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 52eb2e22a..d12405a67 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -233,10 +233,11 @@ public: * @param filePath filename of code * @param includePaths Paths where headers might be * @param defs defines (only values) + * @param pragmaOnce includes that has already been included and contains a #pragma once statement * @param includes provide a empty list. this is just used to prevent recursive inclusions. * \return resulting string */ - std::string handleIncludes(const std::string &code, const std::string &filePath, const std::list &includePaths, std::map &defs, std::list includes = std::list()); + std::string handleIncludes(const std::string &code, const std::string &filePath, const std::list &includePaths, std::map &defs, std::list &pragmaOnce, std::list includes); void setFile0(const std::string &f) { file0 = f; diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index ce16be567..fa43151c8 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -208,6 +208,7 @@ private: TEST_CASE(pragma); TEST_CASE(pragma_asm_1); TEST_CASE(pragma_asm_2); + TEST_CASE(pragma_once); TEST_CASE(endifsemicolon); TEST_CASE(missing_doublequote); TEST_CASE(handle_error); @@ -2410,6 +2411,19 @@ private: ASSERT_EQUALS("\n\nasm(temp);\nbbb\n", actual[""]); } + void pragma_once() { + const char code[] = "#pragma once\n" + "int x"; + + Preprocessor preprocessor(NULL, this); + const std::list includePaths; + std::map defs; + std::list pragmaOnce; + preprocessor.handleIncludes(code, "123.h", includePaths, defs, pragmaOnce, std::list()); + ASSERT_EQUALS(1U, pragmaOnce.size()); + ASSERT_EQUALS("123.h", *(pragmaOnce.begin())); + } + void endifsemicolon() { const char filedata[] = "void f() {\n" "#ifdef A\n" @@ -3278,12 +3292,14 @@ private: defs.clear(); defs["A"] = ""; { + std::list pragmaOnce; const std::string code("#ifdef A\n123\n#endif\n"); - const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n123\n\n", actual); }{ + std::list pragmaOnce; const std::string code("#ifdef B\n123\n#endif\n"); - const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n\n\n", actual); } } @@ -3293,33 +3309,37 @@ private: defs.clear(); defs["A"] = ""; { + std::list pragmaOnce; const std::string code("#ifndef A\n123\n#endif\n"); - const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n\n\n", actual); }{ + std::list pragmaOnce; const std::string code("#ifndef B\n123\n#endif\n"); - const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n123\n\n", actual); } } // define - ifndef { + std::list pragmaOnce; defs.clear(); const std::string code("#ifndef X\n#define X\n123\n#endif\n" "#ifndef X\n#define X\n123\n#endif\n"); - const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n#define X\n123\n\n" "\n\n\n\n", actual); } // #define => #if { + std::list pragmaOnce; defs.clear(); const std::string code("#define X 123\n" "#if X==123\n" "456\n" "#endif\n"); - const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("#define X 123\n\n456\n\n", actual); } @@ -3335,23 +3355,26 @@ private: "4\n" "#endif"); { + std::list pragmaOnce; defs.clear(); defs["A"] = ""; defs["C"] = ""; - const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n1\n\n\n\n\n\n\n\n", actual); } { + std::list pragmaOnce; defs.clear(); defs["B"] = ""; - const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n\n\n2\n\n\n\n\n\n", actual); } { + std::list pragmaOnce; defs.clear(); - const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n\n\n\n\n\n\n4\n\n", actual); } } @@ -3360,9 +3383,10 @@ private: { // see also endifsemicolon const std::string code("{\n#ifdef X\n#endif;\n}"); + std::list pragmaOnce; defs.clear(); defs["Z"] = ""; - const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("{\n\n\n}\n", actual); } @@ -3373,11 +3397,15 @@ private: "123\n" "#endif\n"); - defs.clear(); - const std::string actual1(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + std::list pragmaOnce; + pragmaOnce.clear(); defs.clear(); - const std::string actual(preprocessor.handleIncludes(code + "#undef X\n" + code, filePath, includePaths, defs)); + const std::string actual1(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); + + pragmaOnce.clear(); + defs.clear(); + const std::string actual(preprocessor.handleIncludes(code + "#undef X\n" + code, filePath, includePaths, defs,pragmaOnce,std::list())); ASSERT_EQUALS(actual1 + "#undef X\n" + actual1, actual); } @@ -3385,9 +3413,10 @@ private: // #error { errout.str(""); + std::list pragmaOnce; defs.clear(); const std::string code("#ifndef X\n#error abc\n#endif"); - const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); ASSERT_EQUALS("\n#error abc\n\n", actual); ASSERT_EQUALS("[test.c:2]: (error) abc\n", errout.str()); } @@ -3403,40 +3432,48 @@ private: // missing local include { const std::string code("#include \"missing-include!!.h\"\n"); + std::list pragmaOnce; + pragmaOnce.clear(); errout.str(""); settings = Settings(); - preprocessor.handleIncludes(code,"test.c",includePaths,defs); + preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("", errout.str()); + pragmaOnce.clear(); errout.str(""); settings.checkConfiguration = true; - preprocessor.handleIncludes(code,"test.c",includePaths,defs); + preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("[test.c:1]: (information) Include file: \"missing-include!!.h\" not found.\n", errout.str()); + pragmaOnce.clear(); errout.str(""); settings.nomsg.addSuppression("missingInclude"); - preprocessor.handleIncludes(code,"test.c",includePaths,defs); + preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("", errout.str()); } // missing system header { const std::string code("#include \n"); + std::list pragmaOnce; + pragmaOnce.clear(); errout.str(""); settings = Settings(); - preprocessor.handleIncludes(code,"test.c",includePaths,defs); + preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("", errout.str()); + pragmaOnce.clear(); errout.str(""); settings.checkConfiguration = true; - preprocessor.handleIncludes(code,"test.c",includePaths,defs); + preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("[test.c:1]: (information) Include file: not found.\n", errout.str()); + pragmaOnce.clear(); errout.str(""); settings.nomsg.addSuppression("missingIncludeSystem"); - preprocessor.handleIncludes(code,"test.c",includePaths,defs); + preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("", errout.str()); } @@ -3449,9 +3486,10 @@ private: defs.clear(); defs["GNU"] = ""; + std::list pragmaOnce; errout.str(""); settings = Settings(); - preprocessor.handleIncludes(code,"test.c",includePaths,defs); + preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list()); ASSERT_EQUALS("", errout.str()); } } @@ -3464,6 +3502,7 @@ private: // #3405 { + std::list pragmaOnce; defs.clear(); defs["A"] = ""; const std::string code("\n#ifndef PAL_UTIL_UTILS_H_\n" @@ -3488,7 +3527,7 @@ private: "8\n" "#endif\n" "\n"); - std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs)); + std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list())); // the 1,2,4 should be in the result actual.erase(0, actual.find("1")); @@ -3499,6 +3538,7 @@ private: // #3418 { + std::list pragmaOnce; defs.clear(); const char code[] = "#define A 1\n" "#define B A\n" @@ -3506,7 +3546,7 @@ private: "123\n" "#endif\n"; - std::string actual(preprocessor.handleIncludes(code, filePath, includePaths, defs)); + std::string actual(preprocessor.handleIncludes(code, filePath, includePaths, defs,pragmaOnce,std::list())); ASSERT_EQUALS("#define A 1\n#define B A\n\n123\n\n", actual); } } @@ -3530,8 +3570,9 @@ private: const std::list includePaths; std::map defs; defs["A"] = "1"; + std::list pragmaOnce; ASSERT_EQUALS(std::string::npos, // No "123" in the output - preprocessor.handleIncludes(code, "test.c", includePaths, defs).find("123")); + preprocessor.handleIncludes(code, "test.c", includePaths, defs, pragmaOnce,std::list()).find("123")); } void def_handleIncludes_ifelse3() { // #4865 @@ -3550,7 +3591,8 @@ private: std::map defs; defs["B"] = "1"; defs["C"] = "1"; - preprocessor.handleIncludes(code, "test.c", includePaths, defs); // don't crash + std::list pragmaOnce; + preprocessor.handleIncludes(code, "test.c", includePaths, defs, pragmaOnce, std::list()); // don't crash } void def_valueWithParentheses() { @@ -3564,11 +3606,12 @@ private: const std::string filePath("test.c"); const std::list includePaths; std::map defs; + std::list pragmaOnce; Preprocessor preprocessor(NULL, this); std::istringstream istr(code); const std::string s(preprocessor.read(istr, "")); - preprocessor.handleIncludes(s, filePath, includePaths, defs); + preprocessor.handleIncludes(s, filePath, includePaths, defs, pragmaOnce, std::list()); ASSERT(defs.find("A") != defs.end()); ASSERT_EQUALS("(Fred)", defs["A"]);