Skip to content

Commit 8f8bfe3

Browse files
authored
Fix #368 (__VA_OPT__ is not handled good enough) (#451)
1 parent a6d3586 commit 8f8bfe3

File tree

3 files changed

+148
-27
lines changed

3 files changed

+148
-27
lines changed

simplecpp.cpp

Lines changed: 89 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,7 @@ namespace simplecpp {
14851485

14861486
class Macro {
14871487
public:
1488-
explicit Macro(std::vector<std::string> &f) : nameTokDef(nullptr), valueToken(nullptr), endToken(nullptr), files(f), tokenListDefine(f), variadic(false), valueDefinedInCode_(false) {}
1488+
explicit Macro(std::vector<std::string> &f) : nameTokDef(nullptr), valueToken(nullptr), endToken(nullptr), files(f), tokenListDefine(f), variadic(false), variadicOpt(false), optExpandValue(nullptr), optNoExpandValue(nullptr), valueDefinedInCode_(false) {}
14891489

14901490
Macro(const Token *tok, std::vector<std::string> &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(true) {
14911491
if (sameline(tok->previousSkipComments(), tok))
@@ -1515,6 +1515,11 @@ namespace simplecpp {
15151515
*this = other;
15161516
}
15171517

1518+
~Macro() {
1519+
delete optExpandValue;
1520+
delete optNoExpandValue;
1521+
}
1522+
15181523
Macro &operator=(const Macro &other) {
15191524
if (this != &other) {
15201525
files = other.files;
@@ -1707,6 +1712,9 @@ namespace simplecpp {
17071712
bool parseDefine(const Token *nametoken) {
17081713
nameTokDef = nametoken;
17091714
variadic = false;
1715+
variadicOpt = false;
1716+
optExpandValue = nullptr;
1717+
optNoExpandValue = nullptr;
17101718
if (!nameTokDef) {
17111719
valueToken = endToken = nullptr;
17121720
args.clear();
@@ -1744,8 +1752,54 @@ namespace simplecpp {
17441752
if (!sameline(valueToken, nameTokDef))
17451753
valueToken = nullptr;
17461754
endToken = valueToken;
1747-
while (sameline(endToken, nameTokDef))
1755+
while (sameline(endToken, nameTokDef)) {
1756+
if (variadic && endToken->str() == "__VA_OPT__")
1757+
variadicOpt = true;
17481758
endToken = endToken->next;
1759+
}
1760+
1761+
if (variadicOpt) {
1762+
TokenList expandValue(files);
1763+
TokenList noExpandValue(files);
1764+
for (const Token *tok = valueToken; tok && tok != endToken;) {
1765+
if (tok->str() == "__VA_OPT__") {
1766+
if (!sameline(tok, tok->next) || tok->next->op != '(')
1767+
throw Error(tok->location, "In definition of '" + nameTokDef->str() + "': Missing opening parenthesis for __VA_OPT__");
1768+
tok = tok->next->next;
1769+
int par = 1;
1770+
while (tok && tok != endToken) {
1771+
if (tok->op == '(')
1772+
par++;
1773+
else if (tok->op == ')')
1774+
par--;
1775+
else if (tok->str() == "__VA_OPT__")
1776+
throw Error(tok->location, "In definition of '" + nameTokDef->str() + "': __VA_OPT__ cannot be nested");
1777+
if (par == 0) {
1778+
tok = tok->next;
1779+
break;
1780+
}
1781+
expandValue.push_back(new Token(*tok));
1782+
tok = tok->next;
1783+
}
1784+
if (par != 0) {
1785+
const Token *const lastTok = expandValue.back() ? expandValue.back() : valueToken->next;
1786+
throw Error(lastTok->location, "In definition of '" + nameTokDef->str() + "': Missing closing parenthesis for __VA_OPT__");
1787+
}
1788+
} else {
1789+
expandValue.push_back(new Token(*tok));
1790+
noExpandValue.push_back(new Token(*tok));
1791+
tok = tok->next;
1792+
}
1793+
}
1794+
#if __cplusplus >= 201103L
1795+
optExpandValue = new TokenList(std::move(expandValue));
1796+
optNoExpandValue = new TokenList(std::move(noExpandValue));
1797+
#else
1798+
optExpandValue = new TokenList(expandValue);
1799+
optNoExpandValue = new TokenList(noExpandValue);
1800+
#endif
1801+
}
1802+
17491803
return true;
17501804
}
17511805

@@ -1900,8 +1954,22 @@ namespace simplecpp {
19001954

19011955
Token * const output_end_1 = output->back();
19021956

1957+
const Token *valueToken2;
1958+
const Token *endToken2;
1959+
1960+
if (variadicOpt) {
1961+
if (parametertokens2.size() > args.size() && parametertokens2[args.size() - 1]->next->op != ')')
1962+
valueToken2 = optExpandValue->cfront();
1963+
else
1964+
valueToken2 = optNoExpandValue->cfront();
1965+
endToken2 = nullptr;
1966+
} else {
1967+
valueToken2 = valueToken;
1968+
endToken2 = endToken;
1969+
}
1970+
19031971
// expand
1904-
for (const Token *tok = valueToken; tok != endToken;) {
1972+
for (const Token *tok = valueToken2; tok != endToken2;) {
19051973
if (tok->op != '#') {
19061974
// A##B => AB
19071975
if (sameline(tok, tok->next) && tok->next && tok->next->op == '#' && tok->next->next && tok->next->next->op == '#') {
@@ -1950,7 +2018,7 @@ namespace simplecpp {
19502018
}
19512019

19522020
tok = tok->next;
1953-
if (tok == endToken) {
2021+
if (tok == endToken2) {
19542022
output->push_back(new Token(*tok->previous));
19552023
break;
19562024
}
@@ -2020,24 +2088,6 @@ namespace simplecpp {
20202088
// Macro parameter..
20212089
{
20222090
TokenList temp(files);
2023-
if (tok->str() == "__VA_OPT__") {
2024-
if (sameline(tok, tok->next) && tok->next->str() == "(") {
2025-
tok = tok->next;
2026-
int paren = 1;
2027-
while (sameline(tok, tok->next)) {
2028-
if (tok->next->str() == "(")
2029-
++paren;
2030-
else if (tok->next->str() == ")")
2031-
--paren;
2032-
if (paren == 0)
2033-
return tok->next->next;
2034-
tok = tok->next;
2035-
if (parametertokens.size() > args.size() && parametertokens.front()->next->str() != ")")
2036-
tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens)->previous;
2037-
}
2038-
}
2039-
throw Error(tok->location, "Missing parenthesis for __VA_OPT__(content)");
2040-
}
20412091
if (expandArg(&temp, tok, loc, macros, expandedmacros, parametertokens)) {
20422092
if (tok->str() == "__VA_ARGS__" && temp.empty() && output->cback() && output->cback()->str() == "," &&
20432093
tok->nextSkipComments() && tok->nextSkipComments()->str() == ")")
@@ -2338,6 +2388,13 @@ namespace simplecpp {
23382388
/** is macro variadic? */
23392389
bool variadic;
23402390

2391+
/** does the macro expansion have __VA_OPT__? */
2392+
bool variadicOpt;
2393+
2394+
/** Expansion value for varadic macros with __VA_OPT__ expanded and discarded respectively */
2395+
const TokenList *optExpandValue;
2396+
const TokenList *optNoExpandValue;
2397+
23412398
/** was the value of this macro actually defined in the code? */
23422399
bool valueDefinedInCode_;
23432400
};
@@ -3621,6 +3678,16 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL
36213678
}
36223679
output.clear();
36233680
return;
3681+
} catch (simplecpp::Macro::Error &err) {
3682+
if (outputList) {
3683+
simplecpp::Output out(files);
3684+
out.type = simplecpp::Output::SYNTAX_ERROR;
3685+
out.location = err.location;
3686+
out.msg = "Failed to parse #define, " + err.what;
3687+
outputList->push_back(out);
3688+
}
3689+
output.clear();
3690+
return;
36243691
}
36253692
} else if (ifstates.top() == True && rawtok->str() == INCLUDE) {
36263693
TokenList inc1(files);

test.cpp

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ static void define_va_opt_3()
923923

924924
simplecpp::OutputList outputList;
925925
ASSERT_EQUALS("", preprocess(code1, &outputList));
926-
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'err', Missing parenthesis for __VA_OPT__(content)\n",
926+
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing closing parenthesis for __VA_OPT__\n",
927927
toString(outputList));
928928

929929
outputList.clear();
@@ -934,7 +934,7 @@ static void define_va_opt_3()
934934
"err()";
935935

936936
ASSERT_EQUALS("", preprocess(code2, &outputList));
937-
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'err', Missing parenthesis for __VA_OPT__(content)\n",
937+
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n",
938938
toString(outputList));
939939
}
940940

@@ -946,7 +946,7 @@ static void define_va_opt_4()
946946

947947
simplecpp::OutputList outputList;
948948
ASSERT_EQUALS("", preprocess(code1, &outputList));
949-
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'err', Missing parenthesis for __VA_OPT__(content)\n",
949+
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n",
950950
toString(outputList));
951951

952952
outputList.clear();
@@ -956,7 +956,7 @@ static void define_va_opt_4()
956956
"err()";
957957

958958
ASSERT_EQUALS("", preprocess(code2, &outputList));
959-
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'err', Missing parenthesis for __VA_OPT__(content)\n",
959+
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n",
960960
toString(outputList));
961961
}
962962

@@ -968,7 +968,46 @@ static void define_va_opt_5()
968968

969969
simplecpp::OutputList outputList;
970970
ASSERT_EQUALS("", preprocess(code, &outputList));
971-
ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'err', Missing parenthesis for __VA_OPT__(content)\n",
971+
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n",
972+
toString(outputList));
973+
}
974+
975+
static void define_va_opt_6()
976+
{
977+
// nested __VA_OPT__
978+
const char code[] = "#define err(...) __VA_OPT__(__VA_OPT__(something))\n"
979+
"err()";
980+
981+
simplecpp::OutputList outputList;
982+
ASSERT_EQUALS("", preprocess(code, &outputList));
983+
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': __VA_OPT__ cannot be nested\n",
984+
toString(outputList));
985+
}
986+
987+
static void define_va_opt_7()
988+
{
989+
// eof in __VA_OPT__
990+
const char code1[] = "#define err(...) __VA_OPT__";
991+
992+
simplecpp::OutputList outputList;
993+
ASSERT_EQUALS("", preprocess(code1, &outputList));
994+
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n",
995+
toString(outputList));
996+
997+
outputList.clear();
998+
999+
const char code2[] = "#define err(...) __VA_OPT__(";
1000+
1001+
ASSERT_EQUALS("", preprocess(code2, &outputList));
1002+
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing closing parenthesis for __VA_OPT__\n",
1003+
toString(outputList));
1004+
1005+
outputList.clear();
1006+
1007+
const char code3[] = "#define err(...) __VA_OPT__(x";
1008+
1009+
ASSERT_EQUALS("", preprocess(code3, &outputList));
1010+
ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing closing parenthesis for __VA_OPT__\n",
9721011
toString(outputList));
9731012
}
9741013

@@ -3063,6 +3102,8 @@ int main(int argc, char **argv)
30633102
TEST_CASE(define_va_opt_3);
30643103
TEST_CASE(define_va_opt_4);
30653104
TEST_CASE(define_va_opt_5);
3105+
TEST_CASE(define_va_opt_6);
3106+
TEST_CASE(define_va_opt_7);
30663107

30673108
TEST_CASE(pragma_backslash); // multiline pragma directive
30683109

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %clang_cc1 -E %s | grep '^ printf( "%%s" , "Hello" );$'
2+
3+
#define P( x, ...) printf( x __VA_OPT__(,) __VA_ARGS__ )
4+
#define PF( x, ...) P( x __VA_OPT__(,) __VA_ARGS__ )
5+
6+
int main()
7+
{
8+
PF( "%s", "Hello" );
9+
PF( "Hello", );
10+
PF( "Hello" );
11+
PF( , );
12+
PF( );
13+
}

0 commit comments

Comments
 (0)