C++: show location of unclosed extern "C" specifications
authorDavid Malcolm <dmalcolm@redhat.com>
Fri, 13 Oct 2017 12:42:39 +0000 (12:42 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Fri, 13 Oct 2017 12:42:39 +0000 (12:42 +0000)
If the user fails to close an extern "C" linkage specifier, and then
uses templates, they will run into "template with C linkage" errors.

From personal experience, it can be hard to tell where the
extern "C" began.  As of r251026 there will be a message highlighting
the unclosed '{', but this may be hard to spot at the very end of
the errors.

This patch adds a note to the various diagnostics that complain
about C linkage, showing the user where the extern "C" specification
began.

gcc/cp/ChangeLog:
* cp-tree.h (maybe_show_extern_c_location): New decl.
* decl.c (grokfndecl): When complaining about literal operators
with C linkage, issue a note giving the location of the
extern "C".
* parser.c (cp_parser_new): Initialize new field
"innermost_linkage_specification_location".
(cp_parser_linkage_specification): Store the location
of the linkage specification within the cp_parser.
(cp_parser_explicit_specialization): When complaining about
template specializations with C linkage, issue a note giving the
location of the extern "C".
(cp_parser_explicit_template_declaration): Likewise for templates.
(maybe_show_extern_c_location): New function.
* parser.h (struct cp_parser): New field
"innermost_linkage_specification_location".

gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/udlit-extern-c.C: New test case.
* g++.dg/diagnostic/unclosed-extern-c.C: Add example of a template
erroneously covered by an unclosed extern "C".
* g++.dg/template/extern-c.C: New test case.

From-SVN: r253726

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/parser.c
gcc/cp/parser.h
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/udlit-extern-c.C [new file with mode: 0644]
gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C
gcc/testsuite/g++.dg/template/extern-c.C [new file with mode: 0644]

index 3abf79440cc6f26c85d5c1a3fd9e64f384f52321..c22583554e540f9fe5e00de72c6d072a6d7831d5 100644 (file)
@@ -1,3 +1,21 @@
+2017-10-13  David Malcolm  <dmalcolm@redhat.com>
+
+       * cp-tree.h (maybe_show_extern_c_location): New decl.
+       * decl.c (grokfndecl): When complaining about literal operators
+       with C linkage, issue a note giving the location of the
+       extern "C".
+       * parser.c (cp_parser_new): Initialize new field
+       "innermost_linkage_specification_location".
+       (cp_parser_linkage_specification): Store the location
+       of the linkage specification within the cp_parser.
+       (cp_parser_explicit_specialization): When complaining about
+       template specializations with C linkage, issue a note giving the
+       location of the extern "C".
+       (cp_parser_explicit_template_declaration): Likewise for templates.
+       (maybe_show_extern_c_location): New function.
+       * parser.h (struct cp_parser): New field
+       "innermost_linkage_specification_location".
+
 2017-10-12  Nathan Sidwell  <nathan@acm.org>
 
        * cp-tree.h (cp_expr): Add const operator * and operator->
index dc98dd881c52028f4e017f5779f9b07549fb4e98..b74b6d9d9503f2026ab81846fccc4e9b709405a1 100644 (file)
@@ -6356,6 +6356,7 @@ extern bool parsing_nsdmi (void);
 extern bool parsing_default_capturing_generic_lambda_in_template (void);
 extern void inject_this_parameter (tree, cp_cv_quals);
 extern location_t defarg_location (tree);
+extern void maybe_show_extern_c_location (void);
 
 /* in pt.c */
 extern bool check_template_shadow              (tree);
index f251a9043d46ef5cdb99a4650d613f897e608576..a3cc80cf7a30f5e95e5cde876a114131da3d6511 100644 (file)
@@ -8729,6 +8729,7 @@ grokfndecl (tree ctype,
       if (DECL_LANGUAGE (decl) == lang_c)
        {
          error ("literal operator with C linkage");
+         maybe_show_extern_c_location ();
          return NULL_TREE;
        }
 
index 810e2b7f72e124941c5534e840d71281ad5164b8..2337be52c382bdfdfedd9f51c38fb3a777b76c2a 100644 (file)
@@ -3937,6 +3937,9 @@ cp_parser_new (void)
   /* Allow constrained-type-specifiers. */
   parser->prevent_constrained_type_specifiers = 0;
 
+  /* We haven't yet seen an 'extern "C"'.  */
+  parser->innermost_linkage_specification_location = UNKNOWN_LOCATION;
+
   return parser;
 }
 
@@ -13848,9 +13851,11 @@ cp_parser_linkage_specification (cp_parser* parser)
   tree linkage;
 
   /* Look for the `extern' keyword.  */
-  cp_parser_require_keyword (parser, RID_EXTERN, RT_EXTERN);
+  cp_token *extern_token
+    = cp_parser_require_keyword (parser, RID_EXTERN, RT_EXTERN);
 
   /* Look for the string-literal.  */
+  cp_token *string_token = cp_lexer_peek_token (parser->lexer);
   linkage = cp_parser_string_literal (parser, false, false);
 
   /* Transform the literal into an identifier.  If the literal is a
@@ -13869,6 +13874,20 @@ cp_parser_linkage_specification (cp_parser* parser)
   /* We're now using the new linkage.  */
   push_lang_context (linkage);
 
+  /* Preserve the location of the the innermost linkage specification,
+     tracking the locations of nested specifications via a local.  */
+  location_t saved_location
+    = parser->innermost_linkage_specification_location;
+  /* Construct a location ranging from the start of the "extern" to
+     the end of the string-literal, with the caret at the start, e.g.:
+       extern "C" {
+       ^~~~~~~~~~
+  */
+  parser->innermost_linkage_specification_location
+    = make_location (extern_token->location,
+                    extern_token->location,
+                    get_finish (string_token->location));
+
   /* If the next token is a `{', then we're using the first
      production.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
@@ -13899,6 +13918,9 @@ cp_parser_linkage_specification (cp_parser* parser)
 
   /* We're done with the linkage-specification.  */
   pop_lang_context ();
+
+  /* Restore location of parent linkage specification, if any.  */
+  parser->innermost_linkage_specification_location = saved_location;
 }
 
 /* Parse a static_assert-declaration.
@@ -16643,6 +16665,7 @@ cp_parser_explicit_specialization (cp_parser* parser)
   if (current_lang_name == lang_name_c)
     {
       error_at (token->location, "template specialization with C linkage");
+      maybe_show_extern_c_location ();
       /* Give it C++ linkage to avoid confusing other parts of the
         front end.  */
       push_lang_context (lang_name_cplusplus);
@@ -26979,6 +27002,7 @@ cp_parser_explicit_template_declaration (cp_parser* parser, bool member_p)
   if (current_lang_name == lang_name_c)
     {
       error_at (location, "template with C linkage");
+      maybe_show_extern_c_location ();
       /* Give it C++ linkage to avoid confusing other parts of the
          front end.  */
       push_lang_context (lang_name_cplusplus);
@@ -39552,4 +39576,17 @@ finish_fully_implicit_template (cp_parser *parser, tree member_decl_opt)
   return member_decl_opt;
 }
 
+/* Helper function for diagnostics that have complained about things
+   being used with 'extern "C"' linkage.
+
+   Attempt to issue a note showing where the 'extern "C"' linkage began.  */
+
+void
+maybe_show_extern_c_location (void)
+{
+  if (the_parser->innermost_linkage_specification_location != UNKNOWN_LOCATION)
+    inform (the_parser->innermost_linkage_specification_location,
+           "%<extern \"C\"%> linkage started here");
+}
+
 #include "gt-cp-parser.h"
index 0994e1e7f4fc88150d61118190cfe54ace9180fe..f4f4a010964df477edec1591d26d09ebe2962329 100644 (file)
@@ -412,6 +412,10 @@ struct GTY(()) cp_parser {
      context e.g., because they could never be deduced.  */
   int prevent_constrained_type_specifiers;
 
+  /* Location of the string-literal token within the current linkage
+     specification, if any, or UNKNOWN_LOCATION otherwise.  */
+  location_t innermost_linkage_specification_location;
+
 };
 
 /* In parser.c  */
index 90a7f722111aa4e0028846bfc2138d1b078eb49a..6bb877ea3873ecb376020fc6c6d1eb4aae7ef3ee 100644 (file)
@@ -1,3 +1,10 @@
+2017-10-13  David Malcolm  <dmalcolm@redhat.com>
+
+       * g++.dg/cpp0x/udlit-extern-c.C: New test case.
+       * g++.dg/diagnostic/unclosed-extern-c.C: Add example of a template
+       erroneously covered by an unclosed extern "C".
+       * g++.dg/template/extern-c.C: New test case.
+
 2017-10-13  Richard Biener  <rguenther@suse.de>
 
        * gcc.dg/graphite/pr35356-3.c: XFAIL again.
diff --git a/gcc/testsuite/g++.dg/cpp0x/udlit-extern-c.C b/gcc/testsuite/g++.dg/cpp0x/udlit-extern-c.C
new file mode 100644 (file)
index 0000000..d47a49c
--- /dev/null
@@ -0,0 +1,7 @@
+// { dg-do compile { target c++11 } }
+
+extern "C" { // { dg-message "1: 'extern .C.' linkage started here" }
+
+constexpr double operator"" _deg ( double degrees ); // { dg-error "literal operator with C linkage" }
+
+}
index fda3532266d049b3b5cad9e69ed4c6aea5545a7b..44f538e33ecb069126718b4911031370572a4d67 100644 (file)
@@ -1,3 +1,12 @@
-extern "C" { /* { dg-message "12: to match this '.'" } */
+extern "C" { // { dg-line open_extern_c }
+
+  int foo (void);
+
+/* Missing close-brace for the extern "C" here.  */
+
+template <typename T> // { dg-error "template with C linkage" }
+void bar (void);
+// { dg-message "1: 'extern .C.' linkage started here" "" { target *-*-* } open_extern_c }
 
 void test (void); /* { dg-error "17: expected '.' at end of input" } */
+// { message "12: to match this '.'" "" { target *-*-* } open_extern_c }
diff --git a/gcc/testsuite/g++.dg/template/extern-c.C b/gcc/testsuite/g++.dg/template/extern-c.C
new file mode 100644 (file)
index 0000000..c0dd7cb
--- /dev/null
@@ -0,0 +1,66 @@
+template <typename T> void specializable (T);
+
+/* Invalid template: within "extern C".  */
+
+extern "C" { // { dg-message "1: 'extern .C.' linkage started here" }
+
+template <typename T> // { dg-error "template with C linkage" }
+void within_extern_c_braces (void);
+
+}
+
+/* Valid template: not within "extern C".  */
+
+template <typename T>
+void not_within_extern_c (void);
+
+
+/* Invalid specialization: within "extern C".  */
+
+extern "C" { // { dg-message "1: 'extern .C.' linkage started here" }
+
+template <>  // { dg-error "template specialization with C linkage" }
+void specializable (int);
+
+}
+
+
+/* Valid specialization: not within "extern C".  */
+template <>
+void specializable (char);
+
+
+/* Example of extern C without braces.  */
+
+extern "C" template <typename T> // { dg-line open_extern_c_no_braces }
+void within_extern_c_no_braces (void);
+// { dg-error "12: template with C linkage" "" { target *-*-* } open_extern_c_no_braces }
+// { dg-message "1: 'extern .C.' linkage started here" "" { target *-*-* } open_extern_c_no_braces }
+
+
+/* Nested extern "C" specifications.
+   We should report within the innermost extern "C" that's still open.  */
+
+extern "C" {
+  extern "C" { // { dg-line middle_open_extern_c }
+    extern "C" {
+    }
+
+    template <typename T>  // { dg-error "template with C linkage" }
+    void within_nested_extern_c (void);
+    // { dg-message "3: 'extern .C.' linkage started here" "" { target *-*-* } middle_open_extern_c }
+
+    extern "C++" {
+      /* Valid template: within extern "C++".  */
+      template <typename T>
+      void within_nested_extern_cpp (void);
+
+      extern "C" {  // { dg-line last_open_extern_c }
+       /* Invalid template: within "extern C".  */
+       template <typename T> // { dg-error "template with C linkage" }
+       void within_extern_c_within_extern_cpp (void);
+       // { dg-message "7: 'extern .C.' linkage started here" "" { target *-*-* } last_open_extern_c } 
+      }
+    }
+  }
+}