@@ -1604,6 +1604,7 @@ OBJS = \
mcf.o \
mode-switching.o \
modulo-sched.o \
+ move-history-rich-location.o \
multiple_target.o \
omp-offload.o \
omp-expand.o \
@@ -17,6 +17,7 @@ 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/>. */
+#define INCLUDE_MEMORY
#include "config.h"
#include "system.h"
#include "coretypes.h"
@@ -31,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-dfa.h"
#include "fold-const.h"
#include "diagnostic-core.h"
+#include "move-history-rich-location.h"
#include "intl.h"
#include "tree-vrp.h"
#include "alloc-pool.h"
@@ -262,6 +264,7 @@ get_up_bounds_for_array_ref (tree ref, tree *decl,
static bool
check_out_of_bounds_and_warn (location_t location, tree ref,
+ gimple *stmt,
tree low_sub_org, tree low_sub, tree up_sub,
tree up_bound, tree up_bound_p1,
const irange *vr,
@@ -275,12 +278,13 @@ check_out_of_bounds_and_warn (location_t location, tree ref,
bool warned = false;
*out_of_bound = false;
+ rich_location_with_details richloc (location, stmt);
/* Empty array. */
if (up_bound && tree_int_cst_equal (low_bound, up_bound_p1))
{
*out_of_bound = true;
if (for_array_bound)
- warned = warning_at (location, OPT_Warray_bounds_,
+ warned = warning_at (&richloc, OPT_Warray_bounds_,
"array subscript %E is outside array"
" bounds of %qT", low_sub_org, artype);
}
@@ -299,7 +303,7 @@ check_out_of_bounds_and_warn (location_t location, tree ref,
{
*out_of_bound = true;
if (for_array_bound)
- warned = warning_at (location, OPT_Warray_bounds_,
+ warned = warning_at (&richloc, OPT_Warray_bounds_,
"array subscript [%E, %E] is outside "
"array bounds of %qT",
low_sub, up_sub, artype);
@@ -313,7 +317,7 @@ check_out_of_bounds_and_warn (location_t location, tree ref,
{
*out_of_bound = true;
if (for_array_bound)
- warned = warning_at (location, OPT_Warray_bounds_,
+ warned = warning_at (&richloc, OPT_Warray_bounds_,
"array subscript %E is above array bounds of %qT",
up_sub, artype);
}
@@ -322,7 +326,7 @@ check_out_of_bounds_and_warn (location_t location, tree ref,
{
*out_of_bound = true;
if (for_array_bound)
- warned = warning_at (location, OPT_Warray_bounds_,
+ warned = warning_at (&richloc, OPT_Warray_bounds_,
"array subscript %E is below array bounds of %qT",
low_sub, artype);
}
@@ -388,15 +392,16 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
}
}
- warned = check_out_of_bounds_and_warn (location, ref,
+ warned = check_out_of_bounds_and_warn (location, ref, stmt,
low_sub_org, low_sub, up_sub,
up_bound, up_bound_p1, &vr,
ignore_off_by_one, warn_array_bounds,
&out_of_bound);
+ rich_location_with_details richloc (location, stmt);
if (!warned && sam == special_array_member::int_0)
- warned = warning_at (location, OPT_Wzero_length_bounds,
+ warned = warning_at (&richloc, OPT_Wzero_length_bounds,
(TREE_CODE (low_sub) == INTEGER_CST
? G_("array subscript %E is outside the bounds "
"of an interior zero-length array %qT")
@@ -420,7 +425,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
&& DECL_NOT_FLEXARRAY (afield_decl))
{
bool warned1
- = warning_at (location, OPT_Wstrict_flex_arrays,
+ = warning_at (&richloc, OPT_Wstrict_flex_arrays,
"trailing array %qT should not be used as "
"a flexible array member",
artype);
@@ -478,6 +483,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
bool
array_bounds_checker::check_mem_ref (location_t location, tree ref,
+ gimple *stmt,
bool ignore_off_by_one)
{
if (warning_suppressed_p (ref, OPT_Warray_bounds_))
@@ -576,16 +582,17 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
}
}
+ rich_location_with_details richloc (location, stmt);
bool warned = false;
if (lboob)
{
if (offrange[0] == offrange[1])
- warned = warning_at (location, OPT_Warray_bounds_,
+ warned = warning_at (&richloc, OPT_Warray_bounds_,
"array subscript %wi is outside array bounds "
"of %qT",
offrange[0].to_shwi (), reftype);
else
- warned = warning_at (location, OPT_Warray_bounds_,
+ warned = warning_at (&richloc, OPT_Warray_bounds_,
"array subscript [%wi, %wi] is outside "
"array bounds of %qT",
offrange[0].to_shwi (),
@@ -599,8 +606,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
it were an untyped array of bytes. */
backtype = build_array_type_nelts (unsigned_char_type_node,
aref.sizrng[1].to_uhwi ());
-
- warned = warning_at (location, OPT_Warray_bounds_,
+ warned = warning_at (&richloc, OPT_Warray_bounds_,
"array subscript %<%T[%wi]%> is partly "
"outside array bounds of %qT",
axstype, offrange[0].to_shwi (), backtype);
@@ -623,7 +629,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
{
HOST_WIDE_INT tmpidx = (aref.offmax[i] / eltsize).to_shwi ();
- if (warning_at (location, OPT_Warray_bounds_,
+ if (warning_at (&richloc, OPT_Warray_bounds_,
"intermediate array offset %wi is outside array bounds "
"of %qT", tmpidx, reftype))
{
@@ -656,7 +662,7 @@ array_bounds_checker::check_addr_expr (location_t location, tree t,
ignore_off_by_one = false;
}
else if (TREE_CODE (t) == MEM_REF)
- warned = check_mem_ref (location, t, ignore_off_by_one);
+ warned = check_mem_ref (location, t, stmt, ignore_off_by_one);
if (warned)
suppress_warning (t, OPT_Warray_bounds_);
@@ -692,6 +698,7 @@ array_bounds_checker::check_addr_expr (location_t location, tree t,
if (!mem_ref_offset (t).is_constant (&idx))
return;
+ rich_location_with_details richloc (location, stmt);
bool warned = false;
idx = wi::sdiv_trunc (idx, wi::to_offset (el_sz));
if (idx < 0)
@@ -702,7 +709,7 @@ array_bounds_checker::check_addr_expr (location_t location, tree t,
dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
fprintf (dump_file, "\n");
}
- warned = warning_at (location, OPT_Warray_bounds_,
+ warned = warning_at (&richloc, OPT_Warray_bounds_,
"array subscript %wi is below "
"array bounds of %qT",
idx.to_shwi (), TREE_TYPE (tem));
@@ -716,7 +723,7 @@ array_bounds_checker::check_addr_expr (location_t location, tree t,
dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
fprintf (dump_file, "\n");
}
- warned = warning_at (location, OPT_Warray_bounds_,
+ warned = warning_at (&richloc, OPT_Warray_bounds_,
"array subscript %wu is above "
"array bounds of %qT",
idx.to_uhwi (), TREE_TYPE (tem));
@@ -811,7 +818,7 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
warned = checker->check_array_ref (location, t, wi->stmt,
false/*ignore_off_by_one*/);
else if (TREE_CODE (t) == MEM_REF)
- warned = checker->check_mem_ref (location, t,
+ warned = checker->check_mem_ref (location, t, wi->stmt,
false /*ignore_off_by_one*/);
else if (TREE_CODE (t) == ADDR_EXPR)
{
@@ -33,7 +33,7 @@ public:
private:
static tree check_array_bounds (tree *tp, int *walk_subtree, void *data);
bool check_array_ref (location_t, tree, gimple *, bool ignore_off_by_one);
- bool check_mem_ref (location_t, tree, bool ignore_off_by_one);
+ bool check_mem_ref (location_t, tree, gimple *, bool ignore_off_by_one);
void check_addr_expr (location_t, tree, gimple *);
void get_value_range (irange &r, const_tree op, gimple *);
@@ -57,6 +57,7 @@
#include "attr-fnspec.h"
#include "pointer-query.h"
#include "pretty-print-markup.h"
+#include "move-history-rich-location.h"
/* Return true if tree node X has an associated location. */
@@ -169,17 +170,19 @@ warn_string_no_nul (location_t loc, GimpleOrTree expr, const char *fname,
if (expr)
{
tree func = get_callee_fndecl (expr);
+ rich_location_with_details richloc (loc, expr);
+
if (bndrng)
{
if (wi::ltu_p (maxsiz, bndrng[0]))
- warned = warning_at (loc, opt,
+ warned = warning_at (&richloc, opt,
"%qD specified bound %s exceeds "
"maximum object size %E",
func, bndstr, maxobjsize);
else
{
bool maybe = wi::to_wide (size) == bndrng[0];
- warned = warning_at (loc, opt,
+ warned = warning_at (&richloc, opt,
exact
? G_("%qD specified bound %s exceeds "
"the size %E of unterminated array")
@@ -194,7 +197,7 @@ warn_string_no_nul (location_t loc, GimpleOrTree expr, const char *fname,
}
}
else
- warned = warning_at (loc, opt,
+ warned = warning_at (&richloc, opt,
"%qD argument missing terminating nul",
func);
}
@@ -486,14 +489,16 @@ maybe_warn_nonstring_arg (tree fndecl, GimpleOrTree exp)
tree maxobjsize = max_object_size ();
if (tree_int_cst_lt (maxobjsize, bndrng[0]))
{
+ rich_location_with_details richloc (loc, exp);
+
bool warned = false;
if (tree_int_cst_equal (bndrng[0], bndrng[1]))
- warned = warning_at (loc, OPT_Wstringop_overread,
+ warned = warning_at (&richloc, OPT_Wstringop_overread,
"%qD specified bound %E "
"exceeds maximum object size %E",
fndecl, bndrng[0], maxobjsize);
else
- warned = warning_at (loc, OPT_Wstringop_overread,
+ warned = warning_at (&richloc, OPT_Wstringop_overread,
"%qD specified bound [%E, %E] "
"exceeds maximum object size %E",
fndecl, bndrng[0], bndrng[1],
@@ -641,20 +646,21 @@ maybe_warn_nonstring_arg (tree fndecl, GimpleOrTree exp)
auto_diagnostic_group d;
if (wi::ltu_p (asize, wibnd))
{
+ rich_location_with_details richloc (loc, exp);
if (bndrng[0] == bndrng[1])
- warned = warning_at (loc, OPT_Wstringop_overread,
+ warned = warning_at (&richloc, OPT_Wstringop_overread,
"%qD argument %i declared attribute "
"%<nonstring%> is smaller than the specified "
"bound %wu",
fndecl, argno + 1, wibnd.to_uhwi ());
else if (wi::ltu_p (asize, wi::to_offset (bndrng[0])))
- warned = warning_at (loc, OPT_Wstringop_overread,
+ warned = warning_at (&richloc, OPT_Wstringop_overread,
"%qD argument %i declared attribute "
"%<nonstring%> is smaller than "
"the specified bound [%E, %E]",
fndecl, argno + 1, bndrng[0], bndrng[1]);
else
- warned = warning_at (loc, OPT_Wstringop_overread,
+ warned = warning_at (&richloc, OPT_Wstringop_overread,
"%qD argument %i declared attribute "
"%<nonstring%> may be smaller than "
"the specified bound [%E, %E]",
@@ -726,16 +732,17 @@ maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
auto_diagnostic_group d;
if (tree_int_cst_lt (maxobjsize, bndrng[0]))
{
+ rich_location_with_details richloc (loc, exp);
if (bndrng[0] == bndrng[1])
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD specified bound %E may "
"exceed maximum object size %E")
: G_("%qD specified bound %E "
"exceeds maximum object size %E")),
func, bndrng[0], maxobjsize)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("specified bound %E may "
"exceed maximum object size %E")
@@ -744,7 +751,7 @@ maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
bndrng[0], maxobjsize));
else
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD specified bound [%E, %E] may "
"exceed maximum object size %E")
@@ -752,7 +759,7 @@ maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
"exceeds maximum object size %E")),
func,
bndrng[0], bndrng[1], maxobjsize)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("specified bound [%E, %E] may "
"exceed maximum object size %E")
@@ -763,37 +770,43 @@ maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
else if (!size || tree_int_cst_le (bndrng[0], size))
return false;
else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
- warned = (func
- ? warning_at (loc, opt,
+ {
+ rich_location_with_details richloc (loc, exp);
+ warned = (func
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD specified bound %E may exceed "
"source size %E")
: G_("%qD specified bound %E exceeds "
"source size %E")),
func, bndrng[0], size)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("specified bound %E may exceed "
"source size %E")
: G_("specified bound %E exceeds "
"source size %E")),
bndrng[0], size));
+ }
else
- warned = (func
- ? warning_at (loc, opt,
+ {
+ rich_location_with_details richloc (loc, exp);
+ warned = (func
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD specified bound [%E, %E] may "
"exceed source size %E")
: G_("%qD specified bound [%E, %E] exceeds "
"source size %E")),
func, bndrng[0], bndrng[1], size)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("specified bound [%E, %E] may exceed "
"source size %E")
: G_("specified bound [%E, %E] exceeds "
"source size %E")),
bndrng[0], bndrng[1], size));
+ }
if (warned)
{
if (pad && pad->src.ref
@@ -807,6 +820,7 @@ maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
}
bool maybe = pad && pad->dst.phi ();
+ rich_location_with_details richloc (loc, exp);
if (maybe)
{
/* Issue a "maybe" warning only if the PHI refers to objects
@@ -820,14 +834,14 @@ maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
{
if (bndrng[0] == bndrng[1])
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD specified size %E may "
"exceed maximum object size %E")
: G_("%qD specified size %E "
"exceeds maximum object size %E")),
func, bndrng[0], maxobjsize)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("specified size %E may exceed "
"maximum object size %E")
@@ -836,14 +850,14 @@ maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
bndrng[0], maxobjsize));
else
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD specified size between %E and %E "
"may exceed maximum object size %E")
: G_("%qD specified size between %E and %E "
"exceeds maximum object size %E")),
func, bndrng[0], bndrng[1], maxobjsize)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("specified size between %E and %E "
"may exceed maximum object size %E")
@@ -855,14 +869,14 @@ maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
return false;
else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD specified bound %E may exceed "
"destination size %E")
: G_("%qD specified bound %E exceeds "
"destination size %E")),
func, bndrng[0], size)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("specified bound %E may exceed "
"destination size %E")
@@ -871,14 +885,14 @@ maybe_warn_for_bound (opt_code opt, location_t loc, GimpleOrTree exp, tree func,
bndrng[0], size));
else
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD specified bound [%E, %E] may exceed "
"destination size %E")
: G_("%qD specified bound [%E, %E] exceeds "
"destination size %E")),
func, bndrng[0], bndrng[1], size)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("specified bound [%E, %E] exceeds "
"destination size %E")
@@ -929,11 +943,13 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
{
bool warned = false;
+ rich_location_with_details richloc (loc, exp);
+
if (write && read)
{
if (tree_int_cst_equal (range[0], range[1]))
warned = (func
- ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+ ? warning_n (&richloc, opt, tree_to_uhwi (range[0]),
(maybe
? G_("%qD may access %E byte in a region "
"of size %E")
@@ -945,7 +961,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
: G_ ("%qD accessing %E bytes in a region "
"of size %E")),
func, range[0], size)
- : warning_n (loc, opt, tree_to_uhwi (range[0]),
+ : warning_n (&richloc, opt, tree_to_uhwi (range[0]),
(maybe
? G_("may access %E byte in a region "
"of size %E")
@@ -961,14 +977,14 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
{
/* Avoid printing the upper bound if it's invalid. */
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD may access %E or more bytes "
"in a region of size %E")
: G_("%qD accessing %E or more bytes "
"in a region of size %E")),
func, range[0], size)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("may access %E or more bytes "
"in a region of size %E")
@@ -978,14 +994,14 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
}
else
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD may access between %E and %E "
"bytes in a region of size %E")
: G_("%qD accessing between %E and %E "
"bytes in a region of size %E")),
func, range[0], range[1], size)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("may access between %E and %E bytes "
"in a region of size %E")
@@ -999,7 +1015,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
{
if (tree_int_cst_equal (range[0], range[1]))
warned = (func
- ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+ ? warning_n (&richloc, opt, tree_to_uhwi (range[0]),
(maybe
? G_("%qD may write %E byte into a region "
"of size %E")
@@ -1011,7 +1027,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
: G_("%qD writing %E bytes into a region "
"of size %E overflows the destination")),
func, range[0], size)
- : warning_n (loc, opt, tree_to_uhwi (range[0]),
+ : warning_n (&richloc, opt, tree_to_uhwi (range[0]),
(maybe
? G_("may write %E byte into a region "
"of size %E")
@@ -1027,7 +1043,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
{
/* Avoid printing the upper bound if it's invalid. */
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD may write %E or more bytes "
"into a region of size %E")
@@ -1035,7 +1051,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
"into a region of size %E overflows "
"the destination")),
func, range[0], size)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("may write %E or more bytes into "
"a region of size %E")
@@ -1046,7 +1062,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
}
else
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (&richloc, opt,
(maybe
? G_("%qD may write between %E and %E bytes "
"into a region of size %E")
@@ -1054,7 +1070,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
"into a region of size %E overflows "
"the destination")),
func, range[0], range[1], size)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("may write between %E and %E bytes "
"into a region of size %E")
@@ -1069,7 +1085,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
{
if (tree_int_cst_equal (range[0], range[1]))
warned = (func
- ? warning_n (loc, OPT_Wstringop_overread,
+ ? warning_n (&richloc, OPT_Wstringop_overread,
tree_to_uhwi (range[0]),
(maybe
? G_("%qD may read %E byte from a region "
@@ -1082,7 +1098,7 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
: G_("%qD reading %E bytes from a region "
"of size %E")),
func, range[0], size)
- : warning_n (loc, OPT_Wstringop_overread,
+ : warning_n (&richloc, OPT_Wstringop_overread,
tree_to_uhwi (range[0]),
(maybe
? G_("may read %E byte from a region "
@@ -1099,14 +1115,14 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
{
/* Avoid printing the upper bound if it's invalid. */
warned = (func
- ? warning_at (loc, OPT_Wstringop_overread,
+ ? warning_at (&richloc, OPT_Wstringop_overread,
(maybe
? G_("%qD may read %E or more bytes "
"from a region of size %E")
: G_("%qD reading %E or more bytes "
"from a region of size %E")),
func, range[0], size)
- : warning_at (loc, OPT_Wstringop_overread,
+ : warning_at (&richloc, OPT_Wstringop_overread,
(maybe
? G_("may read %E or more bytes "
"from a region of size %E")
@@ -1116,14 +1132,14 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
}
else
warned = (func
- ? warning_at (loc, OPT_Wstringop_overread,
+ ? warning_at (&richloc, OPT_Wstringop_overread,
(maybe
? G_("%qD may read between %E and %E bytes "
"from a region of size %E")
: G_("%qD reading between %E and %E bytes "
"from a region of size %E")),
func, range[0], range[1], size)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
(maybe
? G_("may read between %E and %E bytes "
"from a region of size %E")
@@ -1140,12 +1156,12 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
if (tree_int_cst_equal (range[0], range[1])
|| tree_int_cst_sign_bit (range[1]))
warned = (func
- ? warning_n (loc, OPT_Wstringop_overread,
+ ? warning_n (&richloc, OPT_Wstringop_overread,
tree_to_uhwi (range[0]),
"%qD expecting %E byte in a region of size %E",
"%qD expecting %E bytes in a region of size %E",
func, range[0], size)
- : warning_n (loc, OPT_Wstringop_overread,
+ : warning_n (&richloc, OPT_Wstringop_overread,
tree_to_uhwi (range[0]),
"expecting %E byte in a region of size %E",
"expecting %E bytes in a region of size %E",
@@ -1154,22 +1170,22 @@ warn_for_access (location_t loc, tree func, GimpleOrTree exp, int opt,
{
/* Avoid printing the upper bound if it's invalid. */
warned = (func
- ? warning_at (loc, OPT_Wstringop_overread,
+ ? warning_at (&richloc, OPT_Wstringop_overread,
"%qD expecting %E or more bytes in a region "
"of size %E",
func, range[0], size)
- : warning_at (loc, OPT_Wstringop_overread,
+ : warning_at (&richloc, OPT_Wstringop_overread,
"expecting %E or more bytes in a region "
"of size %E",
range[0], size));
}
else
warned = (func
- ? warning_at (loc, OPT_Wstringop_overread,
+ ? warning_at (&richloc, OPT_Wstringop_overread,
"%qD expecting between %E and %E bytes in "
"a region of size %E",
func, range[0], range[1], size)
- : warning_at (loc, OPT_Wstringop_overread,
+ : warning_at (&richloc, OPT_Wstringop_overread,
"expecting between %E and %E bytes in "
"a region of size %E",
range[0], range[1], size));
@@ -1400,6 +1416,8 @@ check_access (GimpleOrTree exp, tree dstwrite,
auto_diagnostic_group d;
location_t loc = get_location (exp);
+ rich_location_with_details richloc (loc, exp);
+
bool warned = false;
if (dstwrite == slen && at_least_one)
{
@@ -1407,12 +1425,12 @@ check_access (GimpleOrTree exp, tree dstwrite,
and a source of unknown length. The call will write
at least one byte past the end of the destination. */
warned = (func
- ? warning_at (loc, opt,
+ ? warning_at (&richloc, opt,
"%qD writing %E or more bytes into "
"a region of size %E overflows "
"the destination",
func, range[0], dstsize)
- : warning_at (loc, opt,
+ : warning_at (&richloc, opt,
"writing %E or more bytes into "
"a region of size %E overflows "
"the destination",
@@ -2566,7 +2584,9 @@ pass_waccess::check_strncat (gcall *stmt)
&& tree_int_cst_equal (destsize, maxread))
{
location_t loc = get_location (stmt);
- warning_at (loc, OPT_Wstringop_overflow_,
+ rich_location_with_details richloc (loc, stmt);
+
+ warning_at (&richloc, OPT_Wstringop_overflow_,
"%qD specified bound %E equals destination size",
get_callee_fndecl (stmt), maxread);
@@ -3447,13 +3467,14 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
&& tree_int_cst_sgn (sizrng[0]) < 0
&& tree_int_cst_sgn (sizrng[1]) < 0)
{
+ rich_location_with_details richloc (loc, stmt);
/* Warn about negative sizes. */
if (access.second.internal_p)
{
const std::string argtypestr
= access.second.array_as_string (ptrtype);
- if (warning_at (loc, OPT_Wstringop_overflow_,
+ if (warning_at (&richloc, OPT_Wstringop_overflow_,
"bound argument %i value %s is "
"negative for a variable length array "
"argument %i of type %s",
@@ -3461,7 +3482,7 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
ptridx + 1, argtypestr.c_str ()))
arg_warned = OPT_Wstringop_overflow_;
}
- else if (warning_at (loc, OPT_Wstringop_overflow_,
+ else if (warning_at (&richloc, OPT_Wstringop_overflow_,
"argument %i value %s is negative",
sizidx + 1, sizstr))
arg_warned = OPT_Wstringop_overflow_;
@@ -41,6 +41,7 @@
#include "tree-object-size.h"
#include "calls.h"
#include "cfgloop.h"
+#include "move-history-rich-location.h"
#include "intl.h"
#include "gimple-range.h"
@@ -1694,6 +1695,8 @@ maybe_diag_access_bounds (gimple *call, tree func, int strict,
location_t loc = gimple_location (call);
const offset_int maxobjsize = ref.maxobjsize;
+ rich_location_with_details richloc (loc, call);
+
/* Check for excessive size first and regardless of warning options
since the result is used to make codegen decisions. */
if (ref.sizrange[0] > maxobjsize)
@@ -1710,13 +1713,13 @@ maybe_diag_access_bounds (gimple *call, tree func, int strict,
if (warn_stringop_overflow)
{
if (ref.sizrange[0] == ref.sizrange[1])
- warned = warning_at (loc, opt,
+ warned = warning_at (&richloc, opt,
"%qD specified bound %wu "
"exceeds maximum object size %wu",
func, ref.sizrange[0].to_uhwi (),
maxobjsize.to_uhwi ());
else
- warned = warning_at (loc, opt,
+ warned = warning_at (&richloc, opt,
"%qD specified bound between %wu and %wu "
"exceeds maximum object size %wu",
func, ref.sizrange[0].to_uhwi (),
@@ -1777,7 +1780,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int strict,
&& TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
{
auto_diagnostic_group d;
- if (warning_at (loc, opt,
+ if (warning_at (&richloc, opt,
"%qD pointer overflow between offset %s "
"and size %s accessing array %qD with type %qT",
func, rangestr[0], rangestr[1], ref.base, type))
@@ -1787,13 +1790,13 @@ maybe_diag_access_bounds (gimple *call, tree func, int strict,
warned = true;
}
else
- warned = warning_at (loc, opt,
+ warned = warning_at (&richloc, opt,
"%qD pointer overflow between offset %s "
"and size %s",
func, rangestr[0], rangestr[1]);
}
else
- warned = warning_at (loc, opt,
+ warned = warning_at (&richloc, opt,
"%qD pointer overflow between offset %s "
"and size %s",
func, rangestr[0], rangestr[1]);
@@ -1809,7 +1812,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int strict,
{
auto_diagnostic_group d;
if ((ref.basesize < maxobjsize
- && warning_at (loc, opt,
+ && warning_at (&richloc, opt,
form
? G_("%qD forming offset %s is out of "
"the bounds [0, %wu] of object %qD with "
@@ -1818,7 +1821,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int strict,
"[0, %wu] of object %qD with type %qT"),
func, rangestr[0], ref.basesize.to_uhwi (),
ref.base, TREE_TYPE (ref.base)))
- || warning_at (loc, opt,
+ || warning_at (&richloc, opt,
form
? G_("%qD forming offset %s is out of "
"the bounds of object %qD with type %qT")
@@ -1833,7 +1836,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int strict,
}
}
else if (ref.basesize < maxobjsize)
- warned = warning_at (loc, opt,
+ warned = warning_at (&richloc, opt,
form
? G_("%qD forming offset %s is out "
"of the bounds [0, %wu]")
@@ -1841,7 +1844,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int strict,
"of the bounds [0, %wu]"),
func, rangestr[0], ref.basesize.to_uhwi ());
else
- warned = warning_at (loc, opt,
+ warned = warning_at (&richloc, opt,
form
? G_("%qD forming offset %s is out of bounds")
: G_("%qD offset %s is out of bounds"),
@@ -1855,7 +1858,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int strict,
type = TREE_TYPE (type);
type = TYPE_MAIN_VARIANT (type);
- if (warning_at (loc, opt,
+ if (warning_at (&richloc, opt,
"%qD offset %s from the object at %qE is out "
"of the bounds of %qT",
func, rangestr[0], ref.base, type))
@@ -1873,7 +1876,7 @@ maybe_diag_access_bounds (gimple *call, tree func, int strict,
tree refop = TREE_OPERAND (ref.ref, 0);
tree type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
- if (warning_at (loc, opt,
+ if (warning_at (&richloc, opt,
"%qD offset %s from the object at %qE is out "
"of the bounds of referenced subobject %qD with "
"type %qT at offset %wi",
new file mode 100644
@@ -0,0 +1,56 @@
+/* A rich_location subclass that lazily populates a diagnostic_path
+ with move_history events, but only if the path is actually to be
+ used.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ Contributed by Qing Zhao<qing.zhao@oracle.com>
+
+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/>. */
+
+#define INCLUDE_MEMORY
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "function.h"
+#include "diagnostic-move-history.h"
+#include "simple-diagnostic-path.h"
+#include "move-history-rich-location.h"
+
+/* Implemenation of the method make_inner_path of the class
+ lazy_move_history_path. */
+
+std::unique_ptr<diagnostic_path>
+lazy_move_history_path::make_inner_path () const
+{
+ auto path = std::make_unique<simple_diagnostic_path>
+ (global_dc->get_reference_printer ());
+ if (!flag_diagnostics_details)
+ return path;
+ move_history_t mv_history = m_stmt ? get_move_history (m_stmt) : NULL;
+ for (move_history_t cur_ch = mv_history; cur_ch;
+ cur_ch = cur_ch->prev_move)
+ path->add_event (cur_ch->condition, cfun->decl, 1,
+ "when the condition is evaluated to %s",
+ cur_ch->is_true_path ? "true" : "false");
+
+ /* Add an end of path warning event in the end of the path. */
+ if (path->num_events () > 0)
+ path->add_event (m_location, cfun->decl, 1,
+ "out of array bounds here");
+ return path;
+}
new file mode 100644
@@ -0,0 +1,65 @@
+/* A rich_location subclass that lazily populates a diagnostic_path
+ with move_history events, but only if the path is actually to be
+ used.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ Contributed by Qing Zhao<qing.zhao@oracle.com>
+
+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_MOVE_HISTORY_RICH_LOCATION_H
+#define GCC_MOVE_HISTORY_RICH_LOCATION_H
+
+#include "gcc-rich-location.h"
+#include "lazy-diagnostic-path.h"
+
+class lazy_move_history_path : public lazy_diagnostic_path
+{
+public:
+ lazy_move_history_path (location_t location, gimple *stmt)
+ : m_location (location), m_stmt (stmt)
+ {
+ }
+
+ std::unique_ptr<diagnostic_path>
+ make_inner_path () const final override;
+ /* This methold will be called on demand if a diagnostic is actually
+ emitted for this rich_location. */
+ location_t m_location;
+ gimple *m_stmt;
+};
+
+class rich_location_with_details : public gcc_rich_location
+{
+public:
+ rich_location_with_details (location_t location, gimple *stmt)
+ : gcc_rich_location (location),
+ m_lazy_move_history_path (location, stmt)
+ {
+ set_path (&m_lazy_move_history_path);
+ }
+
+ rich_location_with_details (location_t location, tree exp ATTRIBUTE_UNUSED)
+ : gcc_rich_location (location),
+ m_lazy_move_history_path (location, NULL)
+ {
+ }
+
+private:
+ lazy_move_history_path m_lazy_move_history_path;
+};
+
+#endif // GCC_MOVE_HISTORY_RICH_LOCATION_H
new file mode 100644
@@ -0,0 +1,43 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading. */
+/* { dg-options "-O2 -Wall -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+
+extern void warn(void);
+static inline void assign(int val, int *regs, int index)
+{
+ if (index >= 4)
+ warn();
+ *regs = val;
+}
+struct nums {int vals[4];};
+
+void sparx5_set (int *ptr, struct nums *sg, int index)
+{
+ int *val = &sg->vals[index]; /* { dg-warning "is above array bounds" } */
+
+ assign(0, ptr, index);
+ assign(*val, ptr, index);
+}
+/* { dg-begin-multiline-output "" }
+ NN | int *val = &sg->vals[index];
+ | ~~~~~~~~^~~~~~~
+ 'sparx5_set': events 1-2
+ NN | if (index >= 4)
+ | ^
+ | |
+ | (1) when the condition is evaluated to true
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ | ~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | struct nums {int vals[4];};
+ | ^~~~
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,36 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR88771, which is a duplication of PR109071. */
+/* { dg-options "-O2 -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+typedef struct {
+ int a;
+} * b;
+
+char *c, *x;
+int f;
+
+void d() {
+ b e;
+ char a = f + 1 ?: f;
+ __builtin_strncpy(c, x, f); /* { dg-warning "exceeds maximum object size" } */
+ if (a)
+ e->a = 0;
+}
+/* { dg-begin-multiline-output "" }
+ NN | __builtin_strncpy(c, x, f);
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~
+ 'd': events 1-2
+ NN | char a = f + 1 ?: f;
+ | ^
+ | |
+ | (1) when the condition is evaluated to false
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | __builtin_strncpy(c, x, f);
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,50 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR85788, which is a duplication of PR109071. */
+/* { dg-options "-O2 -Warray-bounds -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+int b=10;
+int *d = &b, *e;
+void a(void *k, long l) {
+ long f = __builtin_object_size(k, 0);
+ __builtin___memset_chk(k, b, l, f); /* { dg-warning "is out of the bounds" } */
+}
+typedef struct {
+ int g;
+ int h;
+ char i[8000 * 8];
+} j;
+static void make_str_raster(j *k) {
+ int *c = d;
+ for (; c; c = e)
+ k->g = k->h = 32767;
+
+ a(k->i, k->g / 8 * k->h);
+ for (; d;)
+ ;
+}
+j m;
+void n() { make_str_raster(&m); }
+/* { dg-begin-multiline-output "" }
+ NN | __builtin___memset_chk(k, b, l, f);
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 'n': events 1-2
+ NN | __builtin___memset_chk(k, b, l, f);
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | for (; c; c = e)
+ | ^
+ | |
+ | (1) when the condition is evaluated to true
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | j m;
+ | ^
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,42 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR108770, which is a duplication of PR109071. */
+/* { dg-options "-O2 -Warray-bounds -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+extern void put(int i);
+int check_idx(int i) {
+ if (i > 1)
+ put(i);
+ return i;
+}
+const char *arr[] = {"A", 0};
+void init() {
+ int i = 0;
+ while (arr[check_idx(i)] != 0) { /* { dg-warning "is above array bounds of" } */
+ if (arr[check_idx(i)]) {}
+ i++;
+ }
+}
+/* { dg-begin-multiline-output "" }
+ NN | while (arr[check_idx(i)] != 0) {
+ | ~~~^~~~~~~~~~~~~~
+ 'init': events 1-2
+ NN | if (i > 1)
+ | ^
+ | |
+ | (1) when the condition is evaluated to true
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | while (arr[check_idx(i)] != 0) {
+ | ~~~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | const char *arr[] = {"A", 0};
+ | ^~~
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,41 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR106762, which is a duplication of PR109071. */
+/* { dg-options "-O2 -Warray-bounds -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+typedef long unsigned int size_t;
+
+struct obj_t { size_t field0; size_t field1; };
+struct obj_array_t { size_t objcnt; struct obj_t* objary; };
+
+extern void *memset (void *__s, int __c, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__(1)));
+
+void bug(struct obj_array_t* ary)
+{
+ size_t idx = 0;
+ struct obj_t* obj;
+ if (idx < ary->objcnt)
+ obj = &ary->objary[idx];
+ else
+ obj = 0;
+ memset(&obj->field1, 0xff, sizeof(obj->field1)); /* { dg-warning "is out of the bounds" } */
+ obj->field0 = 0;
+}
+/* { dg-begin-multiline-output "" }
+ NN | memset(&obj->field1, 0xff, sizeof(obj->field1));
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 'bug': events 1-2
+ NN | if (idx < ary->objcnt)
+ | ^
+ | |
+ | (1) when the condition is evaluated to false
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | memset(&obj->field1, 0xff, sizeof(obj->field1));
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,33 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR115274, which is a duplication of PR109071. */
+/* { dg-options "-O2 -Wstringop-overread -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+#include <string.h>
+char *c;
+void a();
+int b(char *d) { return strlen(d); } /* { dg-warning "or more bytes from a region of size 0" } */
+void e() {
+ long f = 1;
+ f = b(c + f);
+ if (c == 0)
+ a(f);
+}
+/* { dg-begin-multiline-output "" }
+ NN | int b(char *d) { return strlen(d); }
+ | ^~~~~~~~~
+ 'e': events 1-2
+ NN | int b(char *d) { return strlen(d); }
+ | ~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | if (c == 0)
+ | ^
+ | |
+ | (1) when the condition is evaluated to true
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,49 @@
+/* PR tree-optimization/109071 need more context for -Warray-bounds warnings
+ due to code duplication from jump threading.
+ test case is from PR117179, which is a duplication of PR109071. */
+/* { dg-options "-O2 -Warray-bounds -fdiagnostics-details" } */
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-enable-nn-line-numbers "" } */
+const char* commands[] = {"a", "b"};
+
+int setval_internal(int comind)
+{
+ if (comind > sizeof(commands)/sizeof(commands[0])) {
+ return 0;
+ }
+
+ return 1;
+}
+
+_Bool setval_internal_tilde(int comind, const char *com,
+ const char *val)
+{
+ int ret = setval_internal(comind);
+ if (commands[comind] == "b" && /* { dg-warning "is outside array bounds of" } */
+
+ ret)
+ return 1;
+ return 0;
+}
+/* { dg-begin-multiline-output "" }
+ NN | if (commands[comind] == "b" &&
+ | ~~~~~~~~^~~~~~~~
+ 'setval_internal_tilde': events 1-2
+ NN | if (comind > sizeof(commands)/sizeof(commands[0])) {
+ | ^
+ | |
+ | (1) when the condition is evaluated to true
+......
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | if (commands[comind] == "b" &&
+ | ~~~~~~~~~~~~~~~~
+ | |
+ | (2) out of array bounds here
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ NN | const char* commands[] = {"a", "b"};
+ | ^~~~~~~~
+ { dg-end-multiline-output "" } */