c/c++: Add fix-it hints for suggested missing #includes
authorDavid Malcolm <dmalcolm@redhat.com>
Fri, 14 Jul 2017 15:09:00 +0000 (15:09 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Fri, 14 Jul 2017 15:09:00 +0000 (15:09 +0000)
gcc/c-family/ChangeLog:
* c-common.c (try_to_locate_new_include_insertion_point): New
function.
(per_file_includes_t): New typedef.
(added_includes_t): New typedef.
(added_includes): New variable.
(maybe_add_include_fixit): New function.
* c-common.h (maybe_add_include_fixit): New decl.

gcc/c/ChangeLog:
* c-decl.c (implicitly_declare): When suggesting a missing
#include, provide a fix-it hint.

gcc/cp/ChangeLog:
* name-lookup.c (get_std_name_hint): Add '<' and '>' around
the header names.
(maybe_suggest_missing_header): Update for addition of '<' and '>'
to above.  Provide a fix-it hint.
* pt.c: Include "gcc-rich-location.h"
(listify): Attempt to add fix-it hint for missing
#include <initializer_list>.
* rtti.c: Include "gcc-rich-location.h".
(typeid_ok_p): Attempt to add fix-it hint for missing
#include <typeinfo>.

gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/missing-initializer_list-include.C: New test case.
* g++.dg/lookup/missing-std-include-2.C: New test case.
* g++.dg/lookup/missing-std-include-3.C: New test case.
* g++.dg/rtti/missing-typeinfo-include.C: New test case.
* gcc.dg/missing-header-fixit-1.c: New test case.
* gcc.dg/missing-header-fixit-2.c: New test case.
* gcc.dg/missing-header-fixit-2.h: New header.

From-SVN: r250203

17 files changed:
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/c/ChangeLog
gcc/c/c-decl.c
gcc/cp/ChangeLog
gcc/cp/name-lookup.c
gcc/cp/pt.c
gcc/cp/rtti.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/missing-initializer_list-include.C [new file with mode: 0644]
gcc/testsuite/g++.dg/lookup/missing-std-include-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/lookup/missing-std-include-3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/rtti/missing-typeinfo-include.C [new file with mode: 0644]
gcc/testsuite/gcc.dg/missing-header-fixit-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/missing-header-fixit-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/missing-header-fixit-2.h [new file with mode: 0644]

index b21db816b4da0766a5b585669a7573bfecd7ccc2..cea8fc022238ee3f52c5ab83174ada6ee0d5cd77 100644 (file)
@@ -1,3 +1,13 @@
+2017-07-14  David Malcolm  <dmalcolm@redhat.com>
+
+       * c-common.c (try_to_locate_new_include_insertion_point): New
+       function.
+       (per_file_includes_t): New typedef.
+       (added_includes_t): New typedef.
+       (added_includes): New variable.
+       (maybe_add_include_fixit): New function.
+       * c-common.h (maybe_add_include_fixit): New decl.
+
 2017-07-10  Martin Sebor  <msebor@redhat.com>
 
        PR other/81345
index b4217f3a2cca4e99a8bbbaf30dcfa364eb78cd34..feb0904bcbf49a552cb43c99d6b1579e06fef19f 100644 (file)
@@ -8019,4 +8019,135 @@ c_family_tests (void)
 
 #endif /* #if CHECKING_P */
 
+/* Attempt to locate a suitable location within FILE for a
+   #include directive to be inserted before.  FILE should
+   be a string from libcpp (pointer equality is used).
+   LOC is the location of the relevant diagnostic.
+
+   Attempt to return the location within FILE immediately
+   after the last #include within that file, or the start of
+   that file if it has no #include directives.
+
+   Return UNKNOWN_LOCATION if no suitable location is found,
+   or if an error occurs.  */
+
+static location_t
+try_to_locate_new_include_insertion_point (const char *file, location_t loc)
+{
+  /* Locate the last ordinary map within FILE that ended with a #include.  */
+  const line_map_ordinary *last_include_ord_map = NULL;
+
+  /* ...and the next ordinary map within FILE after that one.  */
+  const line_map_ordinary *last_ord_map_after_include = NULL;
+
+  /* ...and the first ordinary map within FILE.  */
+  const line_map_ordinary *first_ord_map_in_file = NULL;
+
+  /*  Get ordinary map containing LOC (or its expansion).  */
+  const line_map_ordinary *ord_map_for_loc = NULL;
+  loc = linemap_resolve_location (line_table, loc, LRK_MACRO_EXPANSION_POINT,
+                                 &ord_map_for_loc);
+  gcc_assert (ord_map_for_loc);
+
+  for (unsigned int i = 0; i < LINEMAPS_ORDINARY_USED (line_table); i++)
+    {
+      const line_map_ordinary *ord_map
+       = LINEMAPS_ORDINARY_MAP_AT (line_table, i);
+
+      const line_map_ordinary *from = INCLUDED_FROM (line_table, ord_map);
+      if (from)
+       if (from->to_file == file)
+         {
+           last_include_ord_map = from;
+           last_ord_map_after_include = NULL;
+         }
+
+      if (ord_map->to_file == file)
+       {
+         if (!first_ord_map_in_file)
+           first_ord_map_in_file = ord_map;
+         if (last_include_ord_map && !last_ord_map_after_include)
+           last_ord_map_after_include = ord_map;
+       }
+
+      /* Stop searching when reaching the ord_map containing LOC,
+        as it makes no sense to provide fix-it hints that appear
+        after the diagnostic in question.  */
+      if (ord_map == ord_map_for_loc)
+       break;
+    }
+
+  /* Determine where to insert the #include.  */
+  const line_map_ordinary *ord_map_for_insertion;
+
+  /* We want the next ordmap in the file after the last one that's a
+     #include, but failing that, the start of the file.  */
+  if (last_ord_map_after_include)
+    ord_map_for_insertion = last_ord_map_after_include;
+  else
+    ord_map_for_insertion = first_ord_map_in_file;
+
+  if (!ord_map_for_insertion)
+    return UNKNOWN_LOCATION;
+
+  /* The "start_location" is column 0, meaning "the whole line".
+     rich_location and edit_context can't cope with this, so use
+     column 1 instead.  */
+  location_t col_0 = ord_map_for_insertion->start_location;
+  return linemap_position_for_loc_and_offset (line_table, col_0, 1);
+}
+
+/* A map from filenames to sets of headers added to them, for
+   ensuring idempotency within maybe_add_include_fixit.  */
+
+/* The values within the map.  We need string comparison as there's
+   no guarantee that two different diagnostics that are recommending
+   adding e.g. "<stdio.h>" are using the same buffer.  */
+
+typedef hash_set <const char *, nofree_string_hash> per_file_includes_t;
+
+/* The map itself.  We don't need string comparison for the filename keys,
+   as they come from libcpp.  */
+
+typedef hash_map <const char *, per_file_includes_t *> added_includes_t;
+static added_includes_t *added_includes;
+
+/* Attempt to add a fix-it hint to RICHLOC, adding "#include HEADER\n"
+   in a suitable location within the file of RICHLOC's primary
+   location.
+
+   This function is idempotent: a header will be added at most once to
+   any given file.  */
+
+void
+maybe_add_include_fixit (rich_location *richloc, const char *header)
+{
+  location_t loc = richloc->get_loc ();
+  const char *file = LOCATION_FILE (loc);
+  if (!file)
+    return;
+
+  /* Idempotency: don't add the same header more than once to a given file.  */
+  if (!added_includes)
+    added_includes = new added_includes_t ();
+  per_file_includes_t *&set = added_includes->get_or_insert (file);
+  if (set)
+    if (set->contains (header))
+      /* ...then we've already added HEADER to that file.  */
+      return;
+  if (!set)
+    set = new per_file_includes_t ();
+  set->add (header);
+
+  /* Attempt to locate a suitable place for the new directive.  */
+  location_t include_insert_loc
+    = try_to_locate_new_include_insertion_point (file, loc);
+  if (include_insert_loc == UNKNOWN_LOCATION)
+    return;
+
+  char *text = xasprintf ("#include %s\n", header);
+  richloc->add_fixit_insert_before (include_insert_loc, text);
+  free (text);
+}
+
 #include "gt-c-family-c-common.h"
index 7e7efb2916eafd43314fa98b95996348596e24ff..a29f1ade25d66741367d32fd3a2c0031a55352e9 100644 (file)
@@ -1556,6 +1556,8 @@ excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method);
 extern int c_flt_eval_method (bool ts18661_p);
 extern void add_no_sanitize_value (tree node, unsigned int flags);
 
+extern void maybe_add_include_fixit (rich_location *, const char *);
+
 #if CHECKING_P
 namespace selftest {
   /* Declarations for specific families of tests within c-family,
index aba08689638ebfe1dc1d7fd5c0a4949f12ee00ce..42ef9a5dfb65d64af58c8692af3a3516f532c079 100644 (file)
@@ -1,3 +1,8 @@
+2017-07-14  David Malcolm  <dmalcolm@redhat.com>
+
+       * c-decl.c (implicitly_declare): When suggesting a missing
+       #include, provide a fix-it hint.
+
 2017-07-06  David Malcolm  <dmalcolm@redhat.com>
 
        * c-lang.c (selftest::run_c_tests): Move body to c_family_tests,
index 317d5cdd099d0c31a5970e7ecd8845f4d48fa49e..50da185e363938a597cca621bbe72ff906b86687 100644 (file)
@@ -3386,8 +3386,14 @@ implicitly_declare (location_t loc, tree functionid)
                  const char *header
                    = header_for_builtin_fn (DECL_FUNCTION_CODE (decl));
                  if (header != NULL && warned)
-                   inform (loc, "include %qs or provide a declaration of %qD",
-                           header, decl);
+                   {
+                     rich_location richloc (line_table, loc);
+                     maybe_add_include_fixit (&richloc, header);
+                     inform_at_rich_loc
+                       (&richloc,
+                        "include %qs or provide a declaration of %qD",
+                        header, decl);
+                   }
                  newtype = TREE_TYPE (decl);
                }
            }
index 02214c9953b9a71d90452d5518734da95fb23e85..715ac76c8c4e73c3f479d557f9b891e09aee6705 100644 (file)
@@ -1,3 +1,16 @@
+2017-07-14  David Malcolm  <dmalcolm@redhat.com>
+
+       * name-lookup.c (get_std_name_hint): Add '<' and '>' around
+       the header names.
+       (maybe_suggest_missing_header): Update for addition of '<' and '>'
+       to above.  Provide a fix-it hint.
+       * pt.c: Include "gcc-rich-location.h"
+       (listify): Attempt to add fix-it hint for missing
+       #include <initializer_list>.
+       * rtti.c: Include "gcc-rich-location.h".
+       (typeid_ok_p): Attempt to add fix-it hint for missing
+       #include <typeinfo>.
+
 2017-07-12  Jason Merrill  <jason@redhat.com>
 
        P0512R0 - Deduction from an initializer list.
index fcf06e0cf5b847efcdeb13855145fe6f79163331..cd7428a4ea4cb663bdef97d1116ccb7d706aa975 100644 (file)
@@ -4760,7 +4760,7 @@ suggest_alternatives_for (location_t location, tree name,
 /* Subroutine of maybe_suggest_missing_header for handling unrecognized names
    for some of the most common names within "std::".
    Given non-NULL NAME, a name for lookup within "std::", return the header
-   name defining it within the C++ Standard Library (without '<' and '>'),
+   name defining it within the C++ Standard Library (with '<' and '>'),
    or NULL.  */
 
 static const char *
@@ -4773,61 +4773,61 @@ get_std_name_hint (const char *name)
   };
   static const std_name_hint hints[] = {
     /* <array>.  */
-    {"array", "array"}, // C++11
+    {"array", "<array>"}, // C++11
     /* <deque>.  */
-    {"deque", "deque"},
+    {"deque", "<deque>"},
     /* <forward_list>.  */
-    {"forward_list", "forward_list"},  // C++11
+    {"forward_list", "<forward_list>"},  // C++11
     /* <fstream>.  */
-    {"basic_filebuf", "fstream"},
-    {"basic_ifstream", "fstream"},
-    {"basic_ofstream", "fstream"},
-    {"basic_fstream", "fstream"},
+    {"basic_filebuf", "<fstream>"},
+    {"basic_ifstream", "<fstream>"},
+    {"basic_ofstream", "<fstream>"},
+    {"basic_fstream", "<fstream>"},
     /* <iostream>.  */
-    {"cin", "iostream"},
-    {"cout", "iostream"},
-    {"cerr", "iostream"},
-    {"clog", "iostream"},
-    {"wcin", "iostream"},
-    {"wcout", "iostream"},
-    {"wclog", "iostream"},
+    {"cin", "<iostream>"},
+    {"cout", "<iostream>"},
+    {"cerr", "<iostream>"},
+    {"clog", "<iostream>"},
+    {"wcin", "<iostream>"},
+    {"wcout", "<iostream>"},
+    {"wclog", "<iostream>"},
     /* <list>.  */
-    {"list", "list"},
+    {"list", "<list>"},
     /* <map>.  */
-    {"map", "map"},
-    {"multimap", "map"},
+    {"map", "<map>"},
+    {"multimap", "<map>"},
     /* <queue>.  */
-    {"queue", "queue"},
-    {"priority_queue", "queue"},
+    {"queue", "<queue>"},
+    {"priority_queue", "<queue>"},
     /* <ostream>.  */
-    {"ostream", "ostream"},
-    {"wostream", "ostream"},
-    {"ends", "ostream"},
-    {"flush", "ostream"},
-    {"endl", "ostream"},
+    {"ostream", "<ostream>"},
+    {"wostream", "<ostream>"},
+    {"ends", "<ostream>"},
+    {"flush", "<ostream>"},
+    {"endl", "<ostream>"},
     /* <set>.  */
-    {"set", "set"},
-    {"multiset", "set"},
+    {"set", "<set>"},
+    {"multiset", "<set>"},
     /* <sstream>.  */
-    {"basic_stringbuf", "sstream"},
-    {"basic_istringstream", "sstream"},
-    {"basic_ostringstream", "sstream"},
-    {"basic_stringstream", "sstream"},
+    {"basic_stringbuf", "<sstream>"},
+    {"basic_istringstream", "<sstream>"},
+    {"basic_ostringstream", "<sstream>"},
+    {"basic_stringstream", "<sstream>"},
     /* <stack>.  */
-    {"stack", "stack"},
+    {"stack", "<stack>"},
     /* <string>.  */
-    {"string", "string"},
-    {"wstring", "string"},
-    {"u16string", "string"},
-    {"u32string", "string"},
+    {"string", "<string>"},
+    {"wstring", "<string>"},
+    {"u16string", "<string>"},
+    {"u32string", "<string>"},
     /* <unordered_map>.  */
-    {"unordered_map", "unordered_map"}, // C++11
-    {"unordered_multimap", "unordered_map"}, // C++11
+    {"unordered_map", "<unordered_map>"}, // C++11
+    {"unordered_multimap", "<unordered_map>"}, // C++11
     /* <unordered_set>.  */
-    {"unordered_set", "unordered_set"}, // C++11
-    {"unordered_multiset", "unordered_set"}, // C++11
+    {"unordered_set", "<unordered_set>"}, // C++11
+    {"unordered_multiset", "<unordered_set>"}, // C++11
     /* <vector>.  */
-    {"vector", "vector"},
+    {"vector", "<vector>"},
   };
   const size_t num_hints = sizeof (hints) / sizeof (hints[0]);
   for (size_t i = 0; i < num_hints; i++)
@@ -4858,10 +4858,14 @@ maybe_suggest_missing_header (location_t location, tree name, tree scope)
   const char *name_str = IDENTIFIER_POINTER (name);
   const char *header_hint = get_std_name_hint (name_str);
   if (header_hint)
-    inform (location,
-           "%<std::%s%> is defined in header %<<%s>%>;"
-           " did you forget to %<#include <%s>%>?",
-           name_str, header_hint, header_hint);
+    {
+      gcc_rich_location richloc (location);
+      maybe_add_include_fixit (&richloc, header_hint);
+      inform_at_rich_loc (&richloc,
+                         "%<std::%s%> is defined in header %qs;"
+                         " did you forget to %<#include %s%>?",
+                         name_str, header_hint, header_hint);
+    }
 }
 
 /* Look for alternatives for NAME, an IDENTIFIER_NODE for which name
index 0df6854f6481bca0e0177b51ce8e9662e2c399f5..da133bd186346f0ea5bc4ffce87733368b5c3334 100644 (file)
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-iterator.h"
 #include "type-utils.h"
 #include "gimplify.h"
+#include "gcc-rich-location.h"
 
 /* The type of functions taking a tree, and some additional data, and
    returning an int.  */
@@ -24867,8 +24868,12 @@ listify (tree arg)
 
   if (!std_init_list || !DECL_CLASS_TEMPLATE_P (std_init_list))
     {    
-      error ("deducing from brace-enclosed initializer list requires "
-            "#include <initializer_list>");
+      gcc_rich_location richloc (input_location);
+      maybe_add_include_fixit (&richloc, "<initializer_list>");
+      error_at_rich_loc (&richloc,
+                         "deducing from brace-enclosed initializer list"
+                         " requires #include <initializer_list>");
+
       return error_mark_node;
     }
   tree argvec = make_tree_vec (1);
index d73b1bcfa973de1b2d42206005eb1e5b74623dce..a660cdd9413b47e9932545fd68a7f580e7b4b4d5 100644 (file)
@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "stor-layout.h"
 #include "c-family/c-pragma.h"
+#include "gcc-rich-location.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -316,7 +317,12 @@ typeid_ok_p (void)
 
   if (!COMPLETE_TYPE_P (const_type_info_type_node))
     {
-      error ("must %<#include <typeinfo>%> before using %<typeid%>");
+      gcc_rich_location richloc (input_location);
+      maybe_add_include_fixit (&richloc, "<typeinfo>");
+      error_at_rich_loc (&richloc,
+                        "must %<#include <typeinfo>%> before using"
+                        " %<typeid%>");
+
       return false;
     }
 
index 41dca744045e015bd16e78ceb675c672dc4f11b8..4bdeeca32135491e85ae48230755869bf2ee7436 100644 (file)
@@ -1,3 +1,13 @@
+2017-07-14  David Malcolm  <dmalcolm@redhat.com>
+
+       * g++.dg/cpp0x/missing-initializer_list-include.C: New test case.
+       * g++.dg/lookup/missing-std-include-2.C: New test case.
+       * g++.dg/lookup/missing-std-include-3.C: New test case.
+       * g++.dg/rtti/missing-typeinfo-include.C: New test case.
+       * gcc.dg/missing-header-fixit-1.c: New test case.
+       * gcc.dg/missing-header-fixit-2.c: New test case.
+       * gcc.dg/missing-header-fixit-2.h: New header.
+
 2017-07-13  David Malcolm  <dmalcolm@redhat.com>
 
        PR c/81405
diff --git a/gcc/testsuite/g++.dg/cpp0x/missing-initializer_list-include.C b/gcc/testsuite/g++.dg/cpp0x/missing-initializer_list-include.C
new file mode 100644 (file)
index 0000000..8e803c8
--- /dev/null
@@ -0,0 +1,28 @@
+/* This is padding (to avoid the generated patch containing DejaGnu
+   directives).  */
+
+/* { dg-options "-fdiagnostics-generate-patch" } */
+
+// { dg-do compile { target c++11 } }
+
+void test (int i)
+{
+  auto a = { &i }; // { dg-error "deducing from brace-enclosed initializer list requires #include <initializer_list>" }
+}
+
+/* Verify the output from -fdiagnostics-generate-patch.
+   We expect the patch to begin with a header, containing this
+   source filename, via an absolute path.
+   Given the path, we can only capture it via regexps.  */
+/* { dg-regexp "\\-\\-\\- .*" } */
+/* { dg-regexp "\\+\\+\\+ .*" } */
+/* Use #if 0/#endif rather than comments, to allow the text to contain
+   a comment.  */
+#if 0
+{ dg-begin-multiline-output "" }
+@@ -1,3 +1,4 @@
++#include <initializer_list>
+ /* This is padding (to avoid the generated patch containing DejaGnu
+    directives).  */
+{ dg-end-multiline-output "" }
+#endif
diff --git a/gcc/testsuite/g++.dg/lookup/missing-std-include-2.C b/gcc/testsuite/g++.dg/lookup/missing-std-include-2.C
new file mode 100644 (file)
index 0000000..ae918f8
--- /dev/null
@@ -0,0 +1,55 @@
+/* Example of fix-it hints that add #include directives,
+   adding them after a pre-existing #include.  */
+
+/* { dg-options "-fdiagnostics-generate-patch" } */
+
+/* This is padding (to avoid the generated patch containing DejaGnu
+   directives).  */
+
+#include <stdio.h>
+
+void test (void)
+{
+  std::string s ("hello world"); // { dg-error ".string. is not a member of .std." }
+  // { dg-message ".std::string. is defined in header .<string>.; did you forget to .#include <string>.?" "" { target *-*-* } .-1 }
+
+  std::cout << 10; // { dg-error ".cout. is not a member of .std." }
+  // { dg-message ".std::cout. is defined in header .<iostream>.; did you forget to .#include <iostream>.?" "" { target *-*-* } .-1 }
+}
+
+/* Same again, to test idempotency of the added "#include" fix-it.  */
+
+void test_2 (void)
+{
+  std::string s ("hello again"); // { dg-error ".string. is not a member of .std." }
+  // { dg-message ".std::string. is defined in header .<string>.; did you forget to .#include <string>.?" "" { target *-*-* } .-1 }
+
+  std::cout << 10; // { dg-error ".cout. is not a member of .std." }
+  // { dg-message ".std::cout. is defined in header .<iostream>.; did you forget to .#include <iostream>.?" "" { target *-*-* } .-1 }
+}
+
+/* Verify the output from -fdiagnostics-generate-patch.
+   We expect the patch to begin with a header, containing this
+   source filename, via an absolute path.
+   Given the path, we can only capture it via regexps.  */
+/* { dg-regexp "\\-\\-\\- .*" } */
+/* { dg-regexp "\\+\\+\\+ .*" } */
+
+/* Verify the hunks within the patch.
+   Use #if 0/#endif rather than comments, to allow the text to contain
+   a comment.
+   We expect a "#include <string>" and "#include <iostream>" to each have been
+   added once, immediately below the last #include.  */
+#if 0
+{ dg-begin-multiline-output "" }
+@@ -7,6 +7,8 @@
+    directives).  */
+ #include <stdio.h>
++#include <string>
++#include <iostream>
+ void test (void)
+ {
+{ dg-end-multiline-output "" }
+#endif
diff --git a/gcc/testsuite/g++.dg/lookup/missing-std-include-3.C b/gcc/testsuite/g++.dg/lookup/missing-std-include-3.C
new file mode 100644 (file)
index 0000000..23f868d
--- /dev/null
@@ -0,0 +1,35 @@
+/* Example of where the error occurs before the first #include,
+   which in this case happens to be the missing header. 
+   For this case, expect to insert the #include at the top of the file. */
+
+/* { dg-options "-fdiagnostics-generate-patch" } */
+
+void test ()
+{
+  std::string test; // { dg-error ".string. is not a member of .std." }
+  // { dg-message ".std::string. is defined in header .<string>.; did you forget to .#include <string>.?" "" { target *-*-* } .-1 }
+}
+
+#include <string>
+
+/* Verify the output from -fdiagnostics-generate-patch.
+   We expect the patch to begin with a header, containing this
+   source filename, via an absolute path.
+   Given the path, we can only capture it via regexps.  */
+/* { dg-regexp "\\-\\-\\- .*" } */
+/* { dg-regexp "\\+\\+\\+ .*" } */
+
+/* Verify the hunks within the patch.
+   Use #if 0/#endif rather than comments, to allow the text to contain
+   a comment.
+   We expect a "#include <string>" have been added once, at the top
+   of the file.  */
+#if 0
+{ dg-begin-multiline-output "" }
+@@ -1,3 +1,4 @@
++#include <string>
+ /* Example of where the error occurs before the first #include,
+    which in this case happens to be the missing header. 
+    For this case, expect to insert the #include at the top of the file. */
+{ dg-end-multiline-output "" }
+#endif
diff --git a/gcc/testsuite/g++.dg/rtti/missing-typeinfo-include.C b/gcc/testsuite/g++.dg/rtti/missing-typeinfo-include.C
new file mode 100644 (file)
index 0000000..937c38f
--- /dev/null
@@ -0,0 +1,27 @@
+/* This is padding (to avoid the generated patch containing DejaGnu
+   directives).  */
+
+/* { dg-options "-fdiagnostics-generate-patch" } */
+
+void test()
+{
+  typeid(void); // { dg-error "must '#include <typeinfo>' before using 'typeid'" }
+}
+
+/* Verify the output from -fdiagnostics-generate-patch.
+   We expect the patch to begin with a header, containing this
+   source filename, via an absolute path.
+   Given the path, we can only capture it via regexps.  */
+/* { dg-regexp "\\-\\-\\- .*" } */
+/* { dg-regexp "\\+\\+\\+ .*" } */
+/* Use #if 0/#endif rather than comments, to allow the text to contain
+   a comment.  */
+#if 0
+{ dg-begin-multiline-output "" }
+@@ -1,3 +1,4 @@
++#include <typeinfo>
+ /* This is padding (to avoid the generated patch containing DejaGnu
+    directives).  */
+{ dg-end-multiline-output "" }
+#endif
diff --git a/gcc/testsuite/gcc.dg/missing-header-fixit-1.c b/gcc/testsuite/gcc.dg/missing-header-fixit-1.c
new file mode 100644 (file)
index 0000000..2b28357
--- /dev/null
@@ -0,0 +1,36 @@
+/* Example of a fix-it hint that adds a #include directive,
+   adding them to the top of the file, given that there is no
+   pre-existing #include.  */
+
+/* This is padding (to avoid the generated patch containing DejaGnu
+   directives).  */
+
+/* { dg-options "-fdiagnostics-generate-patch" } */
+
+void test (int i, int j)
+{
+  printf ("%i of %i\n", i, j); /* { dg-warning "implicit declaration" } */
+  /* { dg-message "include '<stdio.h>' or provide a declaration of 'printf'" "" { target *-*-* } .-1 } */
+}
+
+/* Verify the output from -fdiagnostics-generate-patch.
+   We expect the patch to begin with a header, containing this
+   source filename, via an absolute path.
+   Given the path, we can only capture it via regexps.  */
+/* { dg-regexp "\\-\\-\\- .*" } */
+/* { dg-regexp "\\+\\+\\+ .*" } */
+/* Use #if 0/#endif rather than comments, to allow the text to contain
+   a comment.  */
+#if 0
+{ dg-begin-multiline-output "" }
+@@ -1,3 +1,4 @@
++#include <stdio.h>
+ /* Example of a fix-it hint that adds a #include directive,
+    adding them to the top of the file, given that there is no
+    pre-existing #include.  */
+{ dg-end-multiline-output "" }
+#endif
+
+/* FIXME: should we attempt to skip leading comments when determining the
+   insertion location?
+   Similarly, should we attempt to be within single-inclusion guards, etc?  */
diff --git a/gcc/testsuite/gcc.dg/missing-header-fixit-2.c b/gcc/testsuite/gcc.dg/missing-header-fixit-2.c
new file mode 100644 (file)
index 0000000..5d5f874
--- /dev/null
@@ -0,0 +1,31 @@
+/* Verify that when we suggest adding #include directives that they
+   are added to the affected file.  */
+
+/* The following header file is missing a "#include <stdio.h>".  */
+
+#include "missing-header-fixit-2.h"
+
+/* These directives actually apply to the header.  */
+/* { dg-warning "implicit declaration of function 'printf'" "" { target *-*-* } 6 } */
+/* { dg-warning "incompatible implicit declaration of built-in function 'printf'" "" { target *-*-* } 6 } */
+
+/* { dg-options "-fdiagnostics-generate-patch" } */
+
+/* Verify the output from -fdiagnostics-generate-patch.
+   We expect the patch to begin with a header, containing the
+   filename of the header, via an absolute path.
+   Given the path, we can only capture it via regexps.  */
+/* { dg-regexp "\\-\\-\\- .*" } */
+/* { dg-regexp "\\+\\+\\+ .*" } */
+/* Use #if 0/#endif rather than comments, to allow the text to contain
+   a comment.
+   We expect the *header* to have been patched, adding the missing include.  */
+#if 0
+{ dg-begin-multiline-output "" }
+@@ -1,3 +1,4 @@
++#include <stdio.h>
+ /* This is missing-header-fixit-2.h, for use by
+    missing-header-fixit-2.c  */
+{ dg-end-multiline-output "" }
+#endif
diff --git a/gcc/testsuite/gcc.dg/missing-header-fixit-2.h b/gcc/testsuite/gcc.dg/missing-header-fixit-2.h
new file mode 100644 (file)
index 0000000..c0bf55d
--- /dev/null
@@ -0,0 +1,7 @@
+/* This is missing-header-fixit-2.h, for use by
+   missing-header-fixit-2.c  */
+
+void test (int i, int j)
+{
+  printf ("%i of %i\n", i, j);
+}