}
\f
+/* Handle the standard [[nodiscard]] attribute. */
+
+static tree
+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 (RECORD_OR_UNION_TYPE_P (*node)
+ || TREE_CODE (*node) == ENUMERAL_TYPE)
+ /* OK */;
+ else
+ {
+ pedwarn (input_location,
+ OPT_Wattributes, "%qE attribute can only be applied to "
+ "functions or to structure, union or enumeration types", name);
+ *no_add_attrs = true;
+ }
+ return NULL_TREE;
+}
/* Table of supported standard (C2x) attributes. */
const struct attribute_spec std_attribute_table[] =
{
handle_fallthrough_attribute, NULL },
{ "maybe_unused", 0, 0, false, false, false, false,
handle_unused_attribute, NULL },
+ { "nodiscard", 0, 1, false, false, false, false,
+ handle_nodiscard_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
&& attribute_takes_identifier_p (name));
bool require_string
= (ns == NULL_TREE
- && strcmp (IDENTIFIER_POINTER (name), "deprecated") == 0);
+ && (strcmp (IDENTIFIER_POINTER (name), "deprecated") == 0
+ || strcmp (IDENTIFIER_POINTER (name), "nodiscard") == 0));
TREE_VALUE (attribute)
= c_parser_attribute_arguments (parser, takes_identifier,
require_string, false);
parens.require_close (parser);
}
out:
- if (ns == NULL_TREE && !for_tm && !as && !is_attribute_p ("nodiscard", name))
+ if (ns == NULL_TREE && !for_tm && !as)
{
/* An attribute with standard syntax and no namespace specified
is a constraint violation if it is not one of the known
- standard attributes (of which nodiscard is the only one
- without a handler in GCC). Diagnose it here with a pedwarn
- and then discard it to prevent a duplicate warning later. */
+ standard attributes. Diagnose it here with a pedwarn and
+ then discard it to prevent a duplicate warning later. */
pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored",
name);
return error_mark_node;
return ret;
}
\f
+/* EXPR is an expression, location LOC, whose result is discarded.
+ Warn if it is a call to a nodiscard function (or a COMPOUND_EXPR
+ whose right-hand operand is such a call, possibly recursively). */
+
+static void
+maybe_warn_nodiscard (location_t loc, tree expr)
+{
+ if (VOID_TYPE_P (TREE_TYPE (expr)))
+ return;
+ while (TREE_CODE (expr) == COMPOUND_EXPR)
+ {
+ expr = TREE_OPERAND (expr, 1);
+ if (EXPR_HAS_LOCATION (expr))
+ loc = EXPR_LOCATION (expr);
+ }
+ if (TREE_CODE (expr) != CALL_EXPR)
+ return;
+ tree fn = CALL_EXPR_FN (expr);
+ if (!fn)
+ return;
+ tree attr;
+ if (TREE_CODE (fn) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
+ && (attr = lookup_attribute ("nodiscard",
+ DECL_ATTRIBUTES (TREE_OPERAND (fn, 0)))))
+ {
+ fn = TREE_OPERAND (fn, 0);
+ tree args = TREE_VALUE (attr);
+ if (args)
+ args = TREE_VALUE (args);
+ auto_diagnostic_group d;
+ int warned;
+ if (args)
+ warned = warning_at (loc, OPT_Wunused_result,
+ "ignoring return value of %qD, declared with "
+ "attribute %<nodiscard%>: %E", fn, args);
+ else
+ warned = warning_at (loc, OPT_Wunused_result,
+ "ignoring return value of %qD, declared with "
+ "attribute %<nodiscard%>", fn);
+ if (warned)
+ inform (DECL_SOURCE_LOCATION (fn), "declared here");
+ }
+ else
+ {
+ tree rettype = TREE_TYPE (TREE_TYPE (TREE_TYPE (fn)));
+ attr = lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype));
+ if (!attr)
+ return;
+ tree args = TREE_VALUE (attr);
+ if (args)
+ args = TREE_VALUE (args);
+ auto_diagnostic_group d;
+ int warned;
+ if (args)
+ warned = warning_at (loc, OPT_Wunused_result,
+ "ignoring return value of type %qT, declared "
+ "with attribute %<nodiscard%>: %E",
+ rettype, args);
+ else
+ warned = warning_at (loc, OPT_Wunused_result,
+ "ignoring return value of type %qT, declared "
+ "with attribute %<nodiscard%>", rettype);
+ if (warned)
+ {
+ if (TREE_CODE (fn) == ADDR_EXPR)
+ {
+ fn = TREE_OPERAND (fn, 0);
+ if (TREE_CODE (fn) == FUNCTION_DECL)
+ inform (DECL_SOURCE_LOCATION (fn),
+ "in call to %qD, declared here", fn);
+ }
+ }
+ }
+}
+
/* Return a compound expression that performs two expressions and
returns the value of the second of them.
else if (warn_unused_value)
warn_if_unused_value (expr1, loc);
+ maybe_warn_nodiscard (loc, expr1);
+
if (expr2 == error_mark_node)
return error_mark_node;
static void
emit_side_effect_warnings (location_t loc, tree expr)
{
+ maybe_warn_nodiscard (loc, expr);
+ if (!warn_unused_value)
+ return;
if (expr == error_mark_node)
;
else if (!TREE_SIDE_EFFECTS (expr))
Warnings for statement expressions will be emitted later, once we figure
out which is the result. */
if (!STATEMENT_LIST_STMT_EXPR (cur_stmt_list)
- && warn_unused_value)
+ && (warn_unused_value || warn_unused_result))
emit_side_effect_warnings (EXPR_LOC_OR_LOC (expr, loc), expr);
exprv = expr;
/* If we're supposed to generate side effects warnings, process
all of the statements except the last. */
- if (warn_unused_value)
+ if (warn_unused_value || warn_unused_result)
{
for (tree_stmt_iterator i = tsi_start (last);
tsi_stmt (i) != tsi_stmt (l); tsi_next (&i))
--- /dev/null
+/* Test C2x deprecated attribute: valid uses. */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+[[nodiscard]] int c1 (void); /* { dg-message "declared here" } */
+[[__nodiscard__ ("some reason")]] int c2 (void); /* { dg-message "declared here" } */
+
+struct [[nodiscard ("struct reason")]] s1 { int a; };
+struct [[__nodiscard__]] s2 { long b; };
+struct s1 cs1 (void); /* { dg-message "declared here" } */
+struct s2 cs2 (void); /* { dg-message "declared here" } */
+typedef struct s2 s2t;
+s2t cs3 (void); /* { dg-message "declared here" } */
+
+union [[nodiscard]] u1 { int a; long b; };
+union [[nodiscard ("union reason")]] u2 { short c; float d; };
+union u1 cu1 (void); /* { dg-message "declared here" } */
+union u2 cu2 (void); /* { dg-message "declared here" } */
+
+enum [[nodiscard]] e1 { E1 };
+enum [[nodiscard ("enum reason")]] e2 { E2 };
+enum e1 ce1 (void); /* { dg-message "declared here" } */
+enum e2 ce2 (void); /* { dg-message "declared here" } */
+enum e1 ce1a (void);
+int i;
+
+[[nodiscard]] void v (void); /* { dg-warning "void return type" } */
+
+int ok (void);
+
+void
+f (void)
+{
+ c1 (); /* { dg-warning "ignoring return value" } */
+ c2 (); /* { dg-warning "some reason" } */
+ cs1 (); /* { dg-warning "struct reason" } */
+ cs2 (); /* { dg-warning "ignoring return value of type" } */
+ cs3 (); /* { dg-warning "ignoring return value of type" } */
+ cu1 (); /* { dg-warning "ignoring return value of type" } */
+ cu2 (); /* { dg-warning "union reason" } */
+ ce1 (); /* { dg-warning "ignoring return value of type" } */
+ ce2 (); /* { dg-warning "enum reason" } */
+ ok ();
+ c1 (), ok (); /* { dg-warning "ignoring return value" } */
+ cs1 (), ok (); /* { dg-warning "struct reason" } */
+ ok (), cu1 (); /* { dg-warning "ignoring return value" } */
+ ok (), (ok (), (ok (), ce2 ())); /* { dg-warning "enum reason" } */
+ (ok (), cu1 ()), ok (); /* { dg-warning "ignoring return value" } */
+ v ();
+ (i ? ce1 : ce1a) (); /* { dg-warning "ignoring return value of type" } */
+ (void) c1 ();
+ (void) c2 ();
+ (void) cs1 ();
+ (void) cs2 ();
+ (void) cs3 ();
+ (void) cu1 ();
+ (void) cu2 ();
+ (void) ce1 ();
+ (void) ce2 ();
+ (void) (ok (), cu1 ());
+ (void) (i ? ce1 : ce1a) ();
+}
--- /dev/null
+/* Test C2x nodiscard attribute: invalid contexts. */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+/* This attribute is not valid on types other than their definitions,
+ or on declarations other than function declarations, or on
+ statements, or as an attribute-declaration. */
+
+[[nodiscard]]; /* { dg-error "ignored" } */
+
+int [[nodiscard]] var; /* { dg-error "ignored" } */
+
+int [[nodiscard ("reason")]] var2; /* { dg-error "ignored" } */
+
+int array_with_nod_type[2] [[nodiscard]]; /* { dg-error "ignored" } */
+
+void fn_with_nod_type () [[nodiscard]]; /* { dg-error "ignored" } */
+
+int z = sizeof (int [[__nodiscard__]]); /* { dg-error "ignored" } */
+
+[[nodiscard]] typedef int nod_int; /* { dg-error "can only be applied" } */
+
+[[nodiscard]] int nvar; /* { dg-error "can only be applied" } */
+
+struct s { int a; };
+
+[[nodiscard]] typedef struct s nod_s; /* { dg-error "can only be applied" } */
+
+struct t { [[nodiscard]] int b; }; /* { dg-error "can only be applied" } */
+
+enum e { E [[nodiscard]] }; /* { dg-error "can only be applied" } */
+
+void fx ([[nodiscard]] int p); /* { dg-error "can only be applied" } */
+
+void
+f (void)
+{
+ int a;
+ [[nodiscard ("reason")]] int b = 1; /* { dg-error "can only be applied" } */
+ [[nodiscard]]; /* { dg-error "ignored" } */
+ [[nodiscard]] a = 1; /* { dg-error "ignored" } */
+}
--- /dev/null
+/* Test C2x nodiscard attribute: invalid syntax. */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+[[nodiscard()]] int a (void); /* { dg-error "parentheses must be omitted if attribute argument list is empty" } */
+
+[[nodiscard(0)]] int b (void); /* { dg-error "expected" } */
+
+[[nodiscard("", 123)]] int c (void); /* { dg-error "expected" } */
+
+[[nodiscard((""))]] int d (void); /* { dg-error "expected" } */
--- /dev/null
+/* Test C2x nodiscard attribute: duplicates (allowed after N2557). */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+[[nodiscard, __nodiscard__]] int f (void);
+[[__nodiscard__, nodiscard("message")]] int g (void);
[[unknown_attribute]] x: var = 2; /* { dg-error "attribute ignored" } */
for ([[unknown_attribute]] int zz = 1; zz < 10; zz++) ; /* { dg-error "attribute ignored" } */
}
-
-/* nodiscard is not yet implemented, but is a standard attribute, so
- its use is not a constraint violation and should only receive a
- warning. */
-[[nodiscard]] int ndfunc (void); /* { dg-warning "attribute directive ignored" } */