cppparser: Support __VA_OPT__ in variadic preprocessor defs

This commit is contained in:
rdb 2021-03-02 13:07:27 +01:00
parent 050d86dadb
commit 8eccc9f569
3 changed files with 145 additions and 67 deletions

View File

@ -23,7 +23,7 @@ using std::string;
*/ */
CPPManifest::ExpansionNode:: CPPManifest::ExpansionNode::
ExpansionNode(int parm_number, bool stringify, bool paste) : ExpansionNode(int parm_number, bool stringify, bool paste) :
_parm_number(parm_number), _stringify(stringify), _paste(paste) _parm_number(parm_number), _stringify(stringify), _paste(paste), _optional(false)
{ {
} }
@ -32,7 +32,16 @@ ExpansionNode(int parm_number, bool stringify, bool paste) :
*/ */
CPPManifest::ExpansionNode:: CPPManifest::ExpansionNode::
ExpansionNode(const string &str, bool paste) : ExpansionNode(const string &str, bool paste) :
_parm_number(-1), _stringify(false), _paste(paste), _str(str) _parm_number(-1), _stringify(false), _paste(paste), _optional(false), _str(str)
{
}
/**
*
*/
CPPManifest::ExpansionNode::
ExpansionNode(Expansion nested, bool stringify, bool paste, bool optional) :
_parm_number(-1), _stringify(stringify), _paste(paste), _optional(optional), _nested(std::move(nested))
{ {
} }
@ -76,7 +85,7 @@ CPPManifest(const string &args, const cppyyltype &loc) :
p++; p++;
} }
save_expansion(args.substr(p), parameter_names); save_expansion(_expansion, args.substr(p), parameter_names);
} }
/** /**
@ -119,7 +128,7 @@ CPPManifest(const string &macro, const string &definition) :
_num_parameters = 0; _num_parameters = 0;
} }
save_expansion(definition, parameter_names); save_expansion(_expansion, definition, parameter_names);
} }
/** /**
@ -186,54 +195,7 @@ stringify(const string &source) {
*/ */
string CPPManifest:: string CPPManifest::
expand(const vector_string &args) const { expand(const vector_string &args) const {
string result; return r_expand(_expansion, args);
Expansion::const_iterator ei;
for (ei = _expansion.begin(); ei != _expansion.end(); ++ei) {
if ((*ei)._parm_number >= 0) {
int i = (*ei)._parm_number;
string subst;
if (i < (int)args.size()) {
subst = args[i];
if (i == _variadic_param) {
for (++i; i < (int)args.size(); ++i) {
subst += ", " + args[i];
}
}
if ((*ei)._stringify) {
subst = stringify(subst);
}
} else if (i == _variadic_param && (*ei)._paste) {
// Special case GCC behavior: if __VA_ARGS__ is pasted to a comma and
// no arguments are passed, the comma is removed. MSVC does this
// automatically. Not sure if we should allow MSVC behavior as well.
if (!result.empty() && *result.rbegin() == ',') {
result.resize(result.size() - 1);
}
}
if (!subst.empty()) {
if (result.empty() || (*ei)._paste) {
result += subst;
} else {
result += ' ';
result += subst;
}
}
}
if (!(*ei)._str.empty()) {
if (result.empty() || (*ei)._paste) {
result += (*ei)._str;
} else {
result += ' ';
result += (*ei)._str;
}
}
}
return result;
} }
/** /**
@ -283,19 +245,25 @@ output(std::ostream &out) const {
out << " "; out << " ";
} }
if ((*ei)._parm_number >= 0) {
if ((*ei)._stringify) { if ((*ei)._stringify) {
out << "#"; out << "#";
} }
if ((*ei)._parm_number >= 0) {
if ((*ei)._parm_number == _variadic_param) { if ((*ei)._parm_number == _variadic_param) {
out << "__VA_ARGS__"; out << "__VA_ARGS__";
} else { } else {
out << "$" << (*ei)._parm_number + 1; out << "$" << (*ei)._parm_number + 1;
} }
} }
if ((*ei)._optional) {
out << "__VA_OPT__(";
}
if (!(*ei)._str.empty()) { if (!(*ei)._str.empty()) {
out << (*ei)._str; out << (*ei)._str;
} }
if ((*ei)._optional) {
out << ")";
}
} }
} }
@ -350,7 +318,7 @@ parse_parameters(const string &args, size_t &p,
* *
*/ */
void CPPManifest:: void CPPManifest::
save_expansion(const string &exp, const vector_string &parameter_names) { save_expansion(Expansion &expansion, const string &exp, const vector_string &parameter_names) {
// Walk through the expansion string. For each substring that is an // Walk through the expansion string. For each substring that is an
// identifier, check it against parameter_names. // identifier, check it against parameter_names.
size_t p = 0; size_t p = 0;
@ -375,6 +343,40 @@ save_expansion(const string &exp, const vector_string &parameter_names) {
// C99-style variadics, ie. #define macro(...) __VA_ARGS__ // C99-style variadics, ie. #define macro(...) __VA_ARGS__
pnum = _variadic_param; pnum = _variadic_param;
} else if (ident == "__VA_OPT__") {
// Optional expansion, only expands if __VA_ARGS__ is non-empty
while (p < exp.size() && isspace(exp[p])) {
++p;
}
if (p < exp.size() && exp[p] == '(') {
int start = ++p;
int nesting = 1;
while (p < exp.size() && nesting > 0) {
if (exp[p] == '(') {
++nesting;
}
else if (exp[p] == ')') {
--nesting;
}
++p;
}
if (last != q) {
expansion.push_back(ExpansionNode(exp.substr(last, q - last), paste));
paste = false;
}
// Store this as a nested expansion, because the whole thing may be
// stringified as a whole.
Expansion nested;
save_expansion(nested, exp.substr(start, p - 1 - start), parameter_names);
expansion.push_back(ExpansionNode(std::move(nested), stringify, paste, true));
stringify = false;
paste = false;
last = p;
continue;
}
} else { } else {
for (int i = 0; pnum == -1 && i < (int)parameter_names.size(); ++i) { for (int i = 0; pnum == -1 && i < (int)parameter_names.size(); ++i) {
const string &pname = parameter_names[i]; const string &pname = parameter_names[i];
@ -387,10 +389,10 @@ save_expansion(const string &exp, const vector_string &parameter_names) {
if (pnum != -1) { if (pnum != -1) {
// Yep! // Yep!
if (last != q) { if (last != q) {
_expansion.push_back(ExpansionNode(exp.substr(last, q - last), paste)); expansion.push_back(ExpansionNode(exp.substr(last, q - last), paste));
paste = false; paste = false;
} }
_expansion.push_back(ExpansionNode(pnum, stringify, paste)); expansion.push_back(ExpansionNode(pnum, stringify, paste));
stringify = false; stringify = false;
paste = false; paste = false;
last = p; last = p;
@ -398,7 +400,7 @@ save_expansion(const string &exp, const vector_string &parameter_names) {
} else if (exp[p] == '#') { } else if (exp[p] == '#') {
// This may be a stringification operator. // This may be a stringification operator.
if (last != p) { if (last != p) {
_expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste)); expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste));
paste = false; paste = false;
} }
@ -416,7 +418,7 @@ save_expansion(const string &exp, const vector_string &parameter_names) {
} else if (isspace(exp[p])) { } else if (isspace(exp[p])) {
if (last != p) { if (last != p) {
_expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste)); expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste));
paste = false; paste = false;
} }
@ -429,6 +431,75 @@ save_expansion(const string &exp, const vector_string &parameter_names) {
} }
if (last != p) { if (last != p) {
_expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste)); expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste));
} }
} }
/**
*
*/
string CPPManifest::
r_expand(const Expansion &expansion, const vector_string &args) const {
std::string result;
for (const ExpansionNode &node : expansion) {
if (node._parm_number >= 0) {
int i = node._parm_number;
string subst;
if (i < (int)args.size()) {
subst = args[i];
if (i == _variadic_param) {
for (++i; i < (int)args.size(); ++i) {
subst += ", " + args[i];
}
}
if (node._stringify) {
subst = stringify(subst);
}
} else if (i == _variadic_param && node._paste) {
// Special case GCC behavior: if __VA_ARGS__ is pasted to a comma and
// no arguments are passed, the comma is removed. MSVC does this
// automatically. Not sure if we should allow MSVC behavior as well.
if (!result.empty() && *result.rbegin() == ',') {
result.resize(result.size() - 1);
}
}
if (!subst.empty()) {
if (result.empty() || node._paste) {
result += subst;
} else {
result += ' ';
result += subst;
}
}
}
if (!node._str.empty()) {
if (result.empty() || node._paste) {
result += node._str;
} else {
result += ' ';
result += node._str;
}
}
if (!node._nested.empty()) {
string nested_result;
if (node._optional && args.size() >= _num_parameters) {
nested_result = r_expand(node._nested, args);
}
if (node._stringify) {
nested_result = stringify(nested_result);
}
if (result.empty() || node._paste) {
result += nested_result;
} else {
result += ' ';
result += nested_result;
}
}
}
return result;
}

View File

@ -54,21 +54,28 @@ public:
CPPVisibility _vis; CPPVisibility _vis;
private: private:
void parse_parameters(const std::string &args, size_t &p,
vector_string &parameter_names);
void save_expansion(const std::string &exp,
const vector_string &parameter_names);
class ExpansionNode { class ExpansionNode {
public: public:
ExpansionNode(int parm_number, bool stringify, bool paste); ExpansionNode(int parm_number, bool stringify, bool paste);
ExpansionNode(const std::string &str, bool paste = false); ExpansionNode(const std::string &str, bool paste = false);
ExpansionNode(std::vector<ExpansionNode> nested, bool stringify = false, bool paste = false, bool optional = false);
int _parm_number; int _parm_number;
bool _stringify; bool _stringify;
bool _paste; bool _paste;
bool _optional;
std::string _str; std::string _str;
std::vector<ExpansionNode> _nested;
}; };
typedef std::vector<ExpansionNode> Expansion; typedef std::vector<ExpansionNode> Expansion;
void parse_parameters(const std::string &args, size_t &p,
vector_string &parameter_names);
void save_expansion(Expansion &expansion, const std::string &exp,
const vector_string &parameter_names);
std::string r_expand(const Expansion &expansion,
const vector_string &args = vector_string()) const;
Expansion _expansion; Expansion _expansion;
}; };

View File

@ -2295,7 +2295,7 @@ extract_manifest_args(const string &name, int num_args, int va_arg,
loc.last_column = first_col; loc.last_column = first_col;
loc.file = first_file; loc.file = first_file;
if ((int)args.size() < num_args) { if ((int)args.size() < num_args - (va_arg >= 0)) {
warning("Not enough arguments for manifest " + name, loc); warning("Not enough arguments for manifest " + name, loc);
} else if (va_arg < 0 && (int)args.size() > num_args) { } else if (va_arg < 0 && (int)args.size() > num_args) {