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
This commit is contained in:
David Malcolm 2017-10-13 12:42:39 +00:00 committed by David Malcolm
parent 3b0dd4fea2
commit 15f7a46926
9 changed files with 152 additions and 2 deletions

View 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->

View 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);

View 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;
}

View 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"

View 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 */

View 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.

View file

@ -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" }
}

View 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 }

View file

@ -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 }
}
}
}
}