This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
v2: plugin to warn about surplus includes
- From: Bernhard Reutner-Fischer <rep dot dot dot nop at gmail dot com>
- To: gcc at gcc dot gnu dot org, gcc-patches at gcc dot gnu dot org
- Cc: tromey at redhat dot com
- Date: Fri, 21 May 2010 11:35:56 +0200
- Subject: v2: plugin to warn about surplus includes
- References: <20100518210546.GY14941@mx.loc>
On Tue, May 18, 2010 at 11:05:46PM +0200, Bernhard Reutner-Fischer wrote:
>Hi,
>Observations while thinking about all this:
[]
>- should figure out how to print a help-text for the plugin
> Hints?
>- should handle structs.
updated variant that handles functions a little bit better.
/* Dump superfluous, missing or duplicate includes.
*
* Copyright (C) 2010 Bernhard Reutner-Fischer
*/
/*
This file is part of GCC.
GCC 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.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "gcc-plugin.h"
#include "coretypes.h"
#include "tree.h"
#include "tree-iterator.h"
#include "tree-pass.h"
#include "intl.h"
#include "line-map.h"
#include "langhooks.h"
#include "target.h"
#include "toplev.h" /* warning_at */
/* #define info warning */
#define info(...) /**/
typedef struct include_t {
struct include_t *next;
const char *filename;
source_location location;
} include_t;
static include_t *incs = NULL, *referenced = NULL;
static bool warn_missing;
static bool warn_surplus;
/* Add filename to the end of the include list. */
static void __attribute__ ((__nonnull__ (2)))
add_include (include_t **head, const char *filename, const source_location loc,
const bool mainfile)
{
include_t *tail, *elt;
if (filename == NULL)
gcc_unreachable ();
tail = *head;
while (1)
{
if (tail && strcmp (tail->filename, filename) == 0)
{
const bool old_warn_system_headers = warn_system_headers;
warn_system_headers = 1; /* to please warning_at */
if (tail->location != loc && mainfile && warn_surplus)
warning_at (loc, 0, "Duplicate include %s", filename);
warn_system_headers = old_warn_system_headers;
return; /* don't enter duplicate */
}
if (tail && tail->next)
tail = tail->next;
else
break;
}
info (0, "ADDING %s", filename);
elt = XNEW (include_t);
elt->filename = filename;
elt->location = loc;
elt->next = NULL;
if (tail)
tail->next = elt;
else
*head = elt;
}
/* Remove filename from the include list. */
static void __attribute__ ((__nonnull__ (2)))
remove_include (include_t **head, const char *filename)
{
bool seen;
if (filename == NULL)
gcc_unreachable ();
seen = false;
while (*head)
{
if (strcmp ((*head)->filename, filename) == 0)
{
include_t *delete = *head;
info (0, "REMOVE %s", filename);
*head = (*head)->next;
XDELETE (delete);
seen = true;
break;
}
head = &(*head)->next;
}
if (!seen && warn_missing)
warning (0, "Missing include %s", filename);
}
static void debug_location (location_t l)
{
fprintf (stderr, "LOCATION_FILE=%s\n", LOCATION_FILE (l));
}
static void fn_add_includes (tree, const bool);
static void operands_add_includes (tree, location_t, const bool);
/* Callback function to invoke after GCC finishes parsing a struct. */
void
handle_struct (void *event_data, void *user_data)
{
const bool old_warn_system_headers = warn_system_headers;
tree type = (tree) event_data;
const struct line_map *map;
location_t loc;
if (type == error_mark_node)
return;
warn_system_headers = 1;
loc = DECL_NAME (TYPE_NAME (type))->decl_minimal.locus;
map = linemap_lookup (line_table, loc);
if (!MAIN_FILE_P (map))
{
info (0, G_("Process struct %s"),
IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))));
}
warn_system_headers = old_warn_system_headers;
}
static void
fn_add_includes (tree fndecl, const bool mainfile)
{
tree decl;
const struct line_map *map;
source_location loc = DECL_SOURCE_LOCATION (fndecl);
map = linemap_lookup (line_table, loc);
if (!MAIN_FILE_P (map))
{
info (0,"fn %s: %s", lang_hooks.decl_printable_name (fndecl, 3), LOCATION_FILE (loc));
add_include (&referenced, LOCATION_FILE (loc), loc, mainfile);
}
else
operands_add_includes (DECL_SAVED_TREE (fndecl), loc, true);
for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl))
{
map = linemap_lookup (line_table, DECL_SOURCE_LOCATION (decl));
if (!MAIN_FILE_P (map))
{
source_location aloc = DECL_SOURCE_LOCATION (decl);
info (0,"fn arg %s: %s", lang_hooks.decl_printable_name (decl, 3), LOCATION_FILE (DECL_SOURCE_LOCATION (decl)));
add_include (&referenced, LOCATION_FILE (aloc), aloc, false);
}
}
}
static void
operands_add_includes (tree decl, location_t decl_loc, const bool mainfile)
{
tree op;
const struct line_map *map;
location_t loc;
int i;
if (decl == NULL_TREE)
return;
for (i = tree_operand_length (decl); --i >= 0;)
{
op = TREE_OPERAND (decl, i);
if (op == NULL_TREE)
continue;
if (EXPR_HAS_LOCATION (op))
loc = EXPR_LOCATION (op);
else if (op->decl_minimal.locus != UNKNOWN_LOCATION)
/* cannot use DECL_SOURCE_LOCATION due to DECL_MINIMAL_CHECK */
loc = op->decl_minimal.locus;
else if (decl_loc != UNKNOWN_LOCATION)
loc = decl_loc;
else
loc = UNKNOWN_LOCATION;
map = linemap_lookup (line_table, loc);
if (TREE_CODE (op) == FUNCTION_DECL)
{
fn_add_includes (op, MAIN_FILE_P (map));
continue;
}
operands_add_includes (op, loc, mainfile);
if (!MAIN_FILE_P (map) && mainfile)
{
info (0,"operand decl %s: %s", lang_hooks.decl_printable_name (op, 3), LOCATION_FILE (loc));
add_include (&referenced, LOCATION_FILE (loc), loc, MAIN_FILE_P (map));
}
}
}
/* Callback function to invoke before the function body is genericized. */
void
handle_before_generic (void *event_data, void *user_data)
{
tree fndecl = (tree) event_data, decl;
const struct line_map *map;
if (DECL_ARTIFICIAL (fndecl))
return; /* not interrested in CTOR/DTOR et al */
info (0, "fndecl %s", lang_hooks.decl_printable_name (fndecl, 1));
if (strcmp(lang_hooks.decl_printable_name(fndecl,1),"__static_initialization_and_destruction_0")==0)
printf("FOO\n");
map = linemap_lookup (line_table, DECL_SOURCE_LOCATION (fndecl));
if (MAIN_FILE_P (map))
{
decl = DECL_SAVED_TREE (fndecl);
if (TREE_CODE (decl) == STATEMENT_LIST)
{
tree_stmt_iterator i;
location_t decl_loc;
if (EXPR_HAS_LOCATION (decl))
decl_loc = EXPR_LOCATION (decl);
else if (decl->decl_minimal.locus != UNKNOWN_LOCATION)
/* cannot use DECL_SOURCE_LOCATION due to DECL_MINIMAL_CHECK */
decl_loc = decl->decl_minimal.locus;
else
decl_loc = DECL_SOURCE_LOCATION (fndecl);
for (i = tsi_start (decl);
!tsi_end_p (i);
tsi_next (&i))
{
tree t = tsi_stmt (i);
location_t tloc;
if (EXPR_HAS_LOCATION (t))
tloc = EXPR_LOCATION (t);
else if (t->decl_minimal.locus != UNKNOWN_LOCATION)
/* cannot use DECL_SOURCE_LOCATION due to DECL_MINIMAL_CHECK */
tloc = t->decl_minimal.locus;
else if (decl_loc != UNKNOWN_LOCATION)
tloc = decl_loc;
else
tloc = UNKNOWN_LOCATION;
operands_add_includes (t, tloc,
MAIN_FILE_P (linemap_lookup (line_table, tloc)));
}
}
else
fn_add_includes (fndecl, MAIN_FILE_P (map));
}
}
/* Callback function to invoke after GCC finishes the compilation unit. */
void
handle_end_of_compilation_unit (void *event_data, void *user_data)
{
include_t *inc, *ref;
struct line_map *map = line_table->maps;
struct line_map *map0;
tree t = (tree) event_data;
const bool old_warn_system_headers = warn_system_headers;
while (map && map->column_bits)
{
/* Skip non-files and everything but new include files that were
* included by the main file. */
if (map->to_line != 0
&& !MAIN_FILE_P (map)
&& MAIN_FILE_P (INCLUDED_FROM (line_table, map))
)
{
map0 = INCLUDED_FROM (line_table, map);
#if 0
warning (0, "\"%s\", %s, %s", basename(map->to_file),
map->reason == LC_ENTER ? "ENTER" : map->reason == LC_LEAVE ? "LEAVE" : map->reason == LC_RENAME ? "RENAME" : "RENAME_VERBATIM",
map0->reason == LC_ENTER ? "ENTER" : map0->reason == LC_LEAVE ? "LEAVE" : map0->reason == LC_RENAME ? "RENAME" : "RENAME_VERBATIM");
#endif
if (map->reason == LC_ENTER)
add_include (&incs, map->to_file, map0->start_location, true);
}
map++;
}
/* Remove includes that contain referenced decls. */
ref = referenced;
while (ref)
{
remove_include (&incs, ref->filename);
ref = ref->next;
}
/* Warn about surplus headers. */
inc = incs;
warn_system_headers = 1; /* to please warning_at */
if (warn_surplus)
while (inc)
{
warning_at (inc->location, 0, G_("Unused include %s"), inc->filename);
inc = inc->next;
}
warn_system_headers = old_warn_system_headers;
}
void
handle_start_of_compilation_unit (void *event_data, void *user_data)
{
while (incs)
{
include_t *delete = incs;
incs = incs->next;
XDELETE (delete);
}
while (referenced)
{
include_t *delete = referenced;
referenced = referenced->next;
XDELETE (delete);
}
}
static unsigned int
execute_warn_includes_plugin (void)
{
return 0;
}
static bool
gate_warn_includes_plugin (void)
{
return true;
}
static struct gimple_opt_pass pass_warn_includes_plugin =
{
{
GIMPLE_PASS,
"warn_includes", /* name */
gate_warn_includes_plugin, /* gate */
execute_warn_includes_plugin, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
0, /* tv_id */
PROP_cfg, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_dump_func /* todo_flags_finish */
}
};
/* Initialization function that GCC calls. This plugin takes an argument
that specifies the name of the reference pass and an instance number,
both of which determine where the plugin pass should be inserted. */
int
plugin_init (struct plugin_name_args *plugin_info,
struct plugin_gcc_version *version)
{
struct register_pass_info pass_info;
const char *plugin_name = plugin_info->base_name;
int argc = plugin_info->argc;
struct plugin_argument *argv = plugin_info->argv;
char *ref_pass_name = "cfg";
int ref_instance_number = 0;
int i;
warn_missing = false;
warn_surplus = true;
/* Process the plugin arguments. This plugin takes the following arguments:
missing, [surplus|unused] */
for (i = 0; i < argc; ++i)
{
if (!strcmp (argv[i].key, "missing"))
{
if (argv[i].value)
warn_missing = !(atoi (argv[i].value) == 0);
else
warn_missing = true;
}
else if (!strcmp (argv[i].key, "surplus")
|| !strcmp (argv[i].key, "unused"))
{
if (argv[i].value)
warn_surplus = !(atoi (argv[i].value) == 0);
else
warn_surplus = true;
}
else
warning (0, G_("plugin %qs: unrecognized argument %qs ignored"),
plugin_name, argv[i].key);
}
pass_info.pass = &pass_warn_includes_plugin.pass;
pass_info.reference_pass_name = ref_pass_name;
pass_info.ref_pass_instance_number = ref_instance_number;
pass_info.pos_op = PASS_POS_INSERT_AFTER;
register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info);
register_callback (plugin_name, PLUGIN_START_UNIT, handle_start_of_compilation_unit, NULL);
// register_callback (plugin_name, PLUGIN_FINISH_TYPE, handle_struct, NULL);
register_callback (plugin_name, PLUGIN_PRE_GENERICIZE,
handle_before_generic, NULL);
register_callback (plugin_name, PLUGIN_FINISH_UNIT,
handle_end_of_compilation_unit, NULL);
return 0;
}
/* Announce that we are GPL. */
#if defined __GNUC__
extern __typeof (plugin_init) plugin_is_GPL_compatible __attribute__ ((alias ("plugin_init")));
#else
#warning you are doing something pretty odd.. good luck
unsigned plugin_is_GPL_compatible;
#endif