cppparser: support directives __LINE__, __FILE__, __has_include

This commit is contained in:
rdb 2017-01-04 22:46:57 +01:00
parent fa851ae5ac
commit 40ea0ab786
2 changed files with 177 additions and 73 deletions

View File

@ -809,6 +809,8 @@ expand_manifests(const string &input_expr, bool expand_undefined,
// Here's an identifier. Is it "defined"?
if (ident == "defined") {
expand_defined_function(expr, q, p);
} else if (expand_undefined && ident == "__has_include") {
expand_has_include_function(expr, q, p, loc);
} else {
// Is it a manifest?
Manifests::const_iterator mi = _manifests.find(ident);
@ -817,6 +819,20 @@ expand_manifests(const string &input_expr, bool expand_undefined,
expand_manifest_inline(expr, q, p, manifest);
manifest_found = true;
} else if (ident == "__FILE__") {
// Special case: this is a dynamic definition.
string file = string("\"") + loc.file._filename_as_referenced.get_fullpath() + "\"";
expr = expr.substr(0, q) + file + expr.substr(p);
p = q + file.size();
manifest_found = true;
} else if (ident == "__LINE__") {
// So is this.
string line = format_string(loc.first_line);
expr = expr.substr(0, q) + line + expr.substr(p);
p = q + line.size();
manifest_found = true;
} else if (expand_undefined && ident != "true" && ident != "false") {
// It is not found. Expand it to 0, but only if we are currently
// parsing an #if expression.
@ -1486,31 +1502,22 @@ handle_undef_directive(const string &args, const YYLTYPE &loc) {
*/
void CPPPreprocessor::
handle_ifdef_directive(const string &args, const YYLTYPE &loc) {
Manifests::const_iterator mi = _manifests.find(args);
if (mi != _manifests.end()) {
// The macro is defined. We continue.
return;
}
if (!is_manifest_defined(args)) {
// The macro is undefined. Skip stuff.
skip_false_if_block(true);
}
}
/**
*
*/
void CPPPreprocessor::
handle_ifndef_directive(const string &args, const YYLTYPE &loc) {
Manifests::const_iterator mi = _manifests.find(args);
if (mi == _manifests.end()) {
// The macro is undefined. We continue.
return;
}
if (is_manifest_defined(args)) {
// The macro is defined. Skip stuff.
skip_false_if_block(true);
}
}
/**
*
@ -1593,6 +1600,8 @@ handle_include_directive(const string &args, const YYLTYPE &loc) {
_angle_includes.insert(filename);
}
}
} else {
warning("Ignoring invalid #include directive", loc);
}
filename.set_text();
@ -1600,49 +1609,9 @@ handle_include_directive(const string &args, const YYLTYPE &loc) {
// Now look for the filename. If we didn't use angle quotes, look first in
// the current directory.
bool found_file = false;
CPPFile::Source source = CPPFile::S_none;
if (okflag) {
found_file = false;
// Search the current directory.
if (!angle_quotes && !found_file && filename.exists()) {
found_file = true;
source = CPPFile::S_local;
}
// Search the same directory as the includer.
if (!angle_quotes && !found_file) {
Filename match(get_file()._filename.get_dirname(), filename);
if (match.exists()) {
filename = match;
found_file = true;
source = CPPFile::S_alternate;
}
}
// Now search the angle-include-path
if (angle_quotes && !found_file && filename.resolve_filename(_angle_include_path)) {
found_file = true;
source = CPPFile::S_system;
}
// Now search the quote-include-path
if (!angle_quotes && !found_file) {
for (size_t dir=0; dir<_quote_include_path.get_num_directories(); dir++) {
Filename match(_quote_include_path.get_directory(dir), filename);
if (match.exists()) {
filename = match;
found_file = true;
source = _quote_include_kind[dir];
}
}
}
if (!found_file) {
warning("Cannot find " + filename.get_fullpath(), loc);
} else {
if (find_include(filename, angle_quotes, source)) {
_last_c = '\0';
// If it was explicitly named on the command-line, mark it S_local.
@ -1662,9 +1631,8 @@ handle_include_directive(const string &args, const YYLTYPE &loc) {
if (!push_file(file)) {
warning("Unable to read " + filename.get_fullpath(), loc);
}
}
} else {
warning("Ignoring invalid #include directive", loc);
warning("Cannot find " + filename.get_fullpath(), loc);
}
}
@ -1779,6 +1747,69 @@ skip_false_if_block(bool consider_elifs) {
_save_comments = true;
}
/**
* Returns true if the given manifest is defined.
*/
bool CPPPreprocessor::
is_manifest_defined(const string &manifest_name) {
Manifests::const_iterator mi = _manifests.find(manifest_name);
if (mi != _manifests.end()) {
return true;
}
if (manifest_name == "__has_include" ||
manifest_name == "__FILE__" ||
manifest_name == "__LINE__") {
// Special built-in directives that are considered "defined".
return true;
}
return false;
}
/**
* Locates the given filename. Changes the first argument to the full path.
*/
bool CPPPreprocessor::
find_include(Filename &filename, bool angle_quotes, CPPFile::Source &source) {
// Now look for the filename. If we didn't use angle quotes, look first in
// the current directory.
if (!angle_quotes && filename.exists()) {
source = CPPFile::S_local;
return true;
}
// Search the same directory as the includer.
if (!angle_quotes) {
Filename match(get_file()._filename.get_dirname(), filename);
if (match.exists()) {
filename = match;
source = CPPFile::S_alternate;
return true;
}
}
// Now search the angle-include-path
if (angle_quotes && filename.resolve_filename(_angle_include_path)) {
source = CPPFile::S_system;
return true;
}
// Now search the quote-include-path
if (!angle_quotes) {
for (size_t dir = 0; dir < _quote_include_path.get_num_directories(); ++dir) {
Filename match(_quote_include_path.get_directory(dir), filename);
if (match.exists()) {
filename = match;
source = _quote_include_kind[dir];
return true;
}
}
}
return false;
}
/**
*
*/
@ -1885,6 +1916,14 @@ get_identifier(int c) {
if (mi != _manifests.end() && !should_ignore_manifest((*mi).second)) {
return expand_manifest((*mi).second);
}
if (name == "__FILE__") {
return get_literal(SIMPLE_STRING, loc, loc.file._filename_as_referenced);
}
if (name == "__LINE__") {
YYSTYPE result;
result.u.integer = loc.first_line;
return CPPToken(INTEGER, loc, "", result);
}
// Check for keywords.
int kw = check_keyword(name);
@ -2227,9 +2266,7 @@ expand_defined_function(string &expr, size_t q, size_t &p) {
vector_string args;
extract_manifest_args_inline("defined", 1, -1, args, expr, p);
if (args.size() >= 1) {
const string &manifest_name = args[0];
Manifests::const_iterator mi = _manifests.find(manifest_name);
if (mi != _manifests.end()) {
if (is_manifest_defined(args[0])) {
// The macro is defined; the result is "1".
result = "1";
} else {
@ -2242,6 +2279,70 @@ expand_defined_function(string &expr, size_t q, size_t &p) {
p = q + result.size();
}
/**
* Expands the __has_include(manifest) function to either 1 or 0, depending on
* whether the include file exists.
*/
void CPPPreprocessor::
expand_has_include_function(string &expr, size_t q, size_t &p, YYLTYPE loc) {
bool found_file = false;
// Skip whitespace till paren.
while (p < expr.size() && isspace(expr[p])) {
p++;
}
size_t args_begin = p + 1;
vector_string args;
extract_manifest_args_inline("__has_include", 1, -1, args, expr, p);
if (!args.empty() && args[0].size() >= 2) {
Filename filename;
bool angle_quotes = false;
string inc = args[0];
// Just to play things safe, since our manifest-expansion logic might not
// filter out quotes and angle brackets properly, we'll only expand
// manifests if we don't begin with a quote or bracket.
if (!inc.empty() && (inc[0] != '"' && inc[0] != '<')) {
inc = expand_manifests(inc, false, loc);
}
if (inc[0] == '"' && inc[inc.size() - 1] == '"') {
filename = inc.substr(1, inc.size() - 2);
} else if (inc[0] == '<' && inc[inc.size() - 1] == '>') {
filename = inc.substr(1, inc.size() - 2);
if (!_noangles) {
// If _noangles is true, we don't make a distinction between angle
// brackets and quote marks--all #inc statements are treated the
// same, as if they used quote marks.
angle_quotes = true;
}
} else {
loc.last_column += loc.first_column + p - 2;
loc.first_column += args_begin;
warning("invalid argument for __has_include() directive", loc);
expr = expr.substr(0, q) + "0" + expr.substr(p);
p = q + 1;
return;
}
filename.set_text();
CPPFile::Source source = CPPFile::S_none;
found_file = find_include(filename, angle_quotes, source);
} else {
loc.last_column += loc.first_column + p - 2;
loc.first_column += args_begin;
warning("invalid argument for __has_include() directive", loc);
}
string result = found_file ? "1" : "0";
expr = expr.substr(0, q) + result + expr.substr(p);
p = q + result.size();
}
/**
*
*/

View File

@ -143,6 +143,8 @@ private:
void handle_error_directive(const string &args, const YYLTYPE &loc);
void skip_false_if_block(bool consider_elifs);
bool is_manifest_defined(const string &manifest_name);
bool find_include(Filename &filename, bool angle_quotes, CPPFile::Source &source);
CPPToken get_quoted_char(int c);
CPPToken get_quoted_string(int c);
@ -153,6 +155,7 @@ private:
void extract_manifest_args(const string &name, int num_args,
int va_arg, vector_string &args);
void expand_defined_function(string &expr, size_t q, size_t &p);
void expand_has_include_function(string &expr, size_t q, size_t &p, YYLTYPE loc);
void expand_manifest_inline(string &expr, size_t q, size_t &p,
const CPPManifest *manifest);
void extract_manifest_args_inline(const string &name, int num_args,