From: ThePhD <phdoftheho...@gmail.com>

---
 gcc/c-family/c-lex.c                          |  38 ++--
 gcc/cp/cvt.c                                  | 204 +++++++++++-------
 gcc/cp/parser.c                               |  23 +-
 gcc/cp/tree.c                                 |  56 +++--
 gcc/escaped_string.h                          |  43 ++++
 .../g++.dg/cpp2a/nodiscard-bad-clause.C       |  12 ++
 .../g++.dg/cpp2a/nodiscard-once-clause.C      |  12 ++
 gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C   |  12 ++
 gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C | 203 +++++++++++++++++
 gcc/tree.c                                    |  17 +-
 10 files changed, 487 insertions(+), 133 deletions(-)
 create mode 100644 gcc/escaped_string.h
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C

diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index 851fd704e5d..f5870d095f2 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -345,24 +345,26 @@ c_common_has_attribute (cpp_reader *pfile)
                  attr_name = NULL_TREE;
                }
            }
-         else
-           {
-             /* Some standard attributes need special handling.  */
-             if (is_attribute_p ("noreturn", attr_name))
-               result = 200809;
-             else if (is_attribute_p ("deprecated", attr_name))
-               result = 201309;
-             else if (is_attribute_p ("maybe_unused", attr_name)
-                      || is_attribute_p ("nodiscard", attr_name)
-                      || is_attribute_p ("fallthrough", attr_name))
-               result = 201603;
-             else if (is_attribute_p ("no_unique_address", attr_name)
-                      || is_attribute_p ("likely", attr_name)
-                      || is_attribute_p ("unlikely", attr_name))
-               result = 201803;
-             if (result)
-               attr_name = NULL_TREE;
-           }
+               else
+                       {
+                               /* Some standard attributes need special 
handling.  */
+                               if (is_attribute_p ("noreturn", attr_name))
+                                       result = 200809;
+                               else if (is_attribute_p ("deprecated", 
attr_name))
+                                       result = 201309;
+                               else if (is_attribute_p ("maybe_unused", 
attr_name)
+                                        || is_attribute_p ("fallthrough", 
attr_name))
+                                       result = 201603;
+                               else if (is_attribute_p ("no_unique_address", 
attr_name)
+                                        || is_attribute_p ("likely", attr_name)
+                                        || is_attribute_p ("unlikely", 
attr_name))
+                                       result = 201803;
+                               else if (is_attribute_p ("nodiscard", 
attr_name))
+                                       result = 201907; /* placeholder until 
C++20 Post-Cologne Working Draft. */
+             
+                               if (result)
+                                       attr_name = NULL_TREE;
+                       }
        }
       if (attr_name)
        {
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 23d2aabc483..4a5128c76a1 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "convert.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "escaped_string.h"
 
 static tree convert_to_pointer_force (tree, tree, tsubst_flags_t);
 static tree build_type_conversion (tree, tree);
@@ -1026,49 +1027,99 @@ maybe_warn_nodiscard (tree expr, impl_conv_void 
implicit)
 
   tree rettype = TREE_TYPE (type);
   tree fn = cp_get_fndecl_from_callee (callee);
-  if (implicit != ICV_CAST && fn
-      && lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))
-    {
-      auto_diagnostic_group d;
-      if (warning_at (loc, OPT_Wunused_result,
-                     "ignoring return value of %qD, "
-                     "declared with attribute nodiscard", fn))
-       inform (DECL_SOURCE_LOCATION (fn), "declared here");
-    }
-  else if (implicit != ICV_CAST
-          && lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype)))
-    {
-      auto_diagnostic_group d;
-      if (warning_at (loc, OPT_Wunused_result,
-                     "ignoring returned value of type %qT, "
-                     "declared with attribute nodiscard", rettype))
-       {
-         if (fn)
-           inform (DECL_SOURCE_LOCATION (fn),
-                   "in call to %qD, declared here", fn);
-         inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
-                 "%qT declared here", rettype);
-       }
-    }
-  else if (TREE_CODE (expr) == TARGET_EXPR
-          && lookup_attribute ("warn_unused_result", TYPE_ATTRIBUTES (type)))
-    {
-      /* The TARGET_EXPR confuses do_warn_unused_result into thinking that the
-        result is used, so handle that case here.  */
-      if (fn)
-       {
-         auto_diagnostic_group d;
-         if (warning_at (loc, OPT_Wunused_result,
-                         "ignoring return value of %qD, "
-                         "declared with attribute %<warn_unused_result%>",
-                         fn))
-           inform (DECL_SOURCE_LOCATION (fn), "declared here");
-       }
-      else
-       warning_at (loc, OPT_Wunused_result,
-                   "ignoring return value of function "
-                   "declared with attribute %<warn_unused_result%>");
-    }
+       if (implicit != ICV_CAST && fn
+               && lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn)))
+               {
+                       tree attr = DECL_ATTRIBUTES (fn);
+                       escaped_string msg;
+                       if (attr)
+                               msg.escape (TREE_STRING_POINTER (TREE_VALUE 
(TREE_VALUE (attr))));
+                       bool has_msg = static_cast<bool>(msg);
+                       const char* format = (has_msg ? 
+                               "ignoring return value of %qD, "
+                               "declared with attribute %<nodiscard%>: %<%s%>" 
:
+                               "ignoring return value of %qD, "
+                               "declared with attribute %<nodiscard%>%s");
+                       const char* raw_msg = (has_msg ? (const char*)msg : "");
+                       auto_diagnostic_group d;
+                       if (warning_at (loc, OPT_Wunused_result,
+                                      format, fn, raw_msg))
+                               {
+                                       inform (DECL_SOURCE_LOCATION (fn), 
"declared here");
+                               }
+               }
+       else if (implicit != ICV_CAST
+                && lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype)))
+               {
+                       tree attr = TYPE_ATTRIBUTES (rettype);
+                       escaped_string msg;
+                       if (attr)
+                               msg.escape (TREE_STRING_POINTER (TREE_VALUE 
(TREE_VALUE (attr))));
+                       bool has_msg = static_cast<bool>(msg);
+                       const char* format = (has_msg ? 
+                               "ignoring returned value of type %qT, "
+                               "declared with attribute %<nodiscard%>: %<%s%>" 
:
+                               "ignoring return value of type %qT, "
+                               "declared with attribute %<nodiscard%>%s");
+                       const char* raw_msg = (has_msg ? (const char*)msg : "");
+                       auto_diagnostic_group d;
+                       if (warning_at (loc, OPT_Wunused_result,
+                                       format, rettype, raw_msg))
+                               {
+                                       if (fn) 
+                                               {
+                                                       inform 
(DECL_SOURCE_LOCATION (fn),
+                                                               "in call to 
%qD, declared here", fn);
+                                               }
+                                       inform (DECL_SOURCE_LOCATION (TYPE_NAME 
(rettype)),
+                                               "%qT declared here", rettype);
+                       }
+               }
+       else if (TREE_CODE (expr) == TARGET_EXPR)
+               {
+                       if (lookup_attribute ("warn_unused_result", 
TYPE_ATTRIBUTES (type)))
+                               {
+                                       /* The TARGET_EXPR confuses 
do_warn_unused_result into thinking that the
+                                       result is used, so handle that case 
here.  */
+                                       if (fn)
+                                               {
+                                                       auto_diagnostic_group d;
+                                                       if (warning_at (loc, 
OPT_Wunused_result,
+                                                                       
"ignoring return value of %qD, "
+                                                                       
"declared with attribute %<warn_unused_result%>",
+                                                                       fn))
+                                                               {
+                                                                       inform 
(DECL_SOURCE_LOCATION (fn), 
+                                                                               
                        "declared here");
+                                                               }
+                                               }
+                                       else
+                                               warning_at (loc, 
OPT_Wunused_result,
+                                                       "ignoring return value 
of function "
+                                                       "declared with 
attribute %<warn_unused_result%>");
+                               }
+                       else if (implicit != ICV_CAST && fn 
+                               && lookup_attribute ("nodiscard", 
DECL_ATTRIBUTES (fn))) 
+                               {
+                                       tree attr = DECL_ATTRIBUTES (fn);
+                                       escaped_string msg;
+                                       if (attr)
+                                               msg.escape (TREE_STRING_POINTER 
(TREE_VALUE (TREE_VALUE (attr))));
+                                       bool has_msg = static_cast<bool>(msg);
+                                       const char* format = (has_msg ? 
+                                               "ignoring return value of %qD, "
+                                               "declared with attribute 
%<nodiscard%>: %<%s%>" :
+                                               "ignoring return value of %qD, "
+                                               "declared with attribute 
%<nodiscard%>%s");
+                                       const char* raw_msg = (has_msg ? (const 
char*)msg : "");
+                                       auto_diagnostic_group d;
+                                       if (warning_at (loc, OPT_Wunused_result,
+                                                       format, fn, raw_msg))
+                                               {
+                                                       inform 
(DECL_SOURCE_LOCATION (fn), "declared here");
+                                               }
+                               }
+               }
 }
 
 /* When an expression is used in a void context, its value is discarded and
@@ -1174,17 +1225,17 @@ convert_to_void (tree expr, impl_conv_void implicit, 
tsubst_flags_t complain)
 
     case CALL_EXPR:   /* We have a special meaning for volatile void fn().  */
       /* cdtors may return this or void, depending on
-        targetm.cxx.cdtor_returns_this, but this shouldn't affect our
-        decisions here: neither nodiscard warnings (nodiscard cdtors
-        are nonsensical), nor should any constexpr or template
-        instantiations be affected by an ABI property that is, or at
-        least ought to be transparent to the language.  */
-      if (tree fn = cp_get_callee_fndecl_nofold (expr))
-       if (DECL_CONSTRUCTOR_P (fn) || DECL_DESTRUCTOR_P (fn))
-         return expr;
-
-      maybe_warn_nodiscard (expr, implicit);
-      break;
+                       targetm.cxx.cdtor_returns_this, but this shouldn't 
affect our
+                       decisions here: neither nodiscard warnings (nodiscard 
cdtors
+                       are nonsensical), nor should any constexpr or template
+                       instantiations be affected by an ABI property that is, 
or at
+                       least ought to be transparent to the language.  */
+                       if (tree fn = cp_get_callee_fndecl_nofold (expr))
+                               if (DECL_DESTRUCTOR_P (fn))
+                                       return expr;
+
+                       maybe_warn_nodiscard (expr, implicit);
+                       break;
 
     case INDIRECT_REF:
       {
@@ -1396,28 +1447,29 @@ convert_to_void (tree expr, impl_conv_void implicit, 
tsubst_flags_t complain)
       }
 
     case TARGET_EXPR:
-      /* Don't bother with the temporary object returned from a function if
-        we don't use it, don't need to destroy it, and won't abort in
-        assign_temp.  We'll still
-        allocate space for it in expand_call or declare_return_variable,
-        but we don't need to track it through all the tree phases.  */
-      if (TARGET_EXPR_IMPLICIT_P (expr)
-         && !TREE_ADDRESSABLE (TREE_TYPE (expr)))
-       {
-         tree init = TARGET_EXPR_INITIAL (expr);
-         if (TREE_CODE (init) == AGGR_INIT_EXPR
-             && !AGGR_INIT_VIA_CTOR_P (init))
-           {
-             tree fn = AGGR_INIT_EXPR_FN (init);
-             expr = build_call_array_loc (input_location,
-                                          TREE_TYPE (TREE_TYPE
-                                                     (TREE_TYPE (fn))),
-                                          fn,
-                                          aggr_init_expr_nargs (init),
-                                          AGGR_INIT_EXPR_ARGP (init));
-           }
-       }
-      maybe_warn_nodiscard (expr, implicit);
+                       /* Don't bother with the temporary object returned from 
a function if
+                       we don't use it, don't need to destroy it, and won't 
abort in
+                       assign_temp.  We'll still
+                       allocate space for it in expand_call or 
declare_return_variable,
+                       but we don't need to track it through all the tree 
phases.  */
+                       if (TARGET_EXPR_IMPLICIT_P (expr)
+                           && !TREE_ADDRESSABLE (TREE_TYPE (expr)))
+                               {
+                                       tree init = TARGET_EXPR_INITIAL (expr);
+                                       if (TREE_CODE (init) == AGGR_INIT_EXPR
+                                           && !AGGR_INIT_VIA_CTOR_P (init))
+                                               {
+                                                       tree fn = 
AGGR_INIT_EXPR_FN (init);
+                                                       expr = 
build_call_array_loc (input_location,
+                                                       TREE_TYPE (TREE_TYPE
+                                                       (TREE_TYPE (fn))),
+                                                       fn,
+                                                       aggr_init_expr_nargs 
(init),
+                                                       AGGR_INIT_EXPR_ARGP 
(init));
+                                               }
+                               }
+      
+                       maybe_warn_nodiscard (expr, implicit);
       break;
 
     default:;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index ebeffdb775f..7890a3c6d73 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -26399,13 +26399,26 @@ cp_parser_check_std_attribute (tree attributes, tree 
attribute)
     {
       tree name = get_attribute_name (attribute);
       if (is_attribute_p ("noreturn", name)
-         && lookup_attribute ("noreturn", attributes))
-       error ("attribute %<noreturn%> can appear at most once "
-              "in an attribute-list");
+            && lookup_attribute ("noreturn", attributes)) 
+        {
+               error ("attribute %<noreturn%> can appear at most once "
+                      "in an attribute-list");
+        }
       else if (is_attribute_p ("deprecated", name)
               && lookup_attribute ("deprecated", attributes))
-       error ("attribute %<deprecated%> can appear at most once "
-              "in an attribute-list");
+        {
+                error ("attribute %<deprecated%> can appear at most once "
+                       "in an attribute-list");
+        }
+      else if (cxx_dialect >= cxx2a) 
+        {
+          if (is_attribute_p ("nodiscard", name)
+                && lookup_attribute ("nodiscard", attributes)) 
+            {
+                   error ("%attribute qE can appear at most once "
+                          "in an attribute-list", name);
+            }
+        }
     }
 }
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 37e24a1669c..bfdd804290e 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -4356,25 +4356,45 @@ zero_init_p (const_tree t)
    warn_unused_result attribute.  */
 
 static tree
-handle_nodiscard_attribute (tree *node, tree name, tree /*args*/,
+handle_nodiscard_attribute (tree *node, tree name, tree args,
                            int /*flags*/, bool *no_add_attrs)
 {
-  if (TREE_CODE (*node) == FUNCTION_DECL)
-    {
-      if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
-       warning_at (DECL_SOURCE_LOCATION (*node),
-                   OPT_Wattributes, "%qE attribute applied to %qD with void "
-                   "return type", name, *node);
-    }
-  else if (OVERLOAD_TYPE_P (*node))
-    /* OK */;
-  else
-    {
-      warning (OPT_Wattributes, "%qE attribute can only be applied to "
-              "functions or to class or enumeration types", name);
-      *no_add_attrs = true;
-    }
-  return NULL_TREE;
+       if (!args)
+               *no_add_attrs = true;
+       else if (cxx_dialect >= cxx2a)
+               {
+                       if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+                       {
+                               error ("%qE attribute argument must be a string 
constant", name);
+                               *no_add_attrs = true;
+                       }
+               }
+       else
+               {
+                       if (!*no_add_attrs) 
+                               {
+                                       error ("%qE attribute with an argument 
only available with "
+                                              "%<-std=c++2a%> or 
%<-std=gnu++2a%>", name);
+                               }
+               }
+               
+       
+       if (TREE_CODE (*node) == FUNCTION_DECL)
+               {
+                       if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (*node))) && 
DECL_CONSTRUCTOR_P(*node) == 0)
+                               warning_at (DECL_SOURCE_LOCATION (*node),
+                               OPT_Wattributes, "%qE attribute applied to %qD 
with void "
+                               "return type", name, *node);
+               }
+       else if (OVERLOAD_TYPE_P (*node))
+               /* OK */;
+       else
+               {
+                       warning (OPT_Wattributes, "%qE attribute can only be 
applied to "
+                       "functions or to class or enumeration types", name);
+                       *no_add_attrs = true;
+               }
+       return NULL_TREE;
 }
 
 /* Handle a C++2a "no_unique_address" attribute; arguments as in
@@ -4447,7 +4467,7 @@ const struct attribute_spec std_attribute_table[] =
        affects_type_identity, handler, exclude } */
   { "maybe_unused", 0, 0, false, false, false, false,
     handle_unused_attribute, NULL },
-  { "nodiscard", 0, 0, false, false, false, false,
+  { "nodiscard", 0, 1, false, false, false, false,
     handle_nodiscard_attribute, NULL },
   { "no_unique_address", 0, 0, true, false, false, false,
     handle_no_unique_addr_attribute, NULL },
diff --git a/gcc/escaped_string.h b/gcc/escaped_string.h
new file mode 100644
index 00000000000..ab0fbafc5fd
--- /dev/null
+++ b/gcc/escaped_string.h
@@ -0,0 +1,43 @@
+/* Shared escaped string class.
+   Copyright (C) 1999-2019 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ESCAPED_STRING_H
+#define GCC_ESCAPED_STRING_H
+
+#include <cstdlib>
+
+/* A class to handle converting a string that might contain
+   control characters, (eg newline, form-feed, etc), into one
+   in which contains escape sequences instead.  */
+
+class escaped_string
+{
+ public:
+  escaped_string () { m_owned = false; m_str = NULL; };
+  ~escaped_string () { if (m_owned) free (m_str); }
+  operator const char *() const { return (const char *) m_str; }
+  void escape (const char *);
+ private:
+  escaped_string(const escaped_string&) {}
+  escaped_string& operator=(const escaped_string&) { return *this; }
+  char *m_str;
+  bool  m_owned;
+};
+
+#endif /* ! GCC_ESCAPED_STRING_H */
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C 
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
new file mode 100644
index 00000000000..e2a95ef6625
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-bad-clause.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard(123)]] int check1 (void); /* { dg-error "(?n)nodiscard.*must be a 
string constant" } */
+
+void
+test (void)
+{
+  check1 ();           /* { dg-warning "(?n)nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C 
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
new file mode 100644
index 00000000000..5d7cfafc2c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once-clause.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard("not", "allowed")]] int check1 (void); /* { dg-error "(?n)wrong 
number of arguments..*nodiscard" } */
+
+void
+test (void)
+{
+  check1 ();           /* { dg-warning "nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C 
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
new file mode 100644
index 00000000000..d88d5c9d8b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-once.C
@@ -0,0 +1,12 @@
+/* nodiscard attribute tests  */
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+[[nodiscard, nodiscard]] int check1 (void); /* { dg-error "(?n)nodiscard.*can 
appear at most once" } */
+
+void
+test (void)
+{
+  check1 ();           /* { dg-warning "nodiscard" } */
+  (void) check1 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C 
b/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
new file mode 100644
index 00000000000..3617365b2b0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nodiscard-reason.C
@@ -0,0 +1,203 @@
+/* nodiscard attribute tests, adapted from gcc.dg/attr-warn-unused-result.c.  
*/
+/* { dg-do compile { target c++2a } } */
+/* { dg-options "-O -ftrack-macro-expansion=0" } */
+
+#define NODIS [[nodiscard("exact_message")]]
+#define NODISAI [[nodiscard("exact_inline_message"), gnu::always_inline]] 
inline
+enum [[nodiscard("exact_E_message")]] E { e };
+typedef E (*fnt) (void);
+
+typedef struct { long i; } A;
+typedef struct { long i; long j; } B;
+typedef struct { char big[1024]; fnt fn; } C;
+struct [[nodiscard("exact_D_message")]] D { int i; D(); ~D(); };
+
+NODIS E check1 (void);
+NODIS void check2 (void); /* { dg-warning "(?n)10:.nodiscard.*exact_message" } 
*/
+NODIS int foo;         /* { dg-warning "(?n)9:.nodiscard.*exact_message" } */
+int bar (void);
+NODISAI E check3 (void) { return (E)bar (); }
+NODIS A check4 (void);
+NODIS B check5 (void);
+NODIS C check6 (void);
+A bar7 (void);
+B bar8 (void);
+C bar9 (void);
+NODISAI A check7 (void) { return bar7 (); }
+NODISAI B check8 (void) { return bar8 (); }
+NODISAI C check9 (void) { return bar9 (); }
+/* This is useful for checking whether return value of statement
+   expressions (returning int in this case) is used.  */
+NODISAI int check_int_result (int res) { return res; }
+#define GU(v) ({ int e = 0; (v) = bar (); if ((v) < 23) e = 14; e; })
+fnt fnptr;
+NODIS E check10 (void);
+int baz (void);
+NODISAI E check11 (void) { return (E)baz (); }
+int k;
+
+D check12();
+
+void
+test (void)
+{
+  int i = 0, j;
+  const fnt pcheck1 = check1;
+  const fnt pcheck3 = check3;
+  A a;
+  B b;
+  C c;
+  D d;
+  if (check1 ())
+    return;
+  i += check1 ();
+  i += ({ check1 (); });
+  check1 ();           /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check1 ();
+  check1 (), bar ();   /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  check2 ();
+  (void) check2 ();
+  check2 (), bar ();
+  if (check3 ())
+    return;
+  i += check3 ();
+  i += ({ check3 (); });
+  check3 ();           /* { dg-warning "(?n)nodiscard.*exact_inline_message" } 
*/
+  (void) check3 ();
+  check3 (), bar ();   /* { dg-warning "(?n)nodiscard.*exact_inline_message" } 
*/
+  a = check4 ();
+  if (a.i)
+    return;
+  if (check4 ().i)
+    return;
+  if (({ check4 (); }).i)
+    return;
+  check4 ();           /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check4 ();
+  check4 (), bar ();   /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  b = check5 ();
+  if (b.i + b.j)
+    return;
+  if (check5 ().j)
+    return;
+  if (({ check5 (); }).j)
+    return;
+  check5 ();           /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check5 ();
+  check5 (), bar ();   /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  c = check6 ();
+  if (c.big[12] + c.big[29])
+    return;
+  if (check6 ().big[27])
+    return;
+  if (({ check6 (); }).big[0])
+    return;
+  check6 ();           /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  (void) check6 ();
+  check6 (), bar ();   /* { dg-warning "(?n)nodiscard.*exact_message" } */
+  a = check7 ();
+  if (a.i)
+    return;
+  if (check7 ().i)
+    return;
+  if (({ check7 (); }).i)
+    return;
+  check7 ();           /* { dg-warning "(?n)nodiscard.*exact_inline_message" } 
*/
+  (void) check7 ();
+  check7 (), bar ();   /* { dg-warning "(?n)nodiscard.*exact_inline_message" } 
*/
+  b = check8 ();
+  if (b.i + b.j)
+    return;
+  if (check8 ().j)
+    return;
+  if (({ check8 (); }).j)
+    return;
+  check8 ();           /* { dg-warning "(?n)nodiscard.*exact_inline_message" } 
*/
+  (void) check8 ();
+  check8 (), bar ();   /* { dg-warning "(?n)nodiscard.*exact_inline_message" } 
*/
+  c = check9 ();
+  if (c.big[12] + c.big[29])
+    return;
+  if (check9 ().big[27])
+    return;
+  if (({ check9 (); }).big[0])
+    return;
+  check9 ();           /* { dg-warning "(?n)nodiscard.*exact_inline_message" } 
*/
+  (void) check9 ();
+  check9 (), bar ();   /* { dg-warning "(?n)nodiscard.*exact_inline_message" } 
*/
+  if (check_int_result (GU (j)))
+    return;
+  i += check_int_result (GU (j));
+  i += ({ check_int_result (GU (j)); });
+  check_int_result (GU (j)); /* { dg-warning 
"(?n)nodiscard.*exact_inline_message" } */
+  (void) check_int_result (GU (j));
+  check_int_result (GU (j)), bar (); /* { dg-warning 
"(?n)nodiscard.*exact_inline_message" } */
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr ();            /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar ();    /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  fnptr = check1;
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr ();            /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar ();    /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  fnptr = check3;
+  if (fnptr ())
+    return;
+  i += fnptr ();
+  i += ({ fnptr (); });
+  fnptr ();            /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) fnptr ();
+  fnptr (), bar ();    /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  if (bar9 ().fn ())
+    return;
+  i += bar9 ().fn ();
+  i += ({ bar9 ().fn (); });
+  bar9 ().fn ();       /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) bar9 ().fn ();
+  bar9 ().fn (), bar (); /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  if ((k ? check1 : check10) ())
+    return;
+  i += (k ? check1 : check10) ();
+  i += ({ (k ? check1 : check10) (); });
+  (k ? check1 : check10) (); /* { dg-warning "(?n)nodiscard.*exact_E_message" 
} */
+  (void) (k ? check1 : check10) ();
+  (k ? check1 : check10) (), bar (); /* { dg-warning 
"(?n)nodiscard.*exact_E_message" } */
+  if ((k ? check3 : check11) ())
+    return;
+  i += (k ? check3 : check11) ();
+  i += ({ (k ? check3 : check11) (); });
+  (k ? check3 : check11) (); /* { dg-warning 
"(?n)nodiscard.*exact_inline_message" } */
+  (void) (k ? check3 : check11) ();
+  (k ? check3 : check11) (), bar (); /* { dg-warning 
"(?n)nodiscard.*exact_inline_message" } */
+  if (pcheck1 ())
+    return;
+  i += pcheck1 ();
+  i += ({ pcheck1 (); });
+  pcheck1 ();          /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) pcheck1 ();
+  pcheck1 (), bar ();  /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  if (pcheck3 ())
+    return;
+  i += pcheck3 ();
+  i += ({ pcheck3 (); });
+  pcheck3 ();          /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  (void) pcheck3 ();
+  pcheck3 (), bar ();  /* { dg-warning "(?n)nodiscard.*exact_E_message" } */
+  d = check12 ();
+  if (d.i)
+    return;
+  if (check12 ().i)
+    return;
+  if (({ check12 (); }).i)
+    return;
+  check12 ();          /* { dg-warning "(?n)nodiscard.*exact_D_message" } */
+  (void) check12 ();
+  check12 (), bar ();  /* { dg-warning "(?n)nodiscard.*exact_D_message" } */
+}
diff --git a/gcc/tree.c b/gcc/tree.c
index 8cf75f22220..362e07d1c33 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "backend.h"
 #include "target.h"
 #include "tree.h"
+#include "escaped_string.h"
 #include "gimple.h"
 #include "tree-pass.h"
 #include "ssa.h"
@@ -13132,22 +13133,6 @@ typedef_variant_p (const_tree type)
   return is_typedef_decl (TYPE_NAME (type));
 }
 
-/* A class to handle converting a string that might contain
-   control characters, (eg newline, form-feed, etc), into one
-   in which contains escape sequences instead.  */
-
-class escaped_string
-{
- public:
-  escaped_string () { m_owned = false; m_str = NULL; };
-  ~escaped_string () { if (m_owned) free (m_str); }
-  operator const char *() const { return (const char *) m_str; }
-  void escape (const char *);
- private:
-  char *m_str;
-  bool  m_owned;
-};
-
 /* PR 84195: Replace control characters in "unescaped" with their
    escaped equivalents.  Allow newlines if -fmessage-length has
    been set to a non-zero value.  This is done here, rather than
-- 
2.22.0.windows.1


From 0d694cca799cfc39b443c7d6c6857dd62d783c00 Mon Sep 17 00:00:00 2001
From: ThePhD <phdoftheho...@gmail.com>
Date: Thu, 25 Jul 2019 19:46:03 -0400
Subject: [PATCH 2/2] mark the correct format sequence with a percent sign

---
 gcc/cp/parser.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 7890a3c6d73..20ff70cafc2 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -26415,7 +26415,7 @@ cp_parser_check_std_attribute (tree attributes, tree 
attribute)
           if (is_attribute_p ("nodiscard", name)
                 && lookup_attribute ("nodiscard", attributes)) 
             {
-                   error ("%attribute qE can appear at most once "
+                   error ("attribute %qE can appear at most once "
                           "in an attribute-list", name);
             }
         }
-- 
2.22.0.windows.1

Reply via email to