From 964f77ef6d057874c47157269b439dbc648fed56 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 11 Dec 2015 16:15:57 -0500
Subject: [PATCH 02/24] RFC: Preserving locations for variable-uses and
 constants (PR 43486)

This is a work-in-progress attempt at retaining source-location
information for uses of variables and for constants: the tree nodes
that don't have an EXPR_LOCATION in our internal representation.

I'm posting the patch now to check that my approach is correct and
get feedback.  It adds new "wrapper" tree nodes around the nodes
that don't have a location_t, effectively decorating them with a
location_t.

The patch doesn't yet bootstrap, and fails many tests, but it does fix
the missing location information, so that e.g.:

  test.cc:5:38: error: invalid conversion from 'int' to 'const char*' [-fpermissive]
     return callee (first, second, third);
                                        ^

becomes:

  test.cc:5:25: error: invalid conversion from 'int' to 'const char*' [-fpermissive]
     return callee (first, second, third);
                           ^~~~~~

for a mismatching type in a function call involving a variable or constant.

The case of a compound expression already works for this case e.g.:

     return callee (first, second * 2, third);
                           ~~~~~~~^~~

These cases are already handled within the C frontend by the
vec<location_t> that's passed around for callsites.

FWIW I posted a patch to add a vec<location_t> to the C++ frontend:
  "[PATCH] C++: use an optional vec<location_t> for callsites"
    https://gcc.gnu.org/ml/gcc-patches/2017-08/msg01392.html
which fixes the cases above, but Jason requested at Cauldron that I pursue
the wrapper node approach (as the vec<location_t> is kind of a workaround)
so here's what I have so far.

Limitations:

* The patch as-is preserves the locations during the frontend, and hence
  solves various issues with diagnostics in the frontend, but the
  locations are discarded during gimplification.  PR 43486 requests
  preserving them into gimple, so although this approach would help with
  that PR, it doesn't fully address it.  I'm happy to defer the
  gimplification issue until after GCC 8.

* To simplify things, the patch only touches the C++ frontend.  Similar
  things would need to happen in the C frontend (and presumably others,
  but I care most about C and C++).

* As noted above, it doesn't yet bootstrap, and introduces various test
  regressions; obviously I'd fix all that assuming the direction of the
  patch is acceptable (folding appears to be the main issue: various
  places in the code expect the result of folding to be a decl, and
  go wrong if they see a wrapper node instead, but we still want the
  location information after folding).

Design questions:

* The patch introduces a new kind of tree node, currently called
  DECL_WRAPPER_EXPR (although it's used for wrapping constants as well
  as decls).  Should wrappers be a new kind of tree node, or should they
  reuse an existing TREE_CODE? (e.g. NOP_EXPR, CONVERT_EXPR, etc).
    * NOP_EXPR: seems to be for use as an rvalue
    * CONVERT_EXPR: for type conversions
    * NON_LVALUE_EXPR: "Value is same as argument, but guaranteed not an
      lvalue"
      * but we *do* want to support lvalues here
    * VIEW_CONVERT_EXPR: viewing one thing as of a different type
      * can it support lvalues?
    * C_MAYBE_CONST_EXPR perhaps (generalized somehow)
  Any suggestions or guidance here?

Memory usage stats:

I tried running this on a non-trivial C++ file ("kdecore.cc" [1]),
but it doesn't yet work well enough to compile it.  So I hacked it
up like this:

   diff --git a/gcc/tree.c b/gcc/tree.c
   index 270e680..5711b2a 100644
   --- a/gcc/tree.c
   +++ b/gcc/tree.c
   @@ -13764,7 +13764,15 @@ maybe_wrap_with_location (tree expr, location_t loc)
      gcc_assert (CONSTANT_CLASS_P (expr)
                 || DECL_P (expr)
                 || EXCEPTIONAL_CLASS_P (expr));
   +
   +#if 0
      return build1_loc (loc, DECL_USAGE_EXPR, TREE_TYPE (expr), expr);
   +#else
   +  /* Simulate the GGC-effect of building the node... */
   +  (void)build1_loc (loc, DECL_USAGE_EXPR, TREE_TYPE (expr), expr);
   +  /* But don't actually do it.  */
   +  return expr;
   +#endif
    }

    /* Return the name of combined function FN, for debugging purposes.  */

to simulate the effect of allocating the wrapper nodes, without actually
using those nodes.

With that, -ftime-report's memory stats for "TOTAL" went
from 615999 kB to 617773 kB
i.e. about a 0.3% increase in overall GC-managed allocations.

I don't have reliable timing information yet.

Thoughts?

Thanks
Dave

[1] https://github.com/davidmalcolm/gcc-build/blob/master/kdecore.cc

gcc/ChangeLog:
	PR c++/43486
	* builtins.c (fold_builtin_next_arg): Strip off any
	DECL_USAGE_EXPR.
	* gimplify.c (gimplify_expr): Handle DECL_USAGE_EXPR.
	* tree.c (maybe_wrap_with_location): New function.
	* tree.def (DECL_USAGE_EXPR): New tree code.
	* tree.h (STRIP_DECL_USAGE_EXPR): New macro.
	(maybe_wrap_with_location): New decl.

gcc/c-family/ChangeLog:
	PR c++/43486
	* c-format.c (check_format_arg): Strip of any DECL_USAGE_EXPR.
	* c-pretty-print.c (c_pretty_printer::expression): Handle
	DECL_USAGE_EXPR.

gcc/cp/ChangeLog:
	PR c++/43486
	* constexpr.c (cxx_eval_constant_expression): Handle
	DECL_USAGE_EXPR.
	(cxx_eval_outermost_constant_expr): Use STRIP_DECL_USAGE_EXPR.
	(potential_constant_expression_1): Handle DECL_USAGE_EXPR.
	* cp-tree.h (cp_expr::maybe_add_location_wrapper): New method.
	* error.c (dump_expr): Handle DECL_USAGE_EXPR.
	* expr.c (mark_exp_read): Likewise.
	* parser.c (cp_parser_primary_expression): Call
	maybe_add_location_wrapper on the result for literals.
	(cp_parser_postfix_expression): Likewise for RID_TYPEID.
	(cp_parser_unary_expression): Likewise for RID_ALIGNOF and
	RID_SIZEOF.
	(cp_parser_builtin_offsetof): Likewise.
	* semantics.c (finish_call_expr): Use STRIP_DECL_USAGE_EXPR.
	(finish_id_expression): Rename to...
	(finish_id_expression_1): ...this.
	(finish_id_expression): Reintroduce, calling the above, and
	calling maybe_add_location_wrapper on the result.
	* tree.c (lvalue_kind): Use STRIP_DECL_USAGE_EXPR.
	* typeck.c (cp_build_function_call_vec): Likewise.
	(build_address): Use the EXPR_LOCATION when building the
	ADDR_EXPR.

gcc/testsuite/ChangeLog:
	PR c++/43486
	* g++.dg/diagnostic/param-type-mismatch.C: Update expected results
	to reflect that the arguments are correctly underlined.
	* g++.dg/plugin/diagnostic-test-expressions-1.C: Add test coverage
	for globals, params, locals and literals.
	(test_sizeof): Directly test the location of "sizeof", rather than
	when used in compound expressions.
	(test_alignof): Likewise for "alignof".
	(test_string_literals): Likewise for string literals.
	(test_numeric_literals): Likewise for numeric literals.
	(test_builtin_offsetof): Likewise for "__builtin_offsetof".
	(test_typeid): Likewise for typeid.
	(test_unary_plus): New.
	* gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
	(cb_walk_tree_fn): Handle DECL_USAGE_EXPR.
---
 gcc/builtins.c                                     |   3 +-
 gcc/c-family/c-format.c                            |   3 +
 gcc/c-family/c-pretty-print.c                      |   1 +
 gcc/cp/constexpr.c                                 |   4 +
 gcc/cp/cp-tree.h                                   |   6 +
 gcc/cp/error.c                                     |   1 +
 gcc/cp/expr.c                                      |   1 +
 gcc/cp/parser.c                                    |  15 +-
 gcc/cp/semantics.c                                 |  60 +++--
 gcc/cp/tree.c                                      |   2 +
 gcc/cp/typeck.c                                    |   4 +-
 gcc/gimplify.c                                     |   3 +
 .../g++.dg/diagnostic/param-type-mismatch.C        |  27 +--
 .../g++.dg/plugin/diagnostic-test-expressions-1.C  | 260 +++++++++++++--------
 .../diagnostic_plugin_test_tree_expression_range.c |   2 +
 gcc/tree.c                                         |  20 ++
 gcc/tree.def                                       |   3 +
 gcc/tree.h                                         |  10 +
 18 files changed, 288 insertions(+), 137 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index d3498bb..5398df4 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -9566,7 +9566,8 @@ fold_builtin_next_arg (tree exp, bool va_start_p)
 	 We must also strip off INDIRECT_EXPR for C++ reference
 	 parameters.  */
       while (CONVERT_EXPR_P (arg)
-	     || TREE_CODE (arg) == INDIRECT_REF)
+	     || TREE_CODE (arg) == INDIRECT_REF
+	     || TREE_CODE (arg) == DECL_USAGE_EXPR)
 	arg = TREE_OPERAND (arg, 0);
       if (arg != last_parm)
 	{
diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index 164d035..ae07201 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -1536,6 +1536,9 @@ check_format_arg (void *ctx, tree format_tree,
 
   location_t fmt_param_loc = EXPR_LOC_OR_LOC (format_tree, input_location);
 
+  if (TREE_CODE (format_tree) == DECL_USAGE_EXPR)
+    format_tree = TREE_OPERAND (format_tree, 0);
+
   if (VAR_P (format_tree))
     {
       /* Pull out a constant value if the front end didn't.  */
diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c
index 0f48b9e..c4a7d2c 100644
--- a/gcc/c-family/c-pretty-print.c
+++ b/gcc/c-family/c-pretty-print.c
@@ -2311,6 +2311,7 @@ c_pretty_printer::expression (tree e)
 
     case NON_LVALUE_EXPR:
     case SAVE_EXPR:
+    case DECL_USAGE_EXPR:
       expression (TREE_OPERAND (e, 0));
       break;
 
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 5919282..7551f17 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4423,6 +4423,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 				       non_constant_p, overflow_p);
       break;
 
+    case DECL_USAGE_EXPR:
     case CONVERT_EXPR:
     case VIEW_CONVERT_EXPR:
     case NOP_EXPR:
@@ -4697,6 +4698,8 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
   r = cxx_eval_constant_expression (&ctx, r,
 				    false, &non_constant_p, &overflow_p);
 
+  STRIP_DECL_USAGE_EXPR (r);
+
   verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
 
   /* Mutable logic is a bit tricky: we want to allow initialization of
@@ -5290,6 +5293,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
         }
       return true;
 
+    case DECL_USAGE_EXPR:
     case NOP_EXPR:
     case CONVERT_EXPR:
     case VIEW_CONVERT_EXPR:
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index b74b6d9..254999a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -93,6 +93,12 @@ public:
     set_location (make_location (m_loc, start, finish));
   }
 
+  cp_expr& maybe_add_location_wrapper ()
+  {
+    m_value = maybe_wrap_with_location (m_value, m_loc);
+    return *this;
+  }
+
  private:
   tree m_value;
   location_t m_loc;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 7a98d2e..e78f7d9 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -2424,6 +2424,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
       break;
 
     CASE_CONVERT:
+    case DECL_USAGE_EXPR:
     case IMPLICIT_CONV_EXPR:
     case VIEW_CONVERT_EXPR:
       {
diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c
index 23e30cf..ce900a3 100644
--- a/gcc/cp/expr.c
+++ b/gcc/cp/expr.c
@@ -277,6 +277,7 @@ mark_exp_read (tree exp)
     case MODIFY_EXPR:
     case REALPART_EXPR:
     case IMAGPART_EXPR:
+    case DECL_USAGE_EXPR:
     CASE_CONVERT:
     case ADDR_EXPR:
     case INDIRECT_REF:
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 2337be5..ffe2605 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -5065,7 +5065,8 @@ cp_parser_primary_expression (cp_parser *parser,
 	  if (!cast_p)
 	    cp_parser_non_integral_constant_expression (parser, NIC_FLOAT);
 	}
-      return cp_expr (token->u.value, token->location);
+      return (cp_expr (token->u.value, token->location)
+	      .maybe_add_location_wrapper ());
 
     case CPP_CHAR_USERDEF:
     case CPP_CHAR16_USERDEF:
@@ -5087,9 +5088,10 @@ cp_parser_primary_expression (cp_parser *parser,
       /* ??? Should wide strings be allowed when parser->translate_strings_p
 	 is false (i.e. in attributes)?  If not, we can kill the third
 	 argument to cp_parser_string_literal.  */
-      return cp_parser_string_literal (parser,
-				       parser->translate_strings_p,
-				       true);
+      return (cp_parser_string_literal (parser,
+					parser->translate_strings_p,
+					true)
+	      .maybe_add_location_wrapper ());
 
     case CPP_OPEN_PAREN:
       /* If we see `( { ' then we are looking at the beginning of
@@ -6776,6 +6778,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    location_t typeid_loc
 	      = make_location (start_loc, start_loc, close_paren->location);
 	    postfix_expression.set_location (typeid_loc);
+	    postfix_expression.maybe_add_location_wrapper ();
 	  }
       }
       break;
@@ -8107,7 +8110,7 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 
 	    cp_expr ret_expr (ret);
 	    ret_expr.set_location (compound_loc);
-	    return ret_expr;
+	    return ret_expr.maybe_add_location_wrapper ();
 	  }
 
 	case RID_NEW:
@@ -9933,7 +9936,7 @@ cp_parser_builtin_offsetof (cp_parser *parser)
   parser->integral_constant_expression_p = save_ice_p;
   parser->non_integral_constant_expression_p = save_non_ice_p;
 
-  return expr;
+  return expr.maybe_add_location_wrapper ();
 }
 
 /* Parse a trait expression.
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index a512664..2737776 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2324,6 +2324,10 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
      it so that we can tell this is a call to a known function.  */
   fn = maybe_undo_parenthesized_ref (fn);
 
+  /* Strip away any wrapper to ensure we recognize the underlying fn,
+     if any.  */
+  STRIP_DECL_USAGE_EXPR (fn);
+
   orig_fn = fn;
 
   if (processing_template_decl)
@@ -3450,20 +3454,20 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool force_use)
    the use of "this" explicit.
 
    Upon return, *IDK will be filled in appropriately.  */
-cp_expr
-finish_id_expression (tree id_expression,
-		      tree decl,
-		      tree scope,
-		      cp_id_kind *idk,
-		      bool integral_constant_expression_p,
-		      bool allow_non_integral_constant_expression_p,
-		      bool *non_integral_constant_expression_p,
-		      bool template_p,
-		      bool done,
-		      bool address_p,
-		      bool template_arg_p,
-		      const char **error_msg,
-		      location_t location)
+static cp_expr
+finish_id_expression_1 (tree id_expression,
+			tree decl,
+			tree scope,
+			cp_id_kind *idk,
+			bool integral_constant_expression_p,
+			bool allow_non_integral_constant_expression_p,
+			bool *non_integral_constant_expression_p,
+			bool template_p,
+			bool done,
+			bool address_p,
+			bool template_arg_p,
+			const char **error_msg,
+			location_t location)
 {
   decl = strip_using_decl (decl);
 
@@ -3763,6 +3767,34 @@ finish_id_expression (tree id_expression,
   return cp_expr (decl, location);
 }
 
+/* As per finish_id_expression_1, but adding a wrapper node
+   around the result if needed to express LOCATION.  */
+
+cp_expr
+finish_id_expression (tree id_expression,
+		      tree decl,
+		      tree scope,
+		      cp_id_kind *idk,
+		      bool integral_constant_expression_p,
+		      bool allow_non_integral_constant_expression_p,
+		      bool *non_integral_constant_expression_p,
+		      bool template_p,
+		      bool done,
+		      bool address_p,
+		      bool template_arg_p,
+		      const char **error_msg,
+		      location_t location)
+{
+  cp_expr result
+    = finish_id_expression_1 (id_expression, decl, scope, idk,
+			      integral_constant_expression_p,
+			      allow_non_integral_constant_expression_p,
+			      non_integral_constant_expression_p,
+			      template_p, done, address_p, template_arg_p,
+			      error_msg, location);
+  return result.maybe_add_location_wrapper ();
+}
+
 /* Implement the __typeof keyword: Return the type of EXPR, suitable for
    use as a type-specifier.  */
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 366f46f..c48a5be 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -63,6 +63,8 @@ lvalue_kind (const_tree ref)
   if (REFERENCE_REF_P (ref))
     return lvalue_kind (TREE_OPERAND (ref, 0));
 
+  STRIP_DECL_USAGE_EXPR (ref);
+
   if (TREE_TYPE (ref)
       && TREE_CODE (TREE_TYPE (ref)) == REFERENCE_TYPE)
     {
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 19fbe3c..473ca18 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -3599,6 +3599,8 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
       && TREE_TYPE (function) == TREE_TYPE (TREE_OPERAND (function, 0)))
     function = TREE_OPERAND (function, 0);
 
+  STRIP_DECL_USAGE_EXPR (function);
+
   if (TREE_CODE (function) == FUNCTION_DECL)
     {
       /* If the function is a non-template member function
@@ -5621,7 +5623,7 @@ build_address (tree t)
   if (error_operand_p (t) || !cxx_mark_addressable (t))
     return error_mark_node;
   gcc_checking_assert (TREE_CODE (t) != CONSTRUCTOR);
-  t = build_fold_addr_expr (t);
+  t = build_fold_addr_expr_loc (EXPR_LOCATION (t), t);
   if (TREE_CODE (t) != ADDR_EXPR)
     t = rvalue (t);
   return t;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 2c1ec85..109c498 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -11254,6 +11254,9 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
       else if (ret != GS_UNHANDLED)
 	break;
 
+      if (TREE_CODE (*expr_p) == DECL_USAGE_EXPR)
+	*expr_p = TREE_OPERAND (*expr_p, 0);
+
       /* Make sure that all the cases set 'ret' appropriately.  */
       ret = GS_UNHANDLED;
       switch (TREE_CODE (*expr_p))
diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
index bc3a938..5fcde0b 100644
--- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
@@ -1,9 +1,6 @@
 // { dg-options "-fdiagnostics-show-caret" }
 
-/* A collection of calls where argument 2 is of the wrong type.
-
-   TODO: we should put the caret and underline for the diagnostic
-   at the second argument, rather than the close paren.  */
+/* A collection of calls where argument 2 is of the wrong type.  */
 
 /* decl, with argname.  */
 
@@ -14,7 +11,7 @@ int test_1 (int first, int second, float third)
   return callee_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
   /* { dg-begin-multiline-output "" }
    return callee_1 (first, second, third);
-                                        ^
+                           ^~~~~~
      { dg-end-multiline-output "" } */
   // { dg-message "initializing argument 2 of 'int callee_1\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_1 }
   /* { dg-begin-multiline-output "" }
@@ -32,7 +29,7 @@ int test_2 (int first, int second, float third)
   return callee_2 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
   /* { dg-begin-multiline-output "" }
    return callee_2 (first, second, third);
-                                        ^
+                           ^~~~~~
      { dg-end-multiline-output "" } */
   // { dg-message "initializing argument 2 of 'int callee_2\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_2 }
   /* { dg-begin-multiline-output "" }
@@ -53,7 +50,7 @@ int test_3 (int first, int second, float third)
   return callee_3 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
   /* { dg-begin-multiline-output "" }
    return callee_3 (first, second, third);
-                                        ^
+                           ^~~~~~
      { dg-end-multiline-output "" } */
   // { dg-message "initializing argument 2 of 'int callee_3\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_3 }
   /* { dg-begin-multiline-output "" }
@@ -71,7 +68,7 @@ int test_4 (int first, int second, float third)
   return s4::member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
   /* { dg-begin-multiline-output "" }
    return s4::member_1 (first, second, third);
-                                            ^
+                               ^~~~~~
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
  struct s4 { static int member_1 (int one, const char *two, float three); };
@@ -89,7 +86,7 @@ int test_5 (int first, int second, float third)
   return inst.member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
   /* { dg-begin-multiline-output "" }
    return inst.member_1 (first, second, third);
-                                             ^
+                                ^~~~~~
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
  struct s5 { int member_1 (int one, const char *two, float three); };
@@ -106,7 +103,7 @@ int test_6 (int first, int second, float third, s6 *ptr)
   return ptr->member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
   /* { dg-begin-multiline-output "" }
    return ptr->member_1 (first, second, third);
-                                             ^
+                                ^~~~~~
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
  struct s6 { int member_1 (int one, const char *two, float three); };
@@ -128,7 +125,7 @@ int test_7 (int first, int second, float third)
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
    return test_7 <const char *> (first, second, third);
-                                                     ^
+                                        ^~~~~~
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
  int test_7 (int one, T two, float three);
@@ -146,7 +143,7 @@ int test_8 (int first, int second, float third)
   return s8 <const char *>::member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
   /* { dg-begin-multiline-output "" }
    return s8 <const char *>::member_1 (first, second, third);
-                                                           ^
+                                              ^~~~~~
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
  struct s8 { static int member_1 (int one, T two, float three); };
@@ -165,7 +162,7 @@ int test_9 (int first, int second, float third)
   return inst.member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
   /* { dg-begin-multiline-output "" }
    return inst.member_1 (first, second, third);
-                                             ^
+                                ^~~~~~
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
  struct s9 { int member_1 (int one, T two, float three); };
@@ -182,7 +179,7 @@ int test_10 (int first, int second, float third)
   return callee_10 (first, second, third); // { dg-error "invalid conversion from 'int' to 'int \\(\\*\\)\\(int, int\\)'" }
   /* { dg-begin-multiline-output "" }
    return callee_10 (first, second, third);
-                                         ^
+                            ^~~~~~
      { dg-end-multiline-output "" } */
   // { dg-message "initializing argument 2 of 'int callee_10\\(int, int \\(\\*\\)\\(int, int\\), float\\)'" "" { target *-*-* } callee_10 }
   /* { dg-begin-multiline-output "" }
@@ -200,7 +197,7 @@ int test_11 (int first, int second, float third)
   return callee_11 (first, second, third); // { dg-error "invalid conversion from 'int' to 'int \\(\\*\\)\\(int, int\\)'" }
   /* { dg-begin-multiline-output "" }
    return callee_11 (first, second, third);
-                                         ^
+                            ^~~~~~
      { dg-end-multiline-output "" } */
   // { dg-message "initializing argument 2 of 'int callee_11\\(int, int \\(\\*\\)\\(int, int\\), float\\)'" "" { target *-*-* } callee_11 }
   /* { dg-begin-multiline-output "" }
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
index a145dfe..5e4d78b 100644
--- a/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
@@ -19,6 +19,113 @@ extern void __emit_expression_range (int dummy, ...);
 
 int global;
 
+void test_global (void)
+{
+  __emit_expression_range (0, global); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, global);
+                               ^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_param (int param)
+{
+  __emit_expression_range (0, param); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, param);
+                               ^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_local (void)
+{
+  int local = 5;
+
+  __emit_expression_range (0, local); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, local);
+                               ^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_integer_constants (void)
+{
+  __emit_expression_range (0, 1234); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 1234);
+                               ^~~~
+   { dg-end-multiline-output "" } */
+
+  /* Ensure that zero works.  */
+
+  __emit_expression_range (0, 0); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 0);
+                               ^
+   { dg-end-multiline-output "" } */
+}
+
+void test_character_constants (void)
+{
+  __emit_expression_range (0, 'a'); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 'a');
+                               ^~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_floating_constants (void)
+{
+  __emit_expression_range (0, 98.6); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 98.6);
+                               ^~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, .6); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, .6);
+                               ^~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, 98.); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 98.);
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, 6.022140857e23 ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 6.022140857e23 );
+                               ^~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, 98.6f ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 98.6f );
+                               ^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, 6.022140857e23l ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 6.022140857e23l );
+                               ^~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+enum test_enum {
+  TEST_ENUM_VALUE
+};
+
+void test_enumeration_constant (void)
+{
+  __emit_expression_range (0, TEST_ENUM_VALUE ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, TEST_ENUM_VALUE );
+                               ^~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
 void test_parentheses (int a, int b)
 {
   __emit_expression_range (0, (a + b) ); /* { dg-warning "range" } */
@@ -103,67 +210,36 @@ int test_postfix_incdec (int i)
 
 int test_sizeof (int i)
 {
-  __emit_expression_range (0, sizeof(int) + i); /* { dg-warning "range" } */
-/* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, sizeof(int) + i);
-                               ~~~~~~~~~~~~^~~
-   { dg-end-multiline-output "" } */
-
-  __emit_expression_range (0, i + sizeof(int)); /* { dg-warning "range" } */
-/* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, i + sizeof(int));
-                               ~~^~~~~~~~~~~~~
-   { dg-end-multiline-output "" } */
-
-  __emit_expression_range (0, sizeof i + i); /* { dg-warning "range" } */
+  __emit_expression_range (0, sizeof i ); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, sizeof i + i);
-                               ~~~~~~~~~^~~
+   __emit_expression_range (0, sizeof i );
+                               ^~~~~~~~
    { dg-end-multiline-output "" } */
 
-  __emit_expression_range (0, i + sizeof i); /* { dg-warning "range" } */
+  __emit_expression_range (0, sizeof (char) ); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, i + sizeof i);
-                               ~~^~~~~~~~~~
+   __emit_expression_range (0, sizeof (char) );
+                               ^~~~~~~~~~~~~
    { dg-end-multiline-output "" } */
 }
 
 int test_alignof (int i)
 {
-  __emit_expression_range (0, alignof(int) + i); /* { dg-warning "range" } */
+  __emit_expression_range (0, alignof(int)); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, alignof(int) + i);
-                               ~~~~~~~~~~~~~^~~
-   { dg-end-multiline-output "" } */
-
-  __emit_expression_range (0, i + alignof(int)); /* { dg-warning "range" } */
-/* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, i + alignof(int));
-                               ~~^~~~~~~~~~~~~~
-   { dg-end-multiline-output "" } */
-
-  __emit_expression_range (0, __alignof__(int) + i); /* { dg-warning "range" } */
-/* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, __alignof__(int) + i);
-                               ~~~~~~~~~~~~~~~~~^~~
-   { dg-end-multiline-output "" } */
-
-  __emit_expression_range (0, i + __alignof__(int)); /* { dg-warning "range" } */
-/* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, i + __alignof__(int));
-                               ~~^~~~~~~~~~~~~~~~~~
+   __emit_expression_range (0, alignof(int));
+                               ^~~~~~~~~~~~
    { dg-end-multiline-output "" } */
 
-  __emit_expression_range (0, __alignof__ i + i); /* { dg-warning "range" } */
+  __emit_expression_range (0, __alignof__(int)); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, __alignof__ i + i);
-                               ~~~~~~~~~~~~~~^~~
+   __emit_expression_range (0, __alignof__(int));
+                               ^~~~~~~~~~~~~~~~
    { dg-end-multiline-output "" } */
-
-  __emit_expression_range (0, i + __alignof__ i); /* { dg-warning "range" } */
+  __emit_expression_range (0, __alignof__ i); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, i + __alignof__ i);
-                               ~~^~~~~~~~~~~~~~~
+   __emit_expression_range (0, __alignof__ i);
+                               ^~~~~~~~~~~~~
    { dg-end-multiline-output "" } */
 }
 
@@ -200,6 +276,15 @@ void test_indirection (int *ptr)
    { dg-end-multiline-output "" } */
 }
 
+void test_unary_plus (int i)
+{
+  __emit_expression_range (0, +i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, +i );
+                               ^~
+   { dg-end-multiline-output "" } */
+}
+
 void test_unary_minus (int i)
 {
   __emit_expression_range (0, -i ); /* { dg-warning "range" } */
@@ -471,53 +556,36 @@ void test_comma_operator (int a, int b)
 
 /* Literals.  **************************************************/
 
-/* We can't test the ranges of literals directly, since the underlying
-   tree nodes don't retain a location.  However, we can test that they
-   have ranges during parsing by building compound expressions using
-   them, and verifying the ranges of the compound expressions.  */
-
-void test_string_literals (int i)
+void test_string_literals ()
 {
-  __emit_expression_range (0, "foo"[i] ); /* { dg-warning "range" } */
+  __emit_expression_range (0, "0123456789"); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, "foo"[i] );
-                               ~~~~~~~^
+   __emit_expression_range (0, "0123456789");
+                               ^~~~~~~~~~~~
    { dg-end-multiline-output "" } */
 
-  __emit_expression_range (0, &"foo" "bar" ); /* { dg-warning "range" } */
+  __emit_expression_range (0, "foo" "bar" ); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, &"foo" "bar" );
-                               ^~~~~~~~~~~~
+   __emit_expression_range (0, "foo" "bar" );
+                               ^~~~~~~~~~~
    { dg-end-multiline-output "" } */
 }
 
 void test_numeric_literals (int i)
 {
-  __emit_expression_range (0, 42 + i ); /* { dg-warning "range" } */
-/* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, 42 + i );
-                               ~~~^~~
-   { dg-end-multiline-output "" } */
-
-  __emit_expression_range (0, i + 42 ); /* { dg-warning "range" } */
+  __emit_expression_range (0, 42 ); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, i + 42 );
-                               ~~^~~~
+   __emit_expression_range (0, 42 );
+                               ^~
    { dg-end-multiline-output "" } */
 
   /* Verify locations of negative literals (via folding of
      unary negation).  */
 
-  __emit_expression_range (0, -42 + i ); /* { dg-warning "range" } */
-/* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, -42 + i );
-                               ~~~~^~~
-   { dg-end-multiline-output "" } */
-
-  __emit_expression_range (0, i + -42 ); /* { dg-warning "range" } */
+  __emit_expression_range (0, -42 ); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, i + -42 );
-                               ~~^~~~~
+   __emit_expression_range (0, -42 );
+                               ^~~
    { dg-end-multiline-output "" } */
 
   __emit_expression_range (0, i ? 0 : -1 ); /* { dg-warning "range" } */
@@ -529,6 +597,8 @@ void test_numeric_literals (int i)
 
 /* Braced initializers.  ***************************************/
 
+// FIXME:
+
 /* We can't test the ranges of these directly, since the underlying
    tree nodes don't retain a location.  However, we can test that they
    have ranges during parsing by building compound expressions using
@@ -556,6 +626,8 @@ void test_braced_init (void)
 
 /* Statement expressions.  ***************************************/
 
+// FIXME:
+
 void test_statement_expression (void)
 {
   __emit_expression_range (0, ({ static int a; a; }) + 1);  /* { dg-warning "range" } */
@@ -638,16 +710,10 @@ struct s { int i; float f; };
 
 void test_builtin_offsetof (int i)
 {
-  __emit_expression_range (0,  i + __builtin_offsetof (struct s, f) );  /* { dg-warning "range" } */
+  __emit_expression_range (0,  __builtin_offsetof (struct s, f) );  /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0,  i + __builtin_offsetof (struct s, f) );
-                                ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-   { dg-end-multiline-output "" } */
-
-  __emit_expression_range (0,  __builtin_offsetof (struct s, f) + i );  /* { dg-warning "range" } */
-/* { dg-begin-multiline-output "" }
-   __emit_expression_range (0,  __builtin_offsetof (struct s, f) + i );
-                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
+   __emit_expression_range (0,  __builtin_offsetof (struct s, f) );
+                                ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
    { dg-end-multiline-output "" } */
 }
 
@@ -856,28 +922,22 @@ namespace std
 
 void test_typeid (int i)
 {
-  __emit_expression_range (0, &typeid(i)); /* { dg-warning "range" } */
+  __emit_expression_range (0, typeid(i)); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, &typeid(i));
-                               ^~~~~~~~~~
-   { dg-end-multiline-output "" } */
-
-  __emit_expression_range (0, &typeid(int)); /* { dg-warning "range" } */
-/* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, &typeid(int));
-                               ^~~~~~~~~~~~
+   __emit_expression_range (0, typeid(i));
+                               ^~~~~~~~~
    { dg-end-multiline-output "" } */
 
-  __emit_expression_range (0, &typeid(i * 2)); /* { dg-warning "range" } */
+  __emit_expression_range (0, typeid(int)); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, &typeid(i * 2));
-                               ^~~~~~~~~~~~~~
+   __emit_expression_range (0, typeid(int));
+                               ^~~~~~~~~~~
    { dg-end-multiline-output "" } */
 
-  __emit_expression_range (0, typeid(int).foo); /* { dg-warning "range" } */
+  __emit_expression_range (0, typeid(i * 2)); /* { dg-warning "range" } */
 /* { dg-begin-multiline-output "" }
-   __emit_expression_range (0, typeid(int).foo);
-                               ~~~~~~~~~~~~^~~
+   __emit_expression_range (0, typeid(i * 2));
+                               ^~~~~~~~~~~~~
    { dg-end-multiline-output "" } */
 }
 
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
index 89cc95a..50c69b8 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
@@ -57,6 +57,8 @@ cb_walk_tree_fn (tree * tp, int * walk_subtrees,
   if (TREE_CODE (fn) != ADDR_EXPR)
     return NULL_TREE;
   fn = TREE_OPERAND (fn, 0);
+  if (TREE_CODE (fn) == DECL_USAGE_EXPR)
+    fn = TREE_OPERAND (fn, 0);
   if (TREE_CODE (fn) != FUNCTION_DECL)
     return NULL_TREE;
   if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fn)), "__emit_expression_range"))
diff --git a/gcc/tree.c b/gcc/tree.c
index 62a4386..270e680 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13747,6 +13747,26 @@ set_source_range (tree expr, source_range src_range)
   return adhoc;
 }
 
+/* Return EXPR, potentially wrapped with a DECL_USAGE_EXPR node
+   at LOC, if !CAN_HAVE_LOCATION_P (expr).  */
+
+tree
+maybe_wrap_with_location (tree expr, location_t loc)
+{
+  if (expr == NULL)
+    return NULL;
+  if (loc == UNKNOWN_LOCATION)
+    return expr;
+  if (CAN_HAVE_LOCATION_P (expr))
+    return expr;
+  /* We should only be adding wrappers for constants and for decls,
+     or for some exceptional tree nodes (e.g. BASELINK in the C++ FE).  */
+  gcc_assert (CONSTANT_CLASS_P (expr)
+	      || DECL_P (expr)
+	      || EXCEPTIONAL_CLASS_P (expr));
+  return build1_loc (loc, DECL_USAGE_EXPR, TREE_TYPE (expr), expr);
+}
+
 /* Return the name of combined function FN, for debugging purposes.  */
 
 const char *
diff --git a/gcc/tree.def b/gcc/tree.def
index 3d2bd95..bb44cc6 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -827,6 +827,9 @@ DEFTREECODE (FIXED_CONVERT_EXPR, "fixed_convert_expr", tcc_unary, 1)
 /* Represents a conversion expected to require no code to be generated.  */
 DEFTREECODE (NOP_EXPR, "nop_expr", tcc_unary, 1)
 
+/* Represents the usage of a decl within an expression.  */
+DEFTREECODE (DECL_USAGE_EXPR, "decl_usage_expr", tcc_unary, 1)
+
 /* Value is same as argument, but guaranteed not an lvalue.  */
 DEFTREECODE (NON_LVALUE_EXPR, "non_lvalue_expr", tcc_unary, 1)
 
diff --git a/gcc/tree.h b/gcc/tree.h
index 7214ae2..5c24c07 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -483,6 +483,14 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
 #define STRIP_USELESS_TYPE_CONVERSION(EXP) \
   (EXP) = tree_ssa_strip_useless_type_conversions (EXP)
 
+/* Remove any DECL_USAGE_EXPR.  */
+
+#define STRIP_DECL_USAGE_EXPR(EXP) \
+  do { \
+    if (TREE_CODE (EXP) == DECL_USAGE_EXPR)  \
+      (EXP) = TREE_OPERAND ((EXP), 0);	     \
+  } while (0)
+
 /* Nonzero if TYPE represents a vector type.  */
 
 #define VECTOR_TYPE_P(TYPE) (TREE_CODE (TYPE) == VECTOR_TYPE)
@@ -1147,6 +1155,8 @@ get_expr_source_range (tree expr)
 
 extern void protected_set_expr_location (tree, location_t);
 
+extern tree maybe_wrap_with_location (tree, location_t);
+
 /* In a TARGET_EXPR node.  */
 #define TARGET_EXPR_SLOT(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 0)
 #define TARGET_EXPR_INITIAL(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 1)
-- 
1.8.5.3

