blob: a99d9e9c5cac4ee2b81d41984123c5dc83cd9f71 [file] [log] [blame]
/* Preprocess only, using cpplib.
Copyright (C) 1995-2022 Free Software Foundation, Inc.
Written by Per Bothner, 1994-95.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "c-common.h" /* For flags. */
#include "../libcpp/internal.h"
#include "langhooks.h"
#include "c-pragma.h" /* For parse_in. */
#include "file-prefix-map.h" /* remap_macro_filename() */
class token_streamer;
/* Encapsulates state used to convert a stream of tokens into a text
file. */
static struct
{
FILE *outf; /* Stream to write to. */
const cpp_token *prev; /* Previous token. */
const cpp_token *source; /* Source token for spacing. */
unsigned src_line; /* Line number currently being written. */
bool printed; /* True if something output at line. */
bool first_time; /* pp_file_change hasn't been called yet. */
bool prev_was_system_token; /* True if the previous token was a
system token.*/
const char *src_file; /* Current source file. */
token_streamer *streamer; /* Instance of class token_streamer using this
object. */
} print;
/* Defined and undefined macros being queued for output with -dU at
the next newline. */
struct macro_queue
{
struct macro_queue *next; /* Next macro in the list. */
char *macro; /* The name of the macro if not
defined, the full definition if
defined. */
};
static macro_queue *define_queue, *undef_queue;
/* General output routines. */
static void scan_translation_unit (cpp_reader *);
static void scan_translation_unit_directives_only (cpp_reader *);
static void scan_translation_unit_trad (cpp_reader *);
static void account_for_newlines (const unsigned char *, size_t);
static int dump_macro (cpp_reader *, cpp_hashnode *, void *);
static void dump_queued_macros (cpp_reader *);
static bool print_line_1 (location_t, const char*, FILE *);
static bool print_line (location_t, const char *);
static bool maybe_print_line_1 (location_t, FILE *);
static bool maybe_print_line (location_t);
static bool do_line_change (cpp_reader *, const cpp_token *,
location_t, int);
/* Callback routines for the parser. Most of these are active only
in specific modes. */
static void cb_line_change (cpp_reader *, const cpp_token *, int);
static void cb_define (cpp_reader *, location_t, cpp_hashnode *);
static void cb_undef (cpp_reader *, location_t, cpp_hashnode *);
static void cb_used_define (cpp_reader *, location_t, cpp_hashnode *);
static void cb_used_undef (cpp_reader *, location_t, cpp_hashnode *);
static void cb_include (cpp_reader *, location_t, const unsigned char *,
const char *, int, const cpp_token **);
static void cb_ident (cpp_reader *, location_t, const cpp_string *);
static void cb_def_pragma (cpp_reader *, location_t);
static void cb_read_pch (cpp_reader *pfile, const char *name,
int fd, const char *orig_name);
/* Preprocess and output. */
void
preprocess_file (cpp_reader *pfile)
{
/* A successful cpp_read_main_file guarantees that we can call
cpp_scan_nooutput or cpp_get_token next. */
if (flag_no_output && pfile->buffer)
{
/* Scan -included buffers, then the main file. */
while (pfile->buffer->prev)
cpp_scan_nooutput (pfile);
cpp_scan_nooutput (pfile);
}
else if (cpp_get_options (pfile)->traditional)
scan_translation_unit_trad (pfile);
else if (cpp_get_options (pfile)->directives_only
&& !cpp_get_options (pfile)->preprocessed)
scan_translation_unit_directives_only (pfile);
else
scan_translation_unit (pfile);
/* -dM command line option. Should this be elsewhere? */
if (flag_dump_macros == 'M')
cpp_forall_identifiers (pfile, dump_macro, NULL);
/* Flush any pending output. */
if (print.printed)
putc ('\n', print.outf);
}
/* Don't emit #pragma or #ident directives if we are processing
assembly language; the assembler may choke on them. */
static bool
should_output_pragmas ()
{
return cpp_get_options (parse_in)->lang != CLK_ASM;
}
/* Set up the callbacks as appropriate. */
void
init_pp_output (FILE *out_stream)
{
cpp_callbacks *cb = cpp_get_callbacks (parse_in);
if (!flag_no_output)
{
cb->line_change = cb_line_change;
if (should_output_pragmas ())
{
cb->ident = cb_ident;
cb->def_pragma = cb_def_pragma;
}
}
if (flag_dump_includes)
cb->include = cb_include;
if (flag_pch_preprocess)
{
cb->valid_pch = c_common_valid_pch;
cb->read_pch = cb_read_pch;
}
if (flag_dump_macros == 'N' || flag_dump_macros == 'D')
{
cb->define = cb_define;
cb->undef = cb_undef;
}
if (flag_dump_macros == 'U')
{
cb->before_define = dump_queued_macros;
cb->used_define = cb_used_define;
cb->used_undef = cb_used_undef;
}
cb->has_attribute = c_common_has_attribute;
cb->has_builtin = c_common_has_builtin;
cb->get_source_date_epoch = cb_get_source_date_epoch;
cb->remap_filename = remap_macro_filename;
/* Initialize the print structure. */
print.src_line = 1;
print.printed = false;
print.prev = 0;
print.outf = out_stream;
print.first_time = 1;
print.src_file = "";
print.prev_was_system_token = false;
print.streamer = nullptr;
}
// FIXME: Ideally we'd just turn the entirety of the print struct into
// an encapsulated streamer ...
class token_streamer
{
bool avoid_paste;
bool do_line_adjustments;
bool in_pragma;
public:
token_streamer (cpp_reader *pfile)
:avoid_paste (false),
do_line_adjustments (cpp_get_options (pfile)->lang != CLK_ASM
&& !flag_no_line_commands),
in_pragma (false)
{
gcc_assert (!print.streamer);
print.streamer = this;
}
void begin_pragma ()
{
in_pragma = true;
}
void stream (cpp_reader *pfile, const cpp_token *tok, location_t);
};
void
token_streamer::stream (cpp_reader *pfile, const cpp_token *token,
location_t loc)
{
/* Keep input_location up to date, since it is needed for processing early
pragmas such as #pragma GCC diagnostic. */
input_location = loc;
if (token->type == CPP_PADDING)
{
avoid_paste = true;
if (print.source == NULL
|| (!(print.source->flags & PREV_WHITE)
&& token->val.source == NULL))
print.source = token->val.source;
return;
}
if (token->type == CPP_EOF)
return;
/* Keep track when we move into and out of system locations. */
const bool is_system_token = in_system_header_at (loc);
const bool system_state_changed
= (is_system_token != print.prev_was_system_token);
print.prev_was_system_token = is_system_token;
/* Subtle logic to output a space if and only if necessary. */
bool line_marker_emitted = false;
if (avoid_paste)
{
unsigned src_line = LOCATION_LINE (loc);
if (print.source == NULL)
print.source = token;
if (src_line != print.src_line
&& do_line_adjustments
&& !in_pragma)
{
line_marker_emitted = do_line_change (pfile, token, loc, false);
putc (' ', print.outf);
print.printed = true;
}
else if (print.source->flags & PREV_WHITE
|| (print.prev
&& cpp_avoid_paste (pfile, print.prev, token))
|| (print.prev == NULL && token->type == CPP_HASH))
{
putc (' ', print.outf);
print.printed = true;
}
}
else if (token->flags & PREV_WHITE && token->type != CPP_PRAGMA)
{
unsigned src_line = LOCATION_LINE (loc);
if (src_line != print.src_line
&& do_line_adjustments
&& !in_pragma)
line_marker_emitted = do_line_change (pfile, token, loc, false);
putc (' ', print.outf);
print.printed = true;
}
avoid_paste = false;
print.source = NULL;
print.prev = token;
if (token->type == CPP_PRAGMA)
{
in_pragma = true;
if (should_output_pragmas ())
{
const char *space;
const char *name;
line_marker_emitted = maybe_print_line (token->src_loc);
fputs ("#pragma ", print.outf);
c_pp_lookup_pragma (token->val.pragma, &space, &name);
if (space)
fprintf (print.outf, "%s %s", space, name);
else
fprintf (print.outf, "%s", name);
print.printed = true;
}
if (token->val.pragma >= PRAGMA_FIRST_EXTERNAL)
c_pp_invoke_early_pragma_handler (token->val.pragma);
}
else if (token->type == CPP_PRAGMA_EOL)
{
if (should_output_pragmas ())
maybe_print_line (UNKNOWN_LOCATION);
in_pragma = false;
}
else
{
if (cpp_get_options (parse_in)->debug)
linemap_dump_location (line_table, token->src_loc, print.outf);
if (do_line_adjustments
&& !in_pragma
&& !line_marker_emitted
&& system_state_changed
&& !is_location_from_builtin_token (loc))
/* The system-ness of this token is different from the one of
the previous token. Let's emit a line change to mark the
new system-ness before we emit the token. */
{
line_marker_emitted = do_line_change (pfile, token, loc, false);
}
if (!in_pragma || should_output_pragmas ())
{
cpp_output_token (token, print.outf);
print.printed = true;
}
}
/* CPP_COMMENT tokens and raw-string literal tokens can have
embedded new-line characters. Rather than enumerating all the
possible token types just check if token uses val.str union
member. */
if (cpp_token_val_index (token) == CPP_TOKEN_FLD_STR)
account_for_newlines (token->val.str.text, token->val.str.len);
}
/* Writes out the preprocessed file, handling spacing and paste
avoidance issues. */
static void
scan_translation_unit (cpp_reader *pfile)
{
token_streamer streamer (pfile);
uintptr_t filter = 0;
if (lang_hooks.preprocess_token)
filter = lang_hooks.preprocess_token (pfile, NULL, filter);
print.source = NULL;
for (;;)
{
location_t spelling_loc;
const cpp_token *token
= cpp_get_token_with_location (pfile, &spelling_loc);
streamer.stream (pfile, token, spelling_loc);
if (filter)
{
unsigned flags = lang_hooks.preprocess_token (pfile, token, filter);
if (flags & lang_hooks::PT_begin_pragma)
streamer.begin_pragma ();
}
if (token->type == CPP_EOF)
break;
}
if (filter)
lang_hooks.preprocess_token (pfile, NULL, filter);
}
class do_streamer : public token_streamer
{
public:
uintptr_t filter;
do_streamer (cpp_reader *pfile, uintptr_t filter)
:token_streamer (pfile), filter (filter)
{
}
};
static void
directives_only_cb (cpp_reader *pfile, CPP_DO_task task, void *data_, ...)
{
va_list args;
va_start (args, data_);
do_streamer *streamer = reinterpret_cast <do_streamer *> (data_);
switch (task)
{
default:
gcc_unreachable ();
case CPP_DO_print:
{
print.src_line += va_arg (args, unsigned);
const void *buf = va_arg (args, const void *);
size_t size = va_arg (args, size_t);
fwrite (buf, 1, size, print.outf);
}
break;
case CPP_DO_location:
maybe_print_line (va_arg (args, location_t));
break;
case CPP_DO_token:
{
const cpp_token *token = va_arg (args, const cpp_token *);
location_t spelling_loc = va_arg (args, location_t);
streamer->stream (pfile, token, spelling_loc);
if (streamer->filter)
{
unsigned flags = lang_hooks.preprocess_token
(pfile, token, streamer->filter);
if (flags & lang_hooks::PT_begin_pragma)
streamer->begin_pragma ();
}
}
break;
}
va_end (args);
}
/* Writes out the preprocessed file, handling spacing and paste
avoidance issues. */
static void
scan_translation_unit_directives_only (cpp_reader *pfile)
{
uintptr_t filter = 0;
if (lang_hooks.preprocess_token)
filter = lang_hooks.preprocess_token (pfile, NULL, filter);
do_streamer streamer (pfile, filter);
cpp_directive_only_process (pfile, &streamer, directives_only_cb);
if (streamer.filter)
lang_hooks.preprocess_token (pfile, NULL, streamer.filter);
}
/* Adjust print.src_line for newlines embedded in output. */
static void
account_for_newlines (const unsigned char *str, size_t len)
{
while (len--)
if (*str++ == '\n')
print.src_line++;
}
/* Writes out a traditionally preprocessed file. */
static void
scan_translation_unit_trad (cpp_reader *pfile)
{
while (_cpp_read_logical_line_trad (pfile))
{
size_t len = pfile->out.cur - pfile->out.base;
maybe_print_line (pfile->out.first_line);
fwrite (pfile->out.base, 1, len, print.outf);
print.printed = true;
if (!CPP_OPTION (pfile, discard_comments))
account_for_newlines (pfile->out.base, len);
}
}
/* If the token read on logical line LINE needs to be output on a
different line to the current one, output the required newlines or
a line marker. If a line marker was emitted, return TRUE otherwise
return FALSE. */
static bool
maybe_print_line_1 (location_t src_loc, FILE *stream)
{
bool emitted_line_marker = false;
unsigned src_line = LOCATION_LINE (src_loc);
const char *src_file = LOCATION_FILE (src_loc);
/* End the previous line of text. */
if (print.printed)
{
putc ('\n', stream);
print.src_line++;
print.printed = false;
}
if (!flag_no_line_commands
&& src_line >= print.src_line
&& src_line < print.src_line + 8
&& src_loc != UNKNOWN_LOCATION
&& strcmp (src_file, print.src_file) == 0)
{
while (src_line > print.src_line)
{
putc ('\n', stream);
print.src_line++;
}
}
else
emitted_line_marker = print_line_1 (src_loc, "", stream);
return emitted_line_marker;
}
/* If the token read on logical line LINE needs to be output on a
different line to the current one, output the required newlines or
a line marker. If a line marker was emitted, return TRUE otherwise
return FALSE. */
static bool
maybe_print_line (location_t src_loc)
{
if (cpp_get_options (parse_in)->debug)
linemap_dump_location (line_table, src_loc,
print.outf);
return maybe_print_line_1 (src_loc, print.outf);
}
/* Output a line marker for logical line LINE. Special flags are "1"
or "2" indicating entering or leaving a file. If the line marker
was effectively emitted, return TRUE otherwise return FALSE. */
static bool
print_line_1 (location_t src_loc, const char *special_flags, FILE *stream)
{
bool emitted_line_marker = false;
/* End any previous line of text. */
if (print.printed)
putc ('\n', stream);
print.printed = false;
if (src_loc != UNKNOWN_LOCATION && !flag_no_line_commands)
{
const char *file_path = LOCATION_FILE (src_loc);
size_t to_file_len = strlen (file_path);
unsigned char *to_file_quoted =
(unsigned char *) alloca (to_file_len * 4 + 1);
/* cpp_quote_string does not nul-terminate, so we have to do it
ourselves. */
unsigned char *p = cpp_quote_string (to_file_quoted,
(const unsigned char *) file_path,
to_file_len);
*p = '\0';
print.src_line = LOCATION_LINE (src_loc);
print.src_file = file_path;
fprintf (stream, "# %u \"%s\"%s",
print.src_line, to_file_quoted, special_flags);
int sysp = in_system_header_at (src_loc);
if (sysp == 2)
fputs (" 3 4", stream);
else if (sysp == 1)
fputs (" 3", stream);
putc ('\n', stream);
emitted_line_marker = true;
}
return emitted_line_marker;
}
/* Output a line marker for logical line LINE. Special flags are "1"
or "2" indicating entering or leaving a file. Return TRUE if a
line marker was effectively emitted, FALSE otherwise. */
static bool
print_line (location_t src_loc, const char *special_flags)
{
if (cpp_get_options (parse_in)->debug)
linemap_dump_location (line_table, src_loc,
print.outf);
return print_line_1 (src_loc, special_flags, print.outf);
}
/* Helper function for cb_line_change and scan_translation_unit.
Return TRUE if a line marker is emitted, FALSE otherwise. */
static bool
do_line_change (cpp_reader *pfile, const cpp_token *token,
location_t src_loc, int parsing_args)
{
bool emitted_line_marker = false;
if (define_queue || undef_queue)
dump_queued_macros (pfile);
if (token->type == CPP_EOF || parsing_args)
return false;
emitted_line_marker = maybe_print_line (src_loc);
print.prev = 0;
print.source = 0;
/* Supply enough spaces to put this token in its original column,
one space per column greater than 2, since scan_translation_unit
will provide a space if PREV_WHITE. Don't bother trying to
reconstruct tabs; we can't get it right in general, and nothing
ought to care. Some things do care; the fault lies with them.
Also do not output the spaces if this is a CPP_PRAGMA token. In this
case, libcpp has provided the location of the first token after #pragma,
so we would start at the wrong column. */
if (!CPP_OPTION (pfile, traditional) && token->type != CPP_PRAGMA)
{
int spaces = LOCATION_COLUMN (src_loc) - 2;
print.printed = true;
while (-- spaces >= 0)
putc (' ', print.outf);
}
return emitted_line_marker;
}
/* Called when a line of output is started. TOKEN is the first token
of the line, and at end of file will be CPP_EOF. */
static void
cb_line_change (cpp_reader *pfile, const cpp_token *token,
int parsing_args)
{
do_line_change (pfile, token, token->src_loc, parsing_args);
}
static void
cb_ident (cpp_reader *pfile ATTRIBUTE_UNUSED, location_t line,
const cpp_string *str)
{
maybe_print_line (line);
fprintf (print.outf, "#ident %s\n", str->text);
print.src_line++;
}
static void
cb_define (cpp_reader *pfile, location_t line, cpp_hashnode *node)
{
const line_map_ordinary *map;
maybe_print_line (line);
fputs ("#define ", print.outf);
/* 'D' is whole definition; 'N' is name only. */
if (flag_dump_macros == 'D')
fputs ((const char *) cpp_macro_definition (pfile, node),
print.outf);
else
fputs ((const char *) NODE_NAME (node), print.outf);
putc ('\n', print.outf);
print.printed = false;
linemap_resolve_location (line_table, line,
LRK_MACRO_DEFINITION_LOCATION,
&map);
print.src_line++;
}
static void
cb_undef (cpp_reader *pfile, location_t line, cpp_hashnode *node)
{
if (lang_hooks.preprocess_undef)
lang_hooks.preprocess_undef (pfile, line, node);
maybe_print_line (line);
fprintf (print.outf, "#undef %s\n", NODE_NAME (node));
print.src_line++;
}
static void
cb_used_define (cpp_reader *pfile, location_t line ATTRIBUTE_UNUSED,
cpp_hashnode *node)
{
if (cpp_user_macro_p (node))
{
macro_queue *q;
q = XNEW (macro_queue);
q->macro = xstrdup ((const char *) cpp_macro_definition (pfile, node));
q->next = define_queue;
define_queue = q;
}
}
static void
cb_used_undef (cpp_reader *pfile ATTRIBUTE_UNUSED,
location_t line ATTRIBUTE_UNUSED,
cpp_hashnode *node)
{
macro_queue *q;
q = XNEW (macro_queue);
q->macro = xstrdup ((const char *) NODE_NAME (node));
q->next = undef_queue;
undef_queue = q;
}
static void
dump_queued_macros (cpp_reader *pfile ATTRIBUTE_UNUSED)
{
macro_queue *q;
/* End the previous line of text. */
if (print.printed)
{
putc ('\n', print.outf);
print.src_line++;
print.printed = false;
}
for (q = define_queue; q;)
{
macro_queue *oq;
fputs ("#define ", print.outf);
fputs (q->macro, print.outf);
putc ('\n', print.outf);
print.printed = false;
print.src_line++;
oq = q;
q = q->next;
free (oq->macro);
free (oq);
}
define_queue = NULL;
for (q = undef_queue; q;)
{
macro_queue *oq;
fprintf (print.outf, "#undef %s\n", q->macro);
print.src_line++;
oq = q;
q = q->next;
free (oq->macro);
free (oq);
}
undef_queue = NULL;
}
static void
cb_include (cpp_reader *pfile ATTRIBUTE_UNUSED, location_t line,
const unsigned char *dir, const char *header, int angle_brackets,
const cpp_token **comments)
{
maybe_print_line (line);
if (angle_brackets)
fprintf (print.outf, "#%s <%s>", dir, header);
else
fprintf (print.outf, "#%s \"%s\"", dir, header);
if (comments != NULL)
{
while (*comments != NULL)
{
if ((*comments)->flags & PREV_WHITE)
putc (' ', print.outf);
cpp_output_token (*comments, print.outf);
++comments;
}
}
putc ('\n', print.outf);
print.printed = false;
print.src_line++;
}
/* Callback called when -fworking-director and -E to emit working
directory in cpp output file. */
void
pp_dir_change (cpp_reader *pfile ATTRIBUTE_UNUSED, const char *dir)
{
size_t to_file_len = strlen (dir);
unsigned char *to_file_quoted =
(unsigned char *) alloca (to_file_len * 4 + 1);
unsigned char *p;
/* cpp_quote_string does not nul-terminate, so we have to do it ourselves. */
p = cpp_quote_string (to_file_quoted, (const unsigned char *) dir, to_file_len);
*p = '\0';
fprintf (print.outf, "# 1 \"%s//\"\n", to_file_quoted);
}
/* The file name, line number or system header flags have changed, as
described in MAP. */
void
pp_file_change (const line_map_ordinary *map)
{
const char *flags = "";
if (flag_no_line_commands)
return;
if (map != NULL)
{
input_location = map->start_location;
if (print.first_time)
{
/* Avoid printing foo.i when the main file is foo.c. */
if (!cpp_get_options (parse_in)->preprocessed)
print_line (map->start_location, flags);
print.first_time = 0;
}
else
{
/* Bring current file to correct line when entering a new file. */
if (map->reason == LC_ENTER)
{
maybe_print_line (linemap_included_from (map));
flags = " 1";
}
else if (map->reason == LC_LEAVE)
flags = " 2";
print_line (map->start_location, flags);
}
}
}
/* Copy a #pragma directive to the preprocessed output. */
static void
cb_def_pragma (cpp_reader *pfile, location_t line)
{
maybe_print_line (line);
fputs ("#pragma ", print.outf);
cpp_output_line (pfile, print.outf);
print.printed = false;
print.src_line++;
}
/* Stream a token as if we had seen it directly ourselves; needed
in case a token was lexed externally, e.g. while processing a
pragma. */
void
c_pp_stream_token (cpp_reader *pfile, const cpp_token *tok, location_t loc)
{
gcc_assert (print.streamer);
print.streamer->stream (pfile, tok, loc);
}
/* Dump out the hash table. */
static int
dump_macro (cpp_reader *pfile, cpp_hashnode *node, void *v ATTRIBUTE_UNUSED)
{
if (cpp_user_macro_p (node))
{
fputs ("#define ", print.outf);
fputs ((const char *) cpp_macro_definition (pfile, node),
print.outf);
putc ('\n', print.outf);
print.printed = false;
print.src_line++;
}
return 1;
}
/* Load in the PCH file NAME, open on FD. It was originally searched for
by ORIG_NAME. Also, print out a #include command so that the PCH
file can be loaded when the preprocessed output is compiled. */
static void
cb_read_pch (cpp_reader *pfile, const char *name,
int fd, const char *orig_name ATTRIBUTE_UNUSED)
{
c_common_read_pch (pfile, name, fd, orig_name);
fprintf (print.outf, "#pragma GCC pch_preprocess \"%s\"\n", name);
print.src_line++;
}