Hi,

A couple of days ago i was asking:
does any frontend support some sort of noting that a user-header was
included but no decl of it was ever referenced (i.e. superfluous
#include) ?

I could not find an appropriate tool to diagnose them, so i was thinking
about a gcc plugin to do that.

Would some plugin like the attached be suitable to put into svn if
cleaned up a bit?
If so, where would they live?

Observations while thinking about all this:
- libcpp does not record LC_PSEUDO_ENTER (or something like that),
  so pristine libcpp cannot detect duplicate includes, like iostream
  in attached sample input.cc
- should figure out how to print a help-text for the plugin
  Hints?
- should handle structs.

Thanks in advance for hints on the help-text printing or comments about
the overall idea of such a facility.
/* 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);
}

/* Callback function to invoke after GCC finishes parsing a struct.  */

void
handle_struct (void *event_data, void *user_data)
{
  tree type = (tree) event_data;
  if (type == error_mark_node)
	  return;
  warning (0, G_("Process struct %s"),
           IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))));
}

static void
fn_add_includes (tree fndecl, const bool mainfile)
{
  tree decl, type;
  const struct line_map *map;

  map = linemap_lookup (line_table, DECL_SOURCE_LOCATION (fndecl));
  if (!MAIN_FILE_P (map))
    {
      source_location loc = DECL_SOURCE_LOCATION (fndecl);
      info (0,"fn %s: %s", lang_hooks.decl_printable_name (fndecl, 3), LOCATION_FILE (DECL_SOURCE_LOCATION (fndecl)));
      add_include (&referenced, LOCATION_FILE (loc), loc, mainfile);
    }
  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 loc = 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 (loc),
	      loc,
	      false);
	}
    }
}

static void debug_location (location_t l)
{
  fprintf (stderr, "LOCATION_FILE=%s\n", LOCATION_FILE (l));
}

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, MAIN_FILE_P (map));
      if (!MAIN_FILE_P (map))
	{
	  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_pre_generic (void *event_data, void *user_data)
{
  tree fndecl = (tree) event_data, decl;
  const struct line_map *map;

  map = linemap_lookup (line_table, DECL_SOURCE_LOCATION (fndecl));
  if (MAIN_FILE_P (map))
    {
      location_t decl_loc;

      decl = DECL_SAVED_TREE (fndecl);
      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);

      if (TREE_CODE (decl) == STATEMENT_LIST)
	{
	  tree_stmt_iterator i;
	  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 /* gcc_unreachable (); but i'm a coward.. think asm-only, perhaps? */
	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_pre_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
#include <algorithm>
#include <iostream>
#include <vector>
#include <iostream>
int main() {std::cout << "foo"<<std::endl;return 0;}
GCC_SRC_DIR ?= /scratch/src/gcc-4.6.orig
GCC_OBJ_DIR ?= /scratch/obj.$(shell uname -m)/gcc-4.6.orig
CC = gcc-4.6.orig-HEAD

SRC_INCS := gcc libcpp/include include
OBJ_INCS := gcc
CPPFLAGS += $(patsubst %,-I$(GCC_SRC_DIR)/%,$(SRC_INCS)) \
                $(patsubst %,-I$(GCC_OBJ_DIR)/%,$(OBJ_INCS))
CFLAGS += -fPIC

warn_include.so: warn_include.c
        $(LINK.c) -shared -o $@ $^

.PHONY: clean
clean:
        rm -f *.o *.so

help:
        @echo gcc-4.6.orig-HEAD -fplugin=./warn_include.so 
-fplugin-arg-warn_include-missing -fplugin-arg-warn_include-surplus=0 -c -o 
/dev/null input.cc

Reply via email to