}
/* Verify that argument value POS at position ARGNO to attribute NAME
- applied to function TYPE refers to a function parameter at position
- POS and the expected type CODE. Treat CODE == INTEGER_TYPE as
- matching all C integral types except bool. If successful, return
- POS after default conversions, if any. Otherwise, issue appropriate
- warnings and return null. A non-zero 1-based ARGNO should be passed
- in by callers only for attributes with more than one argument. */
+ applied to function FN (which is either a function declaration or function
+ type) refers to a function parameter at position POS and the expected type
+ CODE. Treat CODE == INTEGER_TYPE as matching all C integral types except
+ bool. If successful, return POS after default conversions (and possibly
+ adjusted by ADJUST_POS). Otherwise, issue appropriate warnings and return
+ null. A non-zero 1-based ARGNO should be passed in by callers only for
+ attributes with more than one argument.
+
+ N.B. This function modifies POS. */
tree
-positional_argument (const_tree fntype, const_tree atname, tree pos,
+positional_argument (const_tree fn, const_tree atname, tree &pos,
tree_code code, int argno /* = 0 */,
int flags /* = posargflags () */)
{
+ const_tree fndecl = TYPE_P (fn) ? NULL_TREE : fn;
+ const_tree fntype = TYPE_P (fn) ? fn : TREE_TYPE (fn);
if (pos && TREE_CODE (pos) != IDENTIFIER_NODE
&& TREE_CODE (pos) != FUNCTION_DECL)
pos = default_conversion (pos);
if (!prototype_p (fntype))
return pos;
+ /* ADJUST_POS is non-zero in C++ when the function type has invisible
+ parameters generated by the compiler, such as the in-charge or VTT
+ parameters. */
+ const int adjust_pos = maybe_adjust_arg_pos_for_attribute (fndecl);
+
/* Verify that the argument position does not exceed the number
of formal arguments to the function. When POSARG_ELLIPSIS
is set, ARGNO may be beyond the last argument of a vararg
if (!nargs
|| !tree_fits_uhwi_p (pos)
|| ((flags & POSARG_ELLIPSIS) == 0
- && !IN_RANGE (tree_to_uhwi (pos), 1, nargs)))
+ && !IN_RANGE (tree_to_uhwi (pos) + adjust_pos, 1, nargs)))
{
if (argno < 1)
}
/* Verify that the type of the referenced formal argument matches
- the expected type. */
- unsigned HOST_WIDE_INT ipos = tree_to_uhwi (pos);
+ the expected type. Invisible parameters may have been added by
+ the compiler, so adjust the position accordingly. */
+ unsigned HOST_WIDE_INT ipos = tree_to_uhwi (pos) + adjust_pos;
/* Zero was handled above. */
gcc_assert (ipos != 0);
return NULL_TREE;
}
- return pos;
+ return build_int_cst (TREE_TYPE (pos), ipos);
}
/* Return the first of DECL or TYPE attributes installed in NODE if it's
/* Check for errors in format strings. */
if (warn_format || warn_suggest_attribute_format)
- check_function_format (fntype, TYPE_ATTRIBUTES (fntype), nargs, argarray,
- arglocs);
+ check_function_format (fndecl ? fndecl : fntype, TYPE_ATTRIBUTES (fntype), nargs,
+ argarray, arglocs);
if (warn_format)
check_function_sentinel (fntype, nargs, argarray);
extern tree lookup_label (tree);
extern tree lookup_name (tree);
extern bool lvalue_p (const_tree);
+extern int maybe_adjust_arg_pos_for_attribute (const_tree);
extern bool vector_targets_convertible_p (const_tree t1, const_tree t2);
extern bool vector_types_convertible_p (const_tree t1, const_tree t2, bool emit_lax_note);
POSARG_ELLIPSIS = 2
};
-extern tree positional_argument (const_tree, const_tree, tree, tree_code,
+extern tree positional_argument (const_tree, const_tree, tree &, tree_code,
int = 0, int = posargflags ());
extern enum flt_eval_method
unsigned HOST_WIDE_INT format_num,
int flags, bool *no_add_attrs,
int expected_format_type);
-static tree get_constant (const_tree fntype, const_tree atname, tree expr,
- int argno, unsigned HOST_WIDE_INT *value,
- int flags, bool validated_p);
+static bool validate_constant (const_tree fn, const_tree atname, tree &expr,
+ int argno, unsigned HOST_WIDE_INT *value,
+ int flags, bool validated_p);
static const char *convert_format_name_to_system_name (const char *attr_name);
static int first_target_format_type;
tree args, int flags, bool *no_add_attrs)
{
tree type = *node;
- /* Note that TREE_VALUE (args) is changed in place below. */
+ /* Note that TREE_VALUE (args) is changed in the validate_constant call. */
tree *format_num_expr = &TREE_VALUE (args);
unsigned HOST_WIDE_INT format_num = 0;
- if (tree val = get_constant (type, atname, *format_num_expr, 0, &format_num,
- 0, false))
- *format_num_expr = val;
- else
+ if (!validate_constant (type, atname, *format_num_expr, 0, &format_num, 0,
+ false))
{
*no_add_attrs = true;
return NULL_TREE;
/* Under the control of FLAGS, verify EXPR is a valid constant that
refers to a positional argument ARGNO having a string type (char*
or, for targets like Darwin, a pointer to struct CFString) to
- a function type FNTYPE declared with attribute ATNAME.
- If valid, store the constant's integer value in *VALUE and return
- the value.
- If VALIDATED_P is true assert the validation is successful.
- Returns the converted constant value on success, null otherwise. */
+ a function FN declared with attribute ATNAME. If valid, store the
+ constant's integer value in *VALUE and return true. If VALIDATED_P
+ is true assert the validation is successful.
-static tree
-get_constant (const_tree fntype, const_tree atname, tree expr, int argno,
- unsigned HOST_WIDE_INT *value, int flags, bool validated_p)
+ N.B. This function modifies EXPR. */
+
+static bool
+validate_constant (const_tree fn, const_tree atname, tree &expr, int argno,
+ unsigned HOST_WIDE_INT *value, int flags, bool validated_p)
{
/* Require the referenced argument to have a string type. For targets
like Darwin, also accept pointers to struct CFString. */
- if (tree val = positional_argument (fntype, atname, expr, STRING_CST,
+ if (tree val = positional_argument (fn, atname, expr, STRING_CST,
argno, flags))
{
*value = TREE_INT_CST_LOW (val);
- return val;
+ return true;
}
gcc_assert (!validated_p);
- return NULL_TREE;
+ return false;
}
/* Decode the arguments to a "format" attribute into a
function_format_info structure. It is already known that the list
is of the right length. If VALIDATED_P is true, then these
attributes have already been validated and must not be erroneous;
- if false, it will give an error message. Returns true if the
- attributes are successfully decoded, false otherwise. */
+ if false, it will give an error message. FN is either a function
+ declaration or function type. Returns true if the attributes are
+ successfully decoded, false otherwise. */
static bool
-decode_format_attr (const_tree fntype, tree atname, tree args,
+decode_format_attr (const_tree fn, tree atname, tree args,
function_format_info *info, bool validated_p)
{
tree format_type_id = TREE_VALUE (args);
}
}
- if (tree val = get_constant (fntype, atname, *format_num_expr,
- 2, &info->format_num, 0, validated_p))
- *format_num_expr = val;
- else
+ if (!validate_constant (fn, atname, *format_num_expr, 2, &info->format_num,
+ 0, validated_p))
return false;
- if (tree val = get_constant (fntype, atname, *first_arg_num_expr,
- 3, &info->first_arg_num,
- (POSARG_ZERO | POSARG_ELLIPSIS), validated_p))
- *first_arg_num_expr = val;
- else
+ if (!validate_constant (fn, atname, *first_arg_num_expr, 3,
+ &info->first_arg_num,
+ (POSARG_ZERO | POSARG_ELLIPSIS), validated_p))
return false;
if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num)
\f
/* Check the argument list of a call to printf, scanf, etc.
ATTRS are the attributes on the function type. There are NARGS argument
- values in the array ARGARRAY.
- Also, if -Wsuggest-attribute=format,
- warn for calls to vprintf or vscanf in functions with no such format
- attribute themselves. */
+ values in the array ARGARRAY. FN is either a function declaration or
+ function type. Also, if -Wsuggest-attribute=format, warn for calls to
+ vprintf or vscanf in functions with no such format attribute themselves. */
void
-check_function_format (const_tree fntype, tree attrs, int nargs,
+check_function_format (const_tree fn, tree attrs, int nargs,
tree *argarray, vec<location_t> *arglocs)
{
tree a;
{
/* Yup; check it. */
function_format_info info;
- decode_format_attr (fntype, atname, TREE_VALUE (a), &info,
+ decode_format_attr (fn, atname, TREE_VALUE (a), &info,
/*validated=*/true);
if (warn_format)
{
/* Handle a "format" attribute; arguments as in
struct attribute_spec.handler. */
tree
-handle_format_attribute (tree *node, tree atname, tree args,
+handle_format_attribute (tree node[3], tree atname, tree args,
int flags, bool *no_add_attrs)
{
const_tree type = *node;
+ /* NODE[2] may be NULL, and it also may be a PARM_DECL for function
+ pointers. */
+ const_tree fndecl = ((node[2] && TREE_CODE (node[2]) == FUNCTION_DECL)
+ ? node[2] : NULL_TREE);
function_format_info info;
#ifdef TARGET_FORMAT_TYPES
if (TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE)
TREE_VALUE (args) = canonicalize_attr_name (TREE_VALUE (args));
- if (!decode_format_attr (type, atname, args, &info, /* validated_p = */false))
+ if (!decode_format_attr (fndecl ? fndecl : type, atname, args, &info,
+ /* validated_p = */false))
{
*no_add_attrs = true;
return NULL_TREE;
return c_common_get_alias_set (t);
}
+
+/* In C there are no invisible parameters like in C++ (this, in-charge, VTT,
+ etc.). */
+
+int
+maybe_adjust_arg_pos_for_attribute (const_tree)
+{
+ return 0;
+}
}
return false;
}
+
+/* FNDECL is a function declaration whose type may have been altered by
+ adding extra parameters such as this, in-charge, or VTT. When this
+ takes place, the positional arguments supplied by the user (as in the
+ 'format' attribute arguments) may refer to the wrong argument. This
+ function returns an integer indicating how many arguments should be
+ skipped. */
+
+int
+maybe_adjust_arg_pos_for_attribute (const_tree fndecl)
+{
+ if (!fndecl)
+ return 0;
+ int n = num_artificial_parms_for (fndecl);
+ /* The manual states that it's the user's responsibility to account
+ for the implicit this parameter. */
+ return n > 0 ? n - 1 : 0;
+}
+
\f
/* Release memory we no longer need after parsing. */
void
--- /dev/null
+// PR c++/101833
+// { dg-do compile }
+// { dg-options "-Wall" }
+
+struct B { };
+
+struct V : virtual B {
+ const char *fmt (int, const char *) __attribute__((format_arg(3)));
+};
+
+struct D : B {
+ const char *fmt (int, const char *) __attribute__((format_arg(3)));
+};
+
+extern void fmt (const char *, ...) __attribute__((format(printf, 1, 2)));
+
+void
+g ()
+{
+ V v;
+ fmt (v.fmt (1, "%d"), 1);
+ fmt (v.fmt (1, "%d"), 1lu); // { dg-warning "expects argument of type" }
+ D d;
+ fmt (d.fmt (1, "%d"), 1);
+ fmt (d.fmt (1, "%d"), 1lu); // { dg-warning "expects argument of type" }
+}
--- /dev/null
+// PR c++/47634
+// { dg-do compile }
+
+class Base {
+public:
+ Base() { }
+};
+
+class VDerived : public virtual Base {
+public:
+ VDerived(int x, const char * f, ...) __attribute__((format(printf, 3, 4)));
+};
+
+class Derived : public Base {
+public:
+ Derived(int x, const char * f, ...) __attribute__((format(printf, 3, 4)));
+};
+
+VDerived::VDerived(int, const char *, ...)
+{
+}
+
+Derived::Derived(int, const char *, ...)
+{
+}
+
+int
+main(int, char **)
+{
+ throw VDerived(1, "%s %d", "foo", 1);
+ throw Derived(1, "%s %d", "bar", 1);
+}
--- /dev/null
+// PR c++/101833
+// { dg-do compile }
+// { dg-options "-Wall" }
+
+struct B { };
+
+struct V : virtual B {
+ V(int, const char *, ...) __attribute__((format(printf, 3, 4)));
+};
+
+struct D : B {
+ D(int, const char *, ...) __attribute__((format(printf, 3, 4)));
+};
+
+struct D2 : B {
+ template<typename T>
+ D2(T, const char *, ...) __attribute__((format(printf, 3, 4)));
+};
+
+struct V2 : virtual B {
+ template<typename T>
+ V2(T, const char *, ...) __attribute__((format(printf, 3, 4)));
+};
+
+struct X {
+ template<typename T>
+ X(T, ...) __attribute__((format(printf, 2, 3)));
+};
+
+V v(1, "%s %d", "foo", 1);
+D d(1, "%s %d", "foo", 1);
+D2 d2(1, "%s %d", "foo", 1);
+V2 v2(1, "%s %d", "foo", 1);
+
+// Test that it actually works.
+V e1(1, "%d", 1L); // { dg-warning "expects argument of type" }
+D e2(1, "%d", 1L); // { dg-warning "expects argument of type" }
+X e3("%d", 1L); // { dg-warning "expects argument of type" }
--- /dev/null
+// PR c++/101833
+// { dg-do compile }
+// { dg-options "-Wall" }
+
+class Base {};
+
+struct VDerived : virtual Base {
+ VDerived(int, int, const char *, ...) __attribute__((format(printf, 2, 3))); // { dg-error ".format. attribute argument 2 value .2. refers to parameter type .int." }
+ VDerived(int, const char *, ...) __attribute__((format(printf, 5, 6))); // { dg-warning ".format. attribute argument 2 value .5. exceeds" }
+} a(1, "%s %d", "foo", 1);
+
+struct Derived : Base {
+ Derived(int, int, const char *, ...) __attribute__((format(printf, 2, 3))); // { dg-error ".format. attribute argument 2 value .2. refers to parameter type .int." }
+ Derived(int, const char *, ...) __attribute__((format(printf, 5, 6))); // { dg-warning ".format. attribute argument 2 value .5. exceeds" }
+} b(1, "%s %d", "foo", 1);
bool function_type_required;
/* Specifies if attribute affects type's identity. */
bool affects_type_identity;
- /* Function to handle this attribute. NODE points to the node to which
- the attribute is to be applied. If a DECL, it should be modified in
+ /* Function to handle this attribute. NODE points to a tree[3] array,
+ where node[0] is the node to which the attribute is to be applied;
+ node[1] is the last pushed/merged declaration if one exists, and node[2]
+ may be the declaration for node[0]. If a DECL, it should be modified in
place; if a TYPE, a copy should be created. NAME is the canonicalized
name of the attribute i.e. without any leading or trailing underscores.
ARGS is the TREE_LIST of the arguments (which may be NULL). FLAGS gives