Skip to content

Commit 342142b

Browse files
committed
Fixed danmar#4256 (Preprocessor: '#pragma once' doesn't work properly when using -D)
1 parent 6a369f0 commit 342142b

File tree

3 files changed

+84
-30
lines changed

3 files changed

+84
-30
lines changed

lib/preprocessor.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -942,7 +942,9 @@ void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processe
942942
std::map<std::string, std::string> defs(getcfgmap(_settings ? _settings->userDefines : std::string("")));
943943

944944
if (_settings && _settings->_maxConfigs == 1U) {
945-
processedFile = handleIncludes(processedFile, filename, includePaths, defs);
945+
std::list<std::string> pragmaOnce;
946+
std::list<std::string> includes;
947+
processedFile = handleIncludes(processedFile, filename, includePaths, defs, pragmaOnce, includes);
946948
resultConfigurations = getcfgs(processedFile, filename, defs);
947949
} else {
948950
handleIncludes(processedFile, filename, includePaths);
@@ -1932,7 +1934,7 @@ static bool openHeader(std::string &filename, const std::list<std::string> &incl
19321934
}
19331935

19341936

1935-
std::string Preprocessor::handleIncludes(const std::string &code, const std::string &filePath, const std::list<std::string> &includePaths, std::map<std::string,std::string> &defs, std::list<std::string> includes)
1937+
std::string Preprocessor::handleIncludes(const std::string &code, const std::string &filePath, const std::list<std::string> &includePaths, std::map<std::string,std::string> &defs, std::list<std::string> &pragmaOnce, std::list<std::string> includes)
19361938
{
19371939
const std::string path(filePath.substr(0, 1 + filePath.find_last_of("\\/")));
19381940

@@ -1975,7 +1977,9 @@ std::string Preprocessor::handleIncludes(const std::string &code, const std::str
19751977

19761978
std::stack<bool>::reference elseIsTrue = elseIsTrueStack.top();
19771979

1978-
if (line.compare(0,7,"#ifdef ") == 0) {
1980+
if (line == "#pragma once") {
1981+
pragmaOnce.push_back(filePath);
1982+
} else if (line.compare(0,7,"#ifdef ") == 0) {
19791983
if (indent == indentmatch) {
19801984
const std::string tag = getdef(line,true);
19811985
if (defs.find(tag) != defs.end()) {
@@ -2115,8 +2119,14 @@ std::string Preprocessor::handleIncludes(const std::string &code, const std::str
21152119

21162120
includes.push_back(filename);
21172121

2122+
// Don't include header if it's already included and contains #pragma once
2123+
if (std::find(pragmaOnce.begin(), pragmaOnce.end(), filename) != pragmaOnce.end()) {
2124+
ostr << std::endl;
2125+
continue;
2126+
}
2127+
21182128
ostr << "#file \"" << filename << "\"\n"
2119-
<< handleIncludes(read(fin, filename), filename, includePaths, defs, includes) << std::endl
2129+
<< handleIncludes(read(fin, filename), filename, includePaths, defs, pragmaOnce, includes) << std::endl
21202130
<< "#endfile\n";
21212131
continue;
21222132
}

lib/preprocessor.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,11 @@ class CPPCHECKLIB Preprocessor {
233233
* @param filePath filename of code
234234
* @param includePaths Paths where headers might be
235235
* @param defs defines (only values)
236+
* @param pragmaOnce includes that has already been included and contains a #pragma once statement
236237
* @param includes provide a empty list. this is just used to prevent recursive inclusions.
237238
* \return resulting string
238239
*/
239-
std::string handleIncludes(const std::string &code, const std::string &filePath, const std::list<std::string> &includePaths, std::map<std::string,std::string> &defs, std::list<std::string> includes = std::list<std::string>());
240+
std::string handleIncludes(const std::string &code, const std::string &filePath, const std::list<std::string> &includePaths, std::map<std::string,std::string> &defs, std::list<std::string> &pragmaOnce, std::list<std::string> includes);
240241

241242
void setFile0(const std::string &f) {
242243
file0 = f;

test/testpreprocessor.cpp

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ class TestPreprocessor : public TestFixture {
208208
TEST_CASE(pragma);
209209
TEST_CASE(pragma_asm_1);
210210
TEST_CASE(pragma_asm_2);
211+
TEST_CASE(pragma_once);
211212
TEST_CASE(endifsemicolon);
212213
TEST_CASE(missing_doublequote);
213214
TEST_CASE(handle_error);
@@ -2410,6 +2411,19 @@ class TestPreprocessor : public TestFixture {
24102411
ASSERT_EQUALS("\n\nasm(temp);\nbbb\n", actual[""]);
24112412
}
24122413

2414+
void pragma_once() {
2415+
const char code[] = "#pragma once\n"
2416+
"int x";
2417+
2418+
Preprocessor preprocessor(NULL, this);
2419+
const std::list<std::string> includePaths;
2420+
std::map<std::string,std::string> defs;
2421+
std::list<std::string> pragmaOnce;
2422+
preprocessor.handleIncludes(code, "123.h", includePaths, defs, pragmaOnce, std::list<std::string>());
2423+
ASSERT_EQUALS(1U, pragmaOnce.size());
2424+
ASSERT_EQUALS("123.h", *(pragmaOnce.begin()));
2425+
}
2426+
24132427
void endifsemicolon() {
24142428
const char filedata[] = "void f() {\n"
24152429
"#ifdef A\n"
@@ -3278,12 +3292,14 @@ class TestPreprocessor : public TestFixture {
32783292
defs.clear();
32793293
defs["A"] = "";
32803294
{
3295+
std::list<std::string> pragmaOnce;
32813296
const std::string code("#ifdef A\n123\n#endif\n");
3282-
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3297+
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
32833298
ASSERT_EQUALS("\n123\n\n", actual);
32843299
}{
3300+
std::list<std::string> pragmaOnce;
32853301
const std::string code("#ifdef B\n123\n#endif\n");
3286-
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3302+
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
32873303
ASSERT_EQUALS("\n\n\n", actual);
32883304
}
32893305
}
@@ -3293,33 +3309,37 @@ class TestPreprocessor : public TestFixture {
32933309
defs.clear();
32943310
defs["A"] = "";
32953311
{
3312+
std::list<std::string> pragmaOnce;
32963313
const std::string code("#ifndef A\n123\n#endif\n");
3297-
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3314+
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
32983315
ASSERT_EQUALS("\n\n\n", actual);
32993316
}{
3317+
std::list<std::string> pragmaOnce;
33003318
const std::string code("#ifndef B\n123\n#endif\n");
3301-
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3319+
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
33023320
ASSERT_EQUALS("\n123\n\n", actual);
33033321
}
33043322
}
33053323

33063324
// define - ifndef
33073325
{
3326+
std::list<std::string> pragmaOnce;
33083327
defs.clear();
33093328
const std::string code("#ifndef X\n#define X\n123\n#endif\n"
33103329
"#ifndef X\n#define X\n123\n#endif\n");
3311-
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3330+
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
33123331
ASSERT_EQUALS("\n#define X\n123\n\n" "\n\n\n\n", actual);
33133332
}
33143333

33153334
// #define => #if
33163335
{
3336+
std::list<std::string> pragmaOnce;
33173337
defs.clear();
33183338
const std::string code("#define X 123\n"
33193339
"#if X==123\n"
33203340
"456\n"
33213341
"#endif\n");
3322-
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3342+
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
33233343
ASSERT_EQUALS("#define X 123\n\n456\n\n", actual);
33243344
}
33253345

@@ -3335,23 +3355,26 @@ class TestPreprocessor : public TestFixture {
33353355
"4\n"
33363356
"#endif");
33373357
{
3358+
std::list<std::string> pragmaOnce;
33383359
defs.clear();
33393360
defs["A"] = "";
33403361
defs["C"] = "";
3341-
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3362+
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
33423363
ASSERT_EQUALS("\n1\n\n\n\n\n\n\n\n", actual);
33433364
}
33443365

33453366
{
3367+
std::list<std::string> pragmaOnce;
33463368
defs.clear();
33473369
defs["B"] = "";
3348-
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3370+
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
33493371
ASSERT_EQUALS("\n\n\n2\n\n\n\n\n\n", actual);
33503372
}
33513373

33523374
{
3375+
std::list<std::string> pragmaOnce;
33533376
defs.clear();
3354-
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3377+
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
33553378
ASSERT_EQUALS("\n\n\n\n\n\n\n4\n\n", actual);
33563379
}
33573380
}
@@ -3360,9 +3383,10 @@ class TestPreprocessor : public TestFixture {
33603383
{
33613384
// see also endifsemicolon
33623385
const std::string code("{\n#ifdef X\n#endif;\n}");
3386+
std::list<std::string> pragmaOnce;
33633387
defs.clear();
33643388
defs["Z"] = "";
3365-
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3389+
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
33663390
ASSERT_EQUALS("{\n\n\n}\n", actual);
33673391
}
33683392

@@ -3373,21 +3397,26 @@ class TestPreprocessor : public TestFixture {
33733397
"123\n"
33743398
"#endif\n");
33753399

3400+
std::list<std::string> pragmaOnce;
3401+
3402+
pragmaOnce.clear();
33763403
defs.clear();
3377-
const std::string actual1(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3404+
const std::string actual1(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
33783405

3406+
pragmaOnce.clear();
33793407
defs.clear();
3380-
const std::string actual(preprocessor.handleIncludes(code + "#undef X\n" + code, filePath, includePaths, defs));
3408+
const std::string actual(preprocessor.handleIncludes(code + "#undef X\n" + code, filePath, includePaths, defs,pragmaOnce,std::list<std::string>()));
33813409

33823410
ASSERT_EQUALS(actual1 + "#undef X\n" + actual1, actual);
33833411
}
33843412

33853413
// #error
33863414
{
33873415
errout.str("");
3416+
std::list<std::string> pragmaOnce;
33883417
defs.clear();
33893418
const std::string code("#ifndef X\n#error abc\n#endif");
3390-
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3419+
const std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
33913420
ASSERT_EQUALS("\n#error abc\n\n", actual);
33923421
ASSERT_EQUALS("[test.c:2]: (error) abc\n", errout.str());
33933422
}
@@ -3403,40 +3432,48 @@ class TestPreprocessor : public TestFixture {
34033432
// missing local include
34043433
{
34053434
const std::string code("#include \"missing-include!!.h\"\n");
3435+
std::list<std::string> pragmaOnce;
34063436

3437+
pragmaOnce.clear();
34073438
errout.str("");
34083439
settings = Settings();
3409-
preprocessor.handleIncludes(code,"test.c",includePaths,defs);
3440+
preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list<std::string>());
34103441
ASSERT_EQUALS("", errout.str());
34113442

3443+
pragmaOnce.clear();
34123444
errout.str("");
34133445
settings.checkConfiguration = true;
3414-
preprocessor.handleIncludes(code,"test.c",includePaths,defs);
3446+
preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list<std::string>());
34153447
ASSERT_EQUALS("[test.c:1]: (information) Include file: \"missing-include!!.h\" not found.\n", errout.str());
34163448

3449+
pragmaOnce.clear();
34173450
errout.str("");
34183451
settings.nomsg.addSuppression("missingInclude");
3419-
preprocessor.handleIncludes(code,"test.c",includePaths,defs);
3452+
preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list<std::string>());
34203453
ASSERT_EQUALS("", errout.str());
34213454
}
34223455

34233456
// missing system header
34243457
{
34253458
const std::string code("#include <missing-include!!.h>\n");
3459+
std::list<std::string> pragmaOnce;
34263460

3461+
pragmaOnce.clear();
34273462
errout.str("");
34283463
settings = Settings();
3429-
preprocessor.handleIncludes(code,"test.c",includePaths,defs);
3464+
preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list<std::string>());
34303465
ASSERT_EQUALS("", errout.str());
34313466

3467+
pragmaOnce.clear();
34323468
errout.str("");
34333469
settings.checkConfiguration = true;
3434-
preprocessor.handleIncludes(code,"test.c",includePaths,defs);
3470+
preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list<std::string>());
34353471
ASSERT_EQUALS("[test.c:1]: (information) Include file: <missing-include!!.h> not found.\n", errout.str());
34363472

3473+
pragmaOnce.clear();
34373474
errout.str("");
34383475
settings.nomsg.addSuppression("missingIncludeSystem");
3439-
preprocessor.handleIncludes(code,"test.c",includePaths,defs);
3476+
preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list<std::string>());
34403477
ASSERT_EQUALS("", errout.str());
34413478
}
34423479

@@ -3449,9 +3486,10 @@ class TestPreprocessor : public TestFixture {
34493486
defs.clear();
34503487
defs["GNU"] = "";
34513488

3489+
std::list<std::string> pragmaOnce;
34523490
errout.str("");
34533491
settings = Settings();
3454-
preprocessor.handleIncludes(code,"test.c",includePaths,defs);
3492+
preprocessor.handleIncludes(code,"test.c",includePaths,defs,pragmaOnce,std::list<std::string>());
34553493
ASSERT_EQUALS("", errout.str());
34563494
}
34573495
}
@@ -3464,6 +3502,7 @@ class TestPreprocessor : public TestFixture {
34643502

34653503
// #3405
34663504
{
3505+
std::list<std::string> pragmaOnce;
34673506
defs.clear();
34683507
defs["A"] = "";
34693508
const std::string code("\n#ifndef PAL_UTIL_UTILS_H_\n"
@@ -3488,7 +3527,7 @@ class TestPreprocessor : public TestFixture {
34883527
"8\n"
34893528
"#endif\n"
34903529
"\n");
3491-
std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs));
3530+
std::string actual(preprocessor.handleIncludes(code,filePath,includePaths,defs,pragmaOnce,std::list<std::string>()));
34923531

34933532
// the 1,2,4 should be in the result
34943533
actual.erase(0, actual.find("1"));
@@ -3499,14 +3538,15 @@ class TestPreprocessor : public TestFixture {
34993538

35003539
// #3418
35013540
{
3541+
std::list<std::string> pragmaOnce;
35023542
defs.clear();
35033543
const char code[] = "#define A 1\n"
35043544
"#define B A\n"
35053545
"#if A == B\n"
35063546
"123\n"
35073547
"#endif\n";
35083548

3509-
std::string actual(preprocessor.handleIncludes(code, filePath, includePaths, defs));
3549+
std::string actual(preprocessor.handleIncludes(code, filePath, includePaths, defs,pragmaOnce,std::list<std::string>()));
35103550
ASSERT_EQUALS("#define A 1\n#define B A\n\n123\n\n", actual);
35113551
}
35123552
}
@@ -3530,8 +3570,9 @@ class TestPreprocessor : public TestFixture {
35303570
const std::list<std::string> includePaths;
35313571
std::map<std::string,std::string> defs;
35323572
defs["A"] = "1";
3573+
std::list<std::string> pragmaOnce;
35333574
ASSERT_EQUALS(std::string::npos, // No "123" in the output
3534-
preprocessor.handleIncludes(code, "test.c", includePaths, defs).find("123"));
3575+
preprocessor.handleIncludes(code, "test.c", includePaths, defs, pragmaOnce,std::list<std::string>()).find("123"));
35353576
}
35363577

35373578
void def_handleIncludes_ifelse3() { // #4865
@@ -3550,7 +3591,8 @@ class TestPreprocessor : public TestFixture {
35503591
std::map<std::string,std::string> defs;
35513592
defs["B"] = "1";
35523593
defs["C"] = "1";
3553-
preprocessor.handleIncludes(code, "test.c", includePaths, defs); // don't crash
3594+
std::list<std::string> pragmaOnce;
3595+
preprocessor.handleIncludes(code, "test.c", includePaths, defs, pragmaOnce, std::list<std::string>()); // don't crash
35543596
}
35553597

35563598
void def_valueWithParentheses() {
@@ -3564,11 +3606,12 @@ class TestPreprocessor : public TestFixture {
35643606
const std::string filePath("test.c");
35653607
const std::list<std::string> includePaths;
35663608
std::map<std::string,std::string> defs;
3609+
std::list<std::string> pragmaOnce;
35673610
Preprocessor preprocessor(NULL, this);
35683611

35693612
std::istringstream istr(code);
35703613
const std::string s(preprocessor.read(istr, ""));
3571-
preprocessor.handleIncludes(s, filePath, includePaths, defs);
3614+
preprocessor.handleIncludes(s, filePath, includePaths, defs, pragmaOnce, std::list<std::string>());
35723615

35733616
ASSERT(defs.find("A") != defs.end());
35743617
ASSERT_EQUALS("(Fred)", defs["A"]);

0 commit comments

Comments
 (0)