diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 3f8b72cdc00..0cf01c6dba4 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1582,6 +1582,10 @@ fdiagnostics-show-template-tree C++ ObjC++ Var(flag_diagnostics_show_template_tree) Init(0) Print hierarchical comparisons when template types are mismatched. +fdiagnostics-use-aliases +C++ Var(flag_diagnostics_use_aliases) Init(1) +Replace identifiers or scope names in diagnostics as defined by the diagnose_as attribute. + fdirectives-only C ObjC C++ ObjC++ Preprocess directives only. diff --git a/gcc/cp/error.c b/gcc/cp/error.c index c88d1749a0f..10b547afaa7 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see #include "internal-fn.h" #include "gcc-rich-location.h" #include "cp-name-hint.h" +#include "attribs.h" #define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',') #define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';') @@ -66,6 +67,7 @@ static void dump_alias_template_specialization (cxx_pretty_printer *, tree, int) static void dump_type (cxx_pretty_printer *, tree, int); static void dump_typename (cxx_pretty_printer *, tree, int); static void dump_simple_decl (cxx_pretty_printer *, tree, tree, int); +static void dump_decl_name_or_diagnose_as (cxx_pretty_printer *, tree, int); static void dump_decl (cxx_pretty_printer *, tree, int); static void dump_template_decl (cxx_pretty_printer *, tree, int); static void dump_function_decl (cxx_pretty_printer *, tree, int); @@ -231,7 +233,15 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags) { if (scope != global_namespace) { - dump_decl (pp, scope, f); + tree diagnose_as + = flag_diagnostics_use_aliases + ? lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (scope)) + : NULL_TREE; + if (diagnose_as) + pp_cxx_ws_string ( + pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (diagnose_as)))); + else + dump_decl (pp, scope, f); pp_cxx_colon_colon (pp); } } @@ -743,6 +753,7 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags) tree decl = TYPE_NAME (t); + tree diagnose_as = NULL_TREE; if (decl) { typdef = (!DECL_ARTIFICIAL (decl) @@ -769,8 +780,15 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags) if (! (flags & TFF_UNQUALIFIED_NAME)) dump_scope (pp, CP_DECL_CONTEXT (decl), flags | TFF_SCOPE); flags &= ~TFF_UNQUALIFIED_NAME; + + tree diagnose_as_specialized = NULL_TREE; if (tmplate) { + if (flag_diagnostics_use_aliases) + diagnose_as_specialized + = lookup_attribute ("diagnose_as", + TYPE_ATTRIBUTES (TREE_TYPE (decl))); + /* Because the template names are mangled, we have to locate the most general template, and use that name. */ tree tpl = TYPE_TI_TEMPLATE (t); @@ -779,9 +797,25 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags) tpl = DECL_TI_TEMPLATE (tpl); decl = tpl; } + + if (flag_diagnostics_use_aliases) + diagnose_as = lookup_attribute ("diagnose_as", + TYPE_ATTRIBUTES (TREE_TYPE (decl))); + if (diagnose_as_specialized + && (!diagnose_as || TREE_VALUE(diagnose_as_specialized) + != TREE_VALUE(diagnose_as))) + { + pp_cxx_ws_string ( + pp, TREE_STRING_POINTER ( + TREE_VALUE (TREE_VALUE (diagnose_as_specialized)))); + return; + } } - if (LAMBDA_TYPE_P (t)) + if (diagnose_as) + pp_cxx_ws_string (pp, TREE_STRING_POINTER ( + TREE_VALUE (TREE_VALUE (diagnose_as)))); + else if (LAMBDA_TYPE_P (t)) { /* A lambda's "type" is essentially its signature. */ pp_string (pp, M_(""); } else - dump_decl (pp, DECL_NAME (t), flags); + dump_decl_name_or_diagnose_as (pp, t, flags); } else if (DECL_DECOMPOSITION_P (t)) pp_string (pp, M_("")); @@ -1147,6 +1181,25 @@ dump_decl_name (cxx_pretty_printer *pp, tree t, int flags) pp_cxx_tree_identifier (pp, t); } +/* Print the DECL_NAME of DECL unless a different string was requested by the + diagnose_as attribute. */ + +static void +dump_decl_name_or_diagnose_as (cxx_pretty_printer *pp, tree decl, int flags) +{ + if (flag_diagnostics_use_aliases) + { + tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (decl)); + if (attr) + { + pp_cxx_ws_string ( + pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)))); + return; + } + } + dump_decl_name (pp, DECL_NAME (decl), flags); +} + /* Dump a human readable string for the decl T under control of FLAGS. */ static void @@ -1192,7 +1245,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags) || flags & TFF_CLASS_KEY_OR_ENUM)) { pp_cxx_ws_string (pp, "using"); - dump_decl (pp, DECL_NAME (t), flags); + dump_decl_name_or_diagnose_as(pp, t, flags); pp_cxx_whitespace (pp); pp_cxx_ws_string (pp, "="); pp_cxx_whitespace (pp); @@ -1374,7 +1427,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags) TREE_CODE (DECL_INITIAL (t)) == TEMPLATE_PARM_INDEX)) dump_simple_decl (pp, t, TREE_TYPE (t), flags); else if (DECL_NAME (t)) - dump_decl (pp, DECL_NAME (t), flags); + dump_decl_name_or_diagnose_as (pp, t, flags); else if (DECL_INITIAL (t)) dump_expr (pp, DECL_INITIAL (t), flags | TFF_EXPR_IN_PARENS); else @@ -1393,7 +1446,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags) } dump_type (pp, scope, flags); pp_cxx_colon_colon (pp); - dump_decl (pp, DECL_NAME (t), flags); + dump_decl_name_or_diagnose_as (pp, t, flags); if (variadic) pp_cxx_ws_string (pp, "..."); } @@ -1657,7 +1710,13 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) /* Pretty print template instantiations only. */ if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t) && !(flags & TFF_NO_TEMPLATE_BINDINGS) - && flag_pretty_templates) + && flag_pretty_templates + // skip the output of template parameters if the function is a member of a + // class with diagnose_as attribute: + && !(flag_diagnostics_use_aliases + && DECL_CLASS_SCOPE_P (t) + && lookup_attribute ("diagnose_as", + TYPE_ATTRIBUTES (DECL_CONTEXT (t))))) { tree tmpl; @@ -1885,6 +1944,16 @@ dump_exception_spec (cxx_pretty_printer *pp, tree t, int flags) static void dump_function_name (cxx_pretty_printer *pp, tree t, int flags) { + if (flag_diagnostics_use_aliases) + { + tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (t)); + if (attr) + { + pp_cxx_ws_string ( + pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)))); + return; + } + } tree name = DECL_NAME (t); /* We can get here with a decl that was synthesized by language- @@ -3133,7 +3202,7 @@ lang_decl_name (tree decl, int v, bool translate) && TREE_CODE (decl) == NAMESPACE_DECL) dump_decl (cxx_pp, decl, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME); else - dump_decl (cxx_pp, DECL_NAME (decl), TFF_PLAIN_IDENTIFIER); + dump_decl_name_or_diagnose_as (cxx_pp, decl, TFF_PLAIN_IDENTIFIER); return pp_ggc_formatted_text (cxx_pp); } diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 4e84e2f9987..80637503310 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -6082,6 +6082,33 @@ handle_namespace_attrs (tree ns, tree attributes) DECL_ATTRIBUTES (ns) = tree_cons (name, args, DECL_ATTRIBUTES (ns)); } + else if (is_attribute_p ("diagnose_as", name)) + { + if (!args || TREE_CHAIN(args)) + { + error ("the %qE attribute requires exactly one argument", name); + continue; + } + if (TREE_CODE (TREE_VALUE (args)) != STRING_CST) + { + error ("the argument to the %qE attribute must be a string " + "literal", name); + continue; + } + tree existing + = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (ns)); + if (existing + && !cp_tree_equal(TREE_VALUE (args), + TREE_VALUE (TREE_VALUE (existing)))) + { + error ("the namespace %qE already uses a different diagnose_as " + "attribute value", ns); + inform (DECL_SOURCE_LOCATION (ns), "previous declaration here"); + continue; + } + DECL_ATTRIBUTES (ns) = tree_cons (name, args, + DECL_ATTRIBUTES (ns)); + } else { warning (OPT_Wattributes, "%qD attribute directive ignored", diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index a8bfd5fc053..f7b93dc89d7 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *); static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *); static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *); +static tree handle_diagnose_as_attribute (tree *, tree, tree, int, bool *); /* If REF is an lvalue, returns the kind of lvalue that REF is. Otherwise, returns clk_none. */ @@ -4860,6 +4861,8 @@ const struct attribute_spec cxx_attribute_table[] = handle_init_priority_attribute, NULL }, { "abi_tag", 1, -1, false, false, false, true, handle_abi_tag_attribute, NULL }, + { "diagnose_as", 1, 1, false, false, false, false, + handle_diagnose_as_attribute, NULL }, { NULL, 0, 0, false, false, false, false, NULL, NULL } }; @@ -5128,6 +5131,120 @@ handle_abi_tag_attribute (tree* node, tree name, tree args, return NULL_TREE; } +static bool +check_diagnose_as_redeclaration (const_tree decl, const_tree old, + const_tree new_) +{ + if (!old) + return true; + if (TREE_CODE (TREE_VALUE (old)) == TREE_LIST) + old = TREE_VALUE (old); + if (TREE_CODE (TREE_VALUE (new_)) == TREE_LIST) + new_ = TREE_VALUE (new_); + tree old_value = TREE_VALUE (old); + tree new_value = TREE_VALUE (new_); + if (cp_tree_equal (old_value, new_value)) + return true; + error ("conflicting declaration of % attribute on %qD would " + "overwrite %qE with %qE", decl, old_value, new_value); + return false; +} + +static tree +handle_diagnose_as_attribute (tree* node, tree name, tree args, + int flags, bool* no_add_attrs) +{ + tree decl = NULL_TREE; + if (!args || TREE_CHAIN (args)) + { + error ("the %qE attribute requires exactly one argument", name); + goto fail; + } + if (TREE_CODE (TREE_VALUE (args)) != STRING_CST) + { + error ("the argument to the %qE attribute must be a string literal", + name); + goto fail; + } + if (TREE_CODE (*node) == TYPE_DECL) + { + // Apply the attribute to the type alias itself. + decl = *node; + tree type = TREE_TYPE (*node); + if (CLASS_TYPE_P (type) && CLASSTYPE_IMPLICIT_INSTANTIATION (type)) + { + if (COMPLETE_OR_OPEN_TYPE_P (type)) + warning (OPT_Wattributes, "%qE attribute cannot be applied to %qT " + "after its instantiation", name, type); + else + { + type = strip_typedefs(type, nullptr, 0); + // And apply the attribute to the specialization on the RHS. + tree attributes = tree_cons (name, args, NULL_TREE); + decl_attributes (&type, attributes, + ATTR_FLAG_TYPE_IN_PLACE, NULL_TREE); + } + } + } + else if (TYPE_P (*node)) + { + if (!OVERLOAD_TYPE_P (*node)) + { + error ("%qE attribute applied to non-class, non-enum type %qT", + name, *node); + goto fail; + } + else if (!(flags & (int)ATTR_FLAG_TYPE_IN_PLACE)) + { + error ("%qE attribute applied to %qT after its definition", + name, *node); + goto fail; + } + decl = TYPE_NAME (*node); + } + else + { + if (!VAR_OR_FUNCTION_DECL_P (*node)) + { + error ("%qE attribute applied to non-function, non-variable %qD", + name, *node); + goto fail; + } + else if (DECL_LANGUAGE (*node) == lang_c) + { + error ("%qE attribute applied to extern \"C\" declaration %qD", + name, *node); + goto fail; + } + decl = *node; + } + + // Make sure all declarations have the same diagnose_as string. + if (DECL_SOURCE_LOCATION (decl) != input_location) + { + tree attributes = TYPE_P (*node) ? TYPE_ATTRIBUTES (*node) + : DECL_ATTRIBUTES (decl); + if (!check_diagnose_as_redeclaration ( + decl, lookup_attribute ("diagnose_as", attributes), args)) + goto fail; + } + else if (CLASS_TYPE_P (*node) && CLASSTYPE_IMPLICIT_INSTANTIATION (*node)) + { + // The above branch (different source location) is taken for declarations + // of type aliases that modify an implicit template specialization on the + // RHS. This branch is taken when the template is instantiated via + // instantiate_class_template_1, in which case the attribute either + // already has the value from the general template or from a type alias. + goto fail; + } + + return NULL_TREE; + + fail: + *no_add_attrs = true; + return NULL_TREE; +} + /* Return a new PTRMEM_CST of the indicated TYPE. The MEMBER is the thing pointed to by the constant. */ diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index c8caf36f293..7976e3a72d4 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -2865,6 +2865,43 @@ types (@pxref{Variable Attributes}, @pxref{Type Attributes}.) The message attached to the attribute is affected by the setting of the @option{-fmessage-length} option. +@item diagnose_as ("@var{string}") +@cindex @code{diagnose_as} function attribute +The @code{diagnose_as} attribute modifies how the entity the attribute +appertains to is diagnosed in compiler messages and __FUNCTION__ / +__PRETTY_FUNCTION__ macros. If the attribute is applied to a @code{namespace}, +the specified string replaces the complete enclosing scope. The effect of the +@code{diagnose_as} attribute can be disabled with the +@option{-fno-diagnostics-use-aliases} command line option. + +@smallexample +namespace Foo @{ + namespace Bar @{ + inline namespace v1 [[gnu::diagnose_as("Foobar")]] @{ + int f() @{ + // __PRETTY_FUNCTION__ == "void Foobar::f()" + @} + @} + @} +@} +// In function 'int Foobar::f()': +// warning: no return statement in function returning non-void [-Wreturn-type] +@end smallexample + +The @code{diagnose_as} attribute can be used with namespaces, functions, +variables, alias declarations (but not alias templates), and user-defined types +(classes, unions, and enums). If the alias declaration aliases an implicit class +template specialization, the attribute is additionally applied to the class +template specialization. + +@smallexample +template struct basic_string @{@}; +using string [[gnu::diagnose_as("string")]] = basic_string; +int f(basic_string) @{@} +// In function 'int f(string)': +// warning: no return statement in function returning non-void [-Wreturn-type] +@end smallexample + @item error ("@var{message}") @itemx warning ("@var{message}") @cindex @code{error} function attribute diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index f5a9dc33819..84c19344c89 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -311,7 +311,8 @@ Objective-C and Objective-C++ Dialects}. -fdiagnostics-show-path-depths @gol -fno-show-column @gol -fdiagnostics-column-unit=@r{[}display@r{|}byte@r{]} @gol --fdiagnostics-column-origin=@var{origin}} +-fdiagnostics-column-origin=@var{origin} @gol +-fno-diagnostics-aliases} @item Warning Options @xref{Warning Options,,Options to Request or Suppress Warnings}. @@ -5077,6 +5078,12 @@ first column. The default value of 1 corresponds to traditional GCC behavior and to the GNU style guide. Some utilities may perform better with an origin of 0; any non-negative value may be specified. +@item -fno-diagnostics-use-aliases +@opindex fno-diagnostics-use-aliases +@opindex fdiagnostics-use-aliases +Do not replace identifiers or scope names with the aliases defined with the +@code{[[gnu::diagnose_as("alias")]]} attribute. + @item -fdiagnostics-format=@var{FORMAT} @opindex fdiagnostics-format Select a different format for printing diagnostics.