diff options
author | David Malcolm <dmalcolm@redhat.com> | 2017-10-13 12:42:39 +0000 |
---|---|---|
committer | David Malcolm <dmalcolm@gcc.gnu.org> | 2017-10-13 12:42:39 +0000 |
commit | 15f7a4692667ce38eed325a9567f5ef492a5ce07 (patch) | |
tree | 7b2e27753270c83805872cbc3270007b040ce3bb | |
parent | 3b0dd4fea23a695df1fb9a7e5c7017f9d1f1ea84 (diff) | |
download | gcc-15f7a4692667ce38eed325a9567f5ef492a5ce07.zip gcc-15f7a4692667ce38eed325a9567f5ef492a5ce07.tar.gz gcc-15f7a4692667ce38eed325a9567f5ef492a5ce07.tar.bz2 |
C++: show location of unclosed extern "C" specifications
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
-rw-r--r-- | gcc/cp/ChangeLog | 18 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/decl.c | 1 | ||||
-rw-r--r-- | gcc/cp/parser.c | 39 | ||||
-rw-r--r-- | gcc/cp/parser.h | 4 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 7 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp0x/udlit-extern-c.C | 7 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C | 11 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/template/extern-c.C | 66 |
9 files changed, 152 insertions, 2 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 3abf794..c225835 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -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-> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index dc98dd8..b74b6d9 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index f251a90..a3cc80c 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -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; } diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 810e2b7..2337be5 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -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" diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index 0994e1e..f4f4a01 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -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 */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 90a7f72..6bb877e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -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 index 0000000..d47a49c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/udlit-extern-c.C @@ -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" } + +} diff --git a/gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C b/gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C index fda3532..44f538e 100644 --- a/gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C +++ b/gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C @@ -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 index 0000000..c0dd7cb --- /dev/null +++ b/gcc/testsuite/g++.dg/template/extern-c.C @@ -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 } + } + } + } +} |