diff mbox

[PATCH/RFC] C++ FE: expression ranges (v2)

Message ID 1448362710.19594.234.camel@surprise
State New
Headers show

Commit Message

David Malcolm Nov. 24, 2015, 10:58 a.m. UTC
On Tue, 2015-11-24 at 10:40 +0100, Richard Biener wrote:
> On Mon, Nov 23, 2015 at 8:25 PM, Jason Merrill <jason@redhat.com> wrote:
> > On 11/23/2015 12:07 PM, Marek Polacek wrote:
> >>
> >> On Mon, Nov 23, 2015 at 05:57:54PM +0100, Jakub Jelinek wrote:
> >>>
> >>> On Mon, Nov 23, 2015 at 11:53:40AM -0500, David Malcolm wrote:
> >>>>
> >>>> Does the following look like the kind of thing you had in mind?  (just
> >>>> the tree.def part for now).   Presumably usable for both lvalues and
> >>>> rvalues, where the thing it wraps is what's important.  It merely exists
> >>>> to add an EXPR_LOCATION, for a usage of the wrapped thing.
> >>>
> >>>
> >>> Yes, but please see with Jason, Richard and perhaps others if they are ok
> >>> with that too before spending too much time in that direction.
> >>> All occurrences of it would have to be folded away during the
> >>> gimplification
> >>> at latest, this shouldn't be something we use in the middle-end.
> >>
> >>
> >> I'd expect LOCATION_EXPR be defined in c-family/c-common.def, not
> >> tree.def.
> >> And I'd think it shouldn't survive genericizing, thus never leak into the
> >> ME.
> >
> >
> > Makes sense.

FWIW, attached is a work-in-progress patch for the LOCATION_EXPR
approach.  This one
* adds LOCATION_EXPR to cp/cp-tree.def, rather than to c-common.def (my
thinking being that if it's only being used in the C++ FE, make it
specific to it)
* LOCATION_EXPR wrapper nodes are created for VAR_DECL, PARM_DECL (I'm
not sure if I want to create them for STRING_CST)
* strips them out in cp_genericize_r
* verifies their absence in cp_gimplify_expr via a gcc_unreachable
* lots of use of STRIP_LOCATION_EXPRS around places which expect a
certain kind of node.  This feels like a game of whack-a-mole.  I've got
increasing amounts of the C++ stdlib to parse, but am running into
various folding issues:

x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42: error:
‘(std::size_t)0’ is not a constant expression
       static const size_t _S_alignment = 0;
                                          ^

which doesn't seem to be easily fixable; I'd already put a cp_fully_fold
into cp/constexpr.c:verify_constant, but this is:

(gdb) call debug_tree (t)
 <nop_expr 0x7fffef03d580
    type <integer_type 0x7ffff1632f18 size_t unsigned type_6 DI
        size <integer_cst 0x7ffff1891e58 constant 64>
        unit size <integer_cst 0x7ffff1891e70 constant 8>
        align 64 symtab 0 alias set -1 canonical type 0x7ffff18959d8
precision 64 min <integer_cst 0x7ffff18b3138 0> max <integer_cst
0x7ffff18a35e0 18446744073709551615>>
    constant
    arg 0 <location_expr 0x7fffef03d4e0
        type <integer_type 0x7ffff18957e0 int asm_written public type_6
SI
            size <integer_cst 0x7ffff18b30a8 constant 32>
            unit size <integer_cst 0x7ffff18b30c0 constant 4>
            align 32 symtab -243642208 alias set -1 canonical type
0x7ffff18957e0 precision 32 min <integer_cst 0x7ffff18b3060 -2147483648>
max <integer_cst 0x7ffff18b3078 2147483647>
            pointer_to_this <pointer_type 0x7ffff18b7930>>
        constant
        arg 0 <integer_cst 0x7ffff18b31f8 constant 0>
        /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 start: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 finish: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42>
    /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 start: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 finish: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42>

which isn't folded here by cp_fully_fold since we're in
processing_template_decl; STRIP_NOPS etc can't do it since LOCATION_EXPR
is frontend-specific.

Any thoughts on how to work around this?


> OTOH then the FEs need to strip trees of it if they ever want to use
> sth from fold-const.c for example.  NON_LVALUE_EXPR is not
> in c-family/c-common.def either... (and conveniently stripped with
> STRIP_NOPS and friends).

Just to clarify, do you mean "NON_LVALUE_EXPR is *not* conveniently
stripped by STRIP_NOPS and friends"?  As far as I can tell, STRIP_NOPS
etc are all frontend-independent, hence frontend-specific tree types
can't be handled there.


> Ok, I _would_ like to move NON_LVALUE_EXPR to the C frontend
> family as well...  so I guess that would be a nice starting project
> and if you can make that work you can add LOCATION_EXPR to
> the C family only.

At a higher level, I'm nervous about feature-creep here, relative to the
v2 patch; that one (mostly) worked, without creating a new tree type, in
that it captured the locations (and thus ranges) for the leaf nodes of
the parse tree for just long enough to let them be used when building
more interesting expressions, which is where the range information is
useful.

Dave

Comments

Richard Biener Nov. 24, 2015, 11:36 a.m. UTC | #1
On Tue, Nov 24, 2015 at 11:58 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> On Tue, 2015-11-24 at 10:40 +0100, Richard Biener wrote:
>> On Mon, Nov 23, 2015 at 8:25 PM, Jason Merrill <jason@redhat.com> wrote:
>> > On 11/23/2015 12:07 PM, Marek Polacek wrote:
>> >>
>> >> On Mon, Nov 23, 2015 at 05:57:54PM +0100, Jakub Jelinek wrote:
>> >>>
>> >>> On Mon, Nov 23, 2015 at 11:53:40AM -0500, David Malcolm wrote:
>> >>>>
>> >>>> Does the following look like the kind of thing you had in mind?  (just
>> >>>> the tree.def part for now).   Presumably usable for both lvalues and
>> >>>> rvalues, where the thing it wraps is what's important.  It merely exists
>> >>>> to add an EXPR_LOCATION, for a usage of the wrapped thing.
>> >>>
>> >>>
>> >>> Yes, but please see with Jason, Richard and perhaps others if they are ok
>> >>> with that too before spending too much time in that direction.
>> >>> All occurrences of it would have to be folded away during the
>> >>> gimplification
>> >>> at latest, this shouldn't be something we use in the middle-end.
>> >>
>> >>
>> >> I'd expect LOCATION_EXPR be defined in c-family/c-common.def, not
>> >> tree.def.
>> >> And I'd think it shouldn't survive genericizing, thus never leak into the
>> >> ME.
>> >
>> >
>> > Makes sense.
>
> FWIW, attached is a work-in-progress patch for the LOCATION_EXPR
> approach.  This one
> * adds LOCATION_EXPR to cp/cp-tree.def, rather than to c-common.def (my
> thinking being that if it's only being used in the C++ FE, make it
> specific to it)
> * LOCATION_EXPR wrapper nodes are created for VAR_DECL, PARM_DECL (I'm
> not sure if I want to create them for STRING_CST)
> * strips them out in cp_genericize_r
> * verifies their absence in cp_gimplify_expr via a gcc_unreachable
> * lots of use of STRIP_LOCATION_EXPRS around places which expect a
> certain kind of node.  This feels like a game of whack-a-mole.  I've got
> increasing amounts of the C++ stdlib to parse, but am running into
> various folding issues:
>
> x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42: error:
> ‘(std::size_t)0’ is not a constant expression
>        static const size_t _S_alignment = 0;
>                                           ^
>
> which doesn't seem to be easily fixable; I'd already put a cp_fully_fold
> into cp/constexpr.c:verify_constant, but this is:
>
> (gdb) call debug_tree (t)
>  <nop_expr 0x7fffef03d580
>     type <integer_type 0x7ffff1632f18 size_t unsigned type_6 DI
>         size <integer_cst 0x7ffff1891e58 constant 64>
>         unit size <integer_cst 0x7ffff1891e70 constant 8>
>         align 64 symtab 0 alias set -1 canonical type 0x7ffff18959d8
> precision 64 min <integer_cst 0x7ffff18b3138 0> max <integer_cst
> 0x7ffff18a35e0 18446744073709551615>>
>     constant
>     arg 0 <location_expr 0x7fffef03d4e0
>         type <integer_type 0x7ffff18957e0 int asm_written public type_6
> SI
>             size <integer_cst 0x7ffff18b30a8 constant 32>
>             unit size <integer_cst 0x7ffff18b30c0 constant 4>
>             align 32 symtab -243642208 alias set -1 canonical type
> 0x7ffff18957e0 precision 32 min <integer_cst 0x7ffff18b3060 -2147483648>
> max <integer_cst 0x7ffff18b3078 2147483647>
>             pointer_to_this <pointer_type 0x7ffff18b7930>>
>         constant
>         arg 0 <integer_cst 0x7ffff18b31f8 constant 0>
>         /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 start: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 finish: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42>
>     /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 start: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 finish: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42>
>
> which isn't folded here by cp_fully_fold since we're in
> processing_template_decl; STRIP_NOPS etc can't do it since LOCATION_EXPR
> is frontend-specific.
>
> Any thoughts on how to work around this?
>
>
>> OTOH then the FEs need to strip trees of it if they ever want to use
>> sth from fold-const.c for example.  NON_LVALUE_EXPR is not
>> in c-family/c-common.def either... (and conveniently stripped with
>> STRIP_NOPS and friends).
>
> Just to clarify, do you mean "NON_LVALUE_EXPR is *not* conveniently
> stripped by STRIP_NOPS and friends"?

No, it _is_ conveniently stripped because it's a middle-end tree code.
All I wanted to say is that if you can make it work with a lang-tree-code
more power to you but I'd expect it to me much easier to use a middle-end
one if not just because of STRIP_NOPS and fold.

>  As far as I can tell, STRIP_NOPS
> etc are all frontend-independent, hence frontend-specific tree types
> can't be handled there.

Yes (we'd definitely not want a langhook here ;))

>> Ok, I _would_ like to move NON_LVALUE_EXPR to the C frontend
>> family as well...  so I guess that would be a nice starting project
>> and if you can make that work you can add LOCATION_EXPR to
>> the C family only.
>
> At a higher level, I'm nervous about feature-creep here, relative to the
> v2 patch; that one (mostly) worked, without creating a new tree type, in
> that it captured the locations (and thus ranges) for the leaf nodes of
> the parse tree for just long enough to let them be used when building
> more interesting expressions, which is where the range information is
> useful.

I think at this stage a new tree code isn't appropriate.

Richard.

> Dave
Marek Polacek Nov. 24, 2015, 12:09 p.m. UTC | #2
On Tue, Nov 24, 2015 at 05:58:30AM -0500, David Malcolm wrote:
> +/* Language-dependent macro for stripping away location wrapper nodes.  */
> +
> +#define STRIP_LOCATION_EXPRS(EXP) \
> +  while (TREE_CODE (EXP) == LOCATION_EXPR) \
> +    (EXP) = TREE_OPERAND ((EXP), 0)

This BTW implies that we might have a LOCATION_EXPR wrapped in another
LOCATION_EXPR, but I don't quite see how that could be useful?

	Marek
diff mbox

Patch

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 8cdda62..623c2ef 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -1087,6 +1087,9 @@  standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
   bool fromref = false;
   tree qualified_to;
 
+  if (expr)
+    STRIP_LOCATION_EXPRS (expr);
+
   to = non_reference (to);
   if (TREE_CODE (from) == REFERENCE_TYPE)
     {
@@ -5768,7 +5771,11 @@  build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
     case REALPART_EXPR:
     case IMAGPART_EXPR:
     case ABS_EXPR:
-      return cp_build_unary_op (code, arg1, candidates != 0, complain);
+      {
+	tree result = cp_build_unary_op (code, arg1, candidates != 0, complain);
+	protected_set_expr_location (result, loc);
+	return result;
+      }
 
     case ARRAY_REF:
       return cp_build_array_ref (input_location, arg1, arg2, complain);
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 459173d..1115403 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1460,6 +1460,9 @@  reduced_constant_expression_p (tree t)
 	  return false;
       return true;
 
+    case LOCATION_EXPR:
+      return reduced_constant_expression_p (TREE_OPERAND (t, 0));
+
     default:
       /* FIXME are we calling this too much?  */
       return initializer_constant_valid_p (t, TREE_TYPE (t)) != NULL_TREE;
@@ -1479,6 +1482,8 @@  static bool
 verify_constant (tree t, bool allow_non_constant, bool *non_constant_p,
 		 bool *overflow_p)
 {
+  t = cp_fully_fold (t);
+
   if (!*non_constant_p && !reduced_constant_expression_p (t))
     {
       if (!allow_non_constant)
@@ -3563,6 +3568,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case VIEW_CONVERT_EXPR:
     case NOP_EXPR:
     case UNARY_PLUS_EXPR:
+    case LOCATION_EXPR:
       {
 	enum tree_code tcode = TREE_CODE (t);
 	tree oldop = TREE_OPERAND (t, 0);
@@ -3589,7 +3595,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	if (op == oldop && tcode != UNARY_PLUS_EXPR)
 	  /* We didn't fold at the top so we could check for ptr-int
 	     conversion.  */
-	  return fold (t);
+	  return cp_fully_fold (t);
 	if (tcode == UNARY_PLUS_EXPR)
 	  r = fold_convert (TREE_TYPE (t), op);
 	else
@@ -4753,6 +4759,9 @@  potential_constant_expression_1 (tree t, bool want_rval, bool strict,
     case EMPTY_CLASS_EXPR:
       return false;
 
+    case LOCATION_EXPR:
+      return RECUR (TREE_OPERAND (t, 0), want_rval);
+
     default:
       if (objc_is_property_ref (t))
 	return false;
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 99d0cfb..ca989d4 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -702,6 +702,9 @@  cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
     case BREAK_STMT:
       gcc_unreachable ();
 
+    case LOCATION_EXPR:
+      gcc_unreachable ();
+
     case OMP_FOR:
     case OMP_SIMD:
     case OMP_DISTRIBUTE:
@@ -1356,6 +1359,13 @@  cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 	   || TREE_CODE (stmt) == OMP_DISTRIBUTE
 	   || TREE_CODE (stmt) == OMP_TASKLOOP)
     genericize_omp_for_stmt (stmt_p, walk_subtrees, data);
+  else if (TREE_CODE (stmt) == LOCATION_EXPR)
+    {
+      /* Strip away LOCATION_EXPR nodes.  */
+      *stmt_p = TREE_OPERAND (stmt, 0);
+      /* *stmt_p has changed, tail recurse to handle it again.  */
+      return cp_genericize_r (stmt_p, walk_subtrees, data);
+    }
   else if ((flag_sanitize
 	    & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_VPTR))
 	   && !wtd->no_sanitize_p)
@@ -2213,6 +2223,10 @@  cp_fold (tree x)
       x = fold (x);
       break;
 
+    case LOCATION_EXPR:
+      x = cp_fold (TREE_OPERAND (x, 0));
+      break;
+
     default:
       return org_x;
     }
diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
index 2758252..816b236 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -306,6 +306,7 @@  cp_common_init_ts (void)
   MARK_TS_TYPED (UNARY_RIGHT_FOLD_EXPR);
   MARK_TS_TYPED (BINARY_LEFT_FOLD_EXPR);
   MARK_TS_TYPED (BINARY_RIGHT_FOLD_EXPR);
+  MARK_TS_TYPED (LOCATION_EXPR);
 }
 
 #include "gt-cp-cp-objcp-common.h"
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 7df72c5..20e6b11 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -582,6 +582,12 @@  DEFTREECODE (PARM_CONSTR, "parm_constr", tcc_expression, 2)
 DEFTREECODE (CONJ_CONSTR, "conj_constr", tcc_expression, 2)
 DEFTREECODE (DISJ_CONSTR, "disj_constr", tcc_expression, 2)
 
+/* Wrapper used by the C++ frontend during parsing to add a source code
+   location to an expression, either one that doesn't have one (such as
+   an INTEGER_CST), or to a usage of a variable (e.g. PARAM_DECL or
+   VAR_DECL), where we want to record the site in the source where the
+   variable was *used* rather than where it was declared.  */
+DEFTREECODE (LOCATION_EXPR, "location_expr", tcc_unary, 1)
 
 /*
 Local variables:
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 160bf1e..4482112 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -256,6 +256,13 @@  c-common.h, not after.
 #define THUNK_FUNCTION_CHECK(NODE) (NODE)
 #endif
 
+/* Language-dependent macro for stripping away location wrapper nodes.  */
+
+#define STRIP_LOCATION_EXPRS(EXP) \
+  while (TREE_CODE (EXP) == LOCATION_EXPR) \
+    (EXP) = TREE_OPERAND ((EXP), 0)
+
+
 /* Language-dependent contents of an identifier.  */
 
 struct GTY(()) lang_identifier {
@@ -271,6 +278,7 @@  struct GTY(()) lang_identifier {
 inline lang_identifier*
 identifier_p (tree t)
 {
+  STRIP_LOCATION_EXPRS (t);
   if (TREE_CODE (t) == IDENTIFIER_NODE)
     return (lang_identifier*) t;
   return NULL;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 38548c7..8ac6ca1 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -2750,6 +2750,10 @@  dump_expr (cxx_pretty_printer *pp, tree t, int flags)
       pp_string (pp, M_("*this"));
       break;
 
+    case LOCATION_EXPR:
+      dump_expr (pp, TREE_OPERAND (t, 0), flags);
+      break;
+
       /*  This list is incomplete, but should suffice for now.
 	  It is very important that `sorry' does not call
 	  `report_error_function'.  That could cause an infinite loop.  */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 0e1116b..aea0a75 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -1414,6 +1414,8 @@  make_id_declarator (tree qualifying_scope, tree unqualified_name,
   if (qualifying_scope && TYPE_P (qualifying_scope))
     qualifying_scope = TYPE_MAIN_VARIANT (qualifying_scope);
 
+  STRIP_LOCATION_EXPRS (unqualified_name);
+
   gcc_assert (identifier_p (unqualified_name)
 	      || TREE_CODE (unqualified_name) == BIT_NOT_EXPR
 	      || TREE_CODE (unqualified_name) == TEMPLATE_ID_EXPR);
@@ -1976,7 +1978,7 @@  static tree cp_parser_postfix_open_square_expression
 static tree cp_parser_postfix_dot_deref_expression
   (cp_parser *, enum cpp_ttype, tree, bool, cp_id_kind *, location_t);
 static vec<tree, va_gc> *cp_parser_parenthesized_expression_list
-  (cp_parser *, int, bool, bool, bool *);
+  (cp_parser *, int, bool, bool, bool *, location_t * = NULL);
 /* Values for the second parameter of cp_parser_parenthesized_expression_list.  */
 enum { non_attr = 0, normal_attr = 1, id_attr = 2 };
 static void cp_parser_pseudo_destructor_name
@@ -3665,6 +3667,18 @@  cp_parser_pop_lexer (cp_parser *parser)
   cp_lexer_set_source_position_from_token (parser->lexer->next_token);
 }
 
+/* FIXME.  */
+static tree
+wrap_with_location (tree value, location_t loc)
+{
+#if 1
+  tree wrapper = build1_loc (loc, LOCATION_EXPR, TREE_TYPE (value), value);
+  return wrapper;
+#else
+  return value;
+#endif
+}
+
 /* Lexical conventions [gram.lex]  */
 
 /* Parse an identifier.  Returns an IDENTIFIER_NODE representing the
@@ -3678,7 +3692,10 @@  cp_parser_identifier (cp_parser* parser)
   /* Look for the identifier.  */
   token = cp_parser_require (parser, CPP_NAME, RT_NAME);
   /* Return the value.  */
-  return token ? token->u.value : error_mark_node;
+  if (token)
+    return token->u.value; // FIXME //use_as_rvalue (token->u.value, token->location); // FIXME: what if an lvalue?
+  else
+    return error_mark_node;
 }
 
 /* Parse a sequence of adjacent string constants.  Returns a
@@ -3717,6 +3734,8 @@  cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
       return error_mark_node;
     }
 
+  location_t loc = tok->location;
+
   if (cpp_userdef_string_p (tok->type))
     {
       string_tree = USERDEF_LITERAL_VALUE (tok->u.value);
@@ -3754,11 +3773,13 @@  cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
     }
   else
     {
+      location_t last_tok_loc;
       gcc_obstack_init (&str_ob);
       count = 0;
 
       do
 	{
+	  last_tok_loc = tok->location;
 	  cp_lexer_consume_token (parser->lexer);
 	  count++;
 	  str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree);
@@ -3813,6 +3834,13 @@  cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 	}
       while (cp_parser_is_string_literal (tok));
 
+      /* A string literal built by concatenation has its caret=start at
+	 the start of the initial string, and its finish at the finish of
+	 the final string literal.  */
+      loc = make_location (loc, loc,
+			   get_range_from_loc (line_table,
+					       last_tok_loc).m_finish);
+
       strs = (cpp_string *) obstack_finish (&str_ob);
     }
 
@@ -3865,7 +3893,11 @@  cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
   if (count > 1)
     obstack_free (&str_ob, 0);
 
+#if 0
   return value;
+#else
+  return wrap_with_location (value, loc);
+#endif
 }
 
 /* Look up a literal operator with the name and the exact arguments.  */
@@ -4614,7 +4646,7 @@  cp_parser_primary_expression (cp_parser *parser,
 	  if (!cast_p)
 	    cp_parser_non_integral_constant_expression (parser, NIC_FLOAT);
 	}
-      return token->u.value;
+      return wrap_with_location (token->u.value, token->location);
 
     case CPP_CHAR_USERDEF:
     case CPP_CHAR16_USERDEF:
@@ -4675,6 +4707,8 @@  cp_parser_primary_expression (cp_parser *parser,
 	tree expr;
 	bool saved_greater_than_is_operator_p;
 
+	location_t open_paren_loc = token->location;
+
 	/* Consume the `('.  */
 	cp_lexer_consume_token (parser->lexer);
 	/* Within a parenthesized expression, a `>' token is always
@@ -4719,7 +4753,11 @@  cp_parser_primary_expression (cp_parser *parser,
 	   template-parameter-list now.  */
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
+
 	/* Consume the `)'.  */
+	token = cp_lexer_peek_token (parser->lexer);
+	location_t close_paren_loc = token->location;
+        set_source_range (expr, open_paren_loc, close_paren_loc);
 	if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)
 	    && !cp_parser_uncommitted_to_tentative_parse_p (parser))
 	  cp_parser_skip_to_end_of_statement (parser);
@@ -4760,20 +4798,20 @@  cp_parser_primary_expression (cp_parser *parser,
 	  /* These two are the boolean literals.  */
 	case RID_TRUE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_true_node;
+	  return wrap_with_location (boolean_true_node, token->location);
 	case RID_FALSE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_false_node;
+	  return wrap_with_location (boolean_false_node, token->location);
 
 	  /* The `__null' literal.  */
 	case RID_NULL:
 	  cp_lexer_consume_token (parser->lexer);
-	  return null_node;
+	  return wrap_with_location (null_node, token->location);
 
 	  /* The `nullptr' literal.  */
 	case RID_NULLPTR:
 	  cp_lexer_consume_token (parser->lexer);
-	  return nullptr_node;
+	  return wrap_with_location (nullptr_node, token->location);
 
 	  /* Recognize the `this' keyword.  */
 	case RID_THIS:
@@ -4921,6 +4959,7 @@  cp_parser_primary_expression (cp_parser *parser,
     case CPP_TEMPLATE_ID:
     case CPP_NESTED_NAME_SPECIFIER:
       {
+      id_expression:
 	tree id_expression;
 	tree decl;
 	const char *error_msg;
@@ -4928,7 +4967,6 @@  cp_parser_primary_expression (cp_parser *parser,
 	bool done;
 	cp_token *id_expr_token;
 
-      id_expression:
 	/* Parse the id-expression.  */
 	id_expression
 	  = cp_parser_id_expression (parser,
@@ -6116,6 +6154,8 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
   loc = token->location;
+  location_t start_loc = get_range_from_loc (line_table, loc).m_start;
+
   /* Some of the productions are determined by keywords.  */
   keyword = token->keyword;
   switch (keyword)
@@ -6159,7 +6199,10 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	/* And the expression which is being cast.  */
 	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
 	expression = cp_parser_expression (parser, & idk, /*cast_p=*/true);
-	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	cp_token *close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN,
+						   RT_CLOSE_PAREN);
+	location_t end_loc = close_paren ?
+	  close_paren->location : UNKNOWN_LOCATION;
 
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
@@ -6192,6 +6235,14 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  default:
 	    gcc_unreachable ();
 	  }
+
+	/* Construct a location e.g. :
+	     reinterpret_cast <int *> (expr)
+	     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	   ranging from the start of the "*_cast" token to the final closing
+	   paren, with the caret at the start.  */
+	location_t cp_cast_loc = make_location (start_loc, start_loc, end_loc);
+	protected_set_expr_location (postfix_expression, cp_cast_loc);
       }
       break;
 
@@ -6471,6 +6522,8 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 							postfix_expression,
 							false,
 							decltype_p);
+          set_source_range (postfix_expression, start_loc,
+                            EXPR_LOCATION (postfix_expression));
 	  idk = CP_ID_KIND_NONE;
           is_member_access = false;
 	  break;
@@ -6484,6 +6537,7 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    bool saved_non_integral_constant_expression_p = false;
 	    tsubst_flags_t complain = complain_flags (decltype_p);
 	    vec<tree, va_gc> *args;
+	    location_t close_paren_loc;
 
             is_member_access = false;
 
@@ -6502,7 +6556,8 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    args = (cp_parser_parenthesized_expression_list
 		    (parser, non_attr,
 		     /*cast_p=*/false, /*allow_expansion_p=*/true,
-		     /*non_constant_p=*/NULL));
+		     /*non_constant_p=*/NULL,
+		     /*close_paren_loc=*/&close_paren_loc));
 	    if (is_builtin_constant_p)
 	      {
 		parser->integral_constant_expression_p
@@ -6644,7 +6699,10 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 				    koenig_p,
 				    complain);
 
-	    protected_set_expr_location (postfix_expression, token->location);
+	    location_t combined_loc = make_location (token->location,
+						     start_loc,
+						     close_paren_loc);
+	    protected_set_expr_location (postfix_expression, combined_loc);
 
 	    /* The POSTFIX_EXPRESSION is certainly no longer an id.  */
 	    idk = CP_ID_KIND_NONE;
@@ -6705,7 +6763,9 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  if (pidk_return != NULL)
 	    * pidk_return = idk;
           if (member_access_only_p)
-            return is_member_access? postfix_expression : error_mark_node;
+            return is_member_access
+              ? postfix_expression
+              : error_mark_node;
           else
             return postfix_expression;
 	}
@@ -7107,7 +7167,8 @@  cp_parser_parenthesized_expression_list (cp_parser* parser,
 					 int is_attribute_list,
 					 bool cast_p,
                                          bool allow_expansion_p,
-					 bool *non_constant_p)
+					 bool *non_constant_p,
+					 location_t *close_paren_loc)
 {
   vec<tree, va_gc> *expression_list;
   bool fold_expr_p = is_attribute_list != non_attr;
@@ -7211,6 +7272,9 @@  cp_parser_parenthesized_expression_list (cp_parser* parser,
 	cp_lexer_consume_token (parser->lexer);
       }
 
+  if (close_paren_loc)
+    *close_paren_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
     {
       int ending;
@@ -7611,6 +7675,14 @@  cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 				     /*cast_p=*/false,
 				     /*decltype*/false,
 				     pidk);
+
+      /* Make a location:
+	    OP_TOKEN  CAST_EXPRESSION
+	    ^~~~~~~~~~~~~~~~~~~~~~~~~
+	 with start==caret at the operator token, and
+	 extending to the end of the cast_expression.  */
+      loc = make_location (loc, loc, get_expr_finish (cast_expression));
+
       /* Now, build an appropriate representation.  */
       switch (unary_operator)
 	{
@@ -7663,6 +7735,8 @@  cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	  gcc_unreachable ();
 	}
 
+      protected_set_expr_location (expression, loc);
+
       if (non_constant_p != NIC_NONE
 	  && cp_parser_non_integral_constant_expression (parser,
 							 non_constant_p))
@@ -7726,6 +7800,8 @@  cp_parser_new_expression (cp_parser* parser)
   tree nelts = NULL_TREE;
   tree ret;
 
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   /* Look for the optional `::' operator.  */
   global_scope_p
     = (cp_parser_global_scope_opt (parser,
@@ -7820,6 +7896,17 @@  cp_parser_new_expression (cp_parser* parser)
   if (initializer != NULL)
     release_tree_vector (initializer);
 
+  /* Construct a location e.g.:
+        ptr = new int[100]
+              ^~~~~~~~~~~~
+     with caret == start at the start of the "new" token, and the end
+     at the end of the final token we consumed.  */
+  cp_token *end_tok = cp_lexer_previous_token (parser->lexer);
+  location_t end_loc = get_range_from_loc (line_table,
+                                           end_tok->location).m_finish;
+  location_t combined_loc = make_location (start_loc, start_loc, end_loc);
+  protected_set_expr_location (ret, combined_loc);
+
   return ret;
 }
 
@@ -8213,7 +8300,9 @@  cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
       parser->type_definition_forbidden_message
 	= G_("types may not be defined in casts");
       /* Consume the `('.  */
-      cp_lexer_consume_token (parser->lexer);
+      cp_token *open_paren = cp_lexer_consume_token (parser->lexer);
+      location_t open_paren_loc = open_paren->location;
+
       /* A very tricky bit is that `(struct S) { 3 }' is a
 	 compound-literal (which we permit in C++ as an extension).
 	 But, that construct is not a cast-expression -- it is a
@@ -8315,7 +8404,15 @@  cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 		return error_mark_node;
 
 	      /* Perform the cast.  */
-	      expr = build_c_cast (input_location, type, expr);
+	      /* Make a location:
+		   (TYPE) EXPR
+		   ^~~~~~~~~~~
+		 with start==caret at the open paren, extending to the
+		 end of "expr".  */
+	      location_t cast_loc = make_location (open_paren_loc,
+						   open_paren_loc,
+						   get_expr_finish (expr));
+	      expr = build_c_cast (cast_loc, type, expr);
 	      return expr;
 	    }
 	}
@@ -8558,6 +8655,11 @@  cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 				      maybe_constant_value (rhs));
 
       overload = NULL;
+
+      location_t combined_loc = make_location (current.loc,
+					       get_expr_start (current.lhs),
+					       get_expr_finish (rhs));
+
       /* ??? Currently we pass lhs_type == ERROR_MARK and rhs_type ==
 	 ERROR_MARK for everything that is not a binary expression.
 	 This makes warn_about_parentheses miss some warnings that
@@ -8568,18 +8670,18 @@  cp_parser_binary_expression (cp_parser* parser, bool cast_p,
       if (no_toplevel_fold_p
 	  && lookahead_prec <= current.prec
 	  && sp == stack)
-	current.lhs = build2 (current.tree_type,
-			      TREE_CODE_CLASS (current.tree_type)
-			      == tcc_comparison
-			      ? boolean_type_node : TREE_TYPE (current.lhs),
-			      current.lhs, rhs);
+	current.lhs = build2_loc (combined_loc,
+				  current.tree_type,
+				  TREE_CODE_CLASS (current.tree_type)
+				  == tcc_comparison
+				  ? boolean_type_node : TREE_TYPE (current.lhs),
+				  current.lhs, rhs);
       else
-	current.lhs = build_x_binary_op (current.loc, current.tree_type,
+	current.lhs = build_x_binary_op (combined_loc, current.tree_type,
 					 current.lhs, current.lhs_type,
 					 rhs, rhs_type, &overload,
 					 complain_flags (decltype_p));
       current.lhs_type = current.tree_type;
-      protected_set_expr_location (current.lhs, current.loc);
 
       /* If the binary operator required the use of an overloaded operator,
 	 then this expression cannot be an integral constant-expression.
@@ -8662,6 +8764,15 @@  cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
   c_inhibit_evaluation_warnings -=
     folded_logical_or_expr == truthvalue_true_node;
 
+  /* Make a location:
+       LOGICAL_OR_EXPR ? EXPR : ASSIGNMENT_EXPR
+       ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
+     with the caret at the "?", ranging from the start of
+     the logical_or_expr to the end of the assignment_expr.  */
+  loc = make_location (loc,
+		       get_expr_start (logical_or_expr),
+		       get_expr_finish (assignment_expr));
+
   /* Build the conditional-expression.  */
   return build_x_conditional_expr (loc, logical_or_expr,
 				   expr,
@@ -8717,7 +8828,8 @@  cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 	      location_t saved_input_location;
 
 	      /* Parse the right-hand side of the assignment.  */
-	      tree rhs = cp_parser_initializer_clause (parser, &non_constant_p);
+	      tree rhs = cp_parser_initializer_clause (parser,
+							  &non_constant_p);
 
 	      if (BRACE_ENCLOSED_INITIALIZER_P (rhs))
 		maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
@@ -8728,13 +8840,22 @@  cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 							      NIC_ASSIGNMENT))
 		return error_mark_node;
 	      /* Build the assignment expression.  Its default
-		 location is the location of the '=' token.  */
+		 location:
+		   LHS = RHS
+		   ~~~~^~~~~
+		 is the location of the '=' token as the
+		 caret, ranging from the start of the lhs to the
+		 end of the rhs.  */
 	      saved_input_location = input_location;
+	      loc = make_location (loc,
+				   get_expr_start (expr),
+				   get_expr_finish (rhs));
 	      input_location = loc;
 	      expr = build_x_modify_expr (loc, expr,
 					  assignment_operator,
 					  rhs,
 					  complain_flags (decltype_p));
+	      protected_set_expr_location (expr, loc);
 	      input_location = saved_input_location;
 	    }
 	}
@@ -8875,9 +8996,17 @@  cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
       if (!expression)
 	expression = assignment_expression;
       else
-	expression = build_x_compound_expr (loc, expression,
-					    assignment_expression,
-					    complain_flags (decltype_p));
+	{
+	  /* Create a location with caret at the comma, ranging
+	     from the start of the LHS to the end of the RHS.  */
+	  loc = make_location (loc,
+			       get_expr_start (expression),
+			       get_expr_finish (assignment_expression));
+	  expression = build_x_compound_expr (loc, expression,
+					      assignment_expression,
+					      complain_flags (decltype_p));
+	  //expression.set_location (loc);
+	}
       /* If the next token is not a comma, or we're in a fold-expression, then
 	 we are done with the expression.  */
       if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)
@@ -12501,6 +12630,7 @@  cp_parser_linkage_specification (cp_parser* parser)
 
   /* Look for the string-literal.  */
   linkage = cp_parser_string_literal (parser, false, false);
+  STRIP_LOCATION_EXPRS (linkage);
 
   /* Transform the literal into an identifier.  If the literal is a
      wide-character string, or contains embedded NULs, then we can't
@@ -13333,6 +13463,9 @@  cp_parser_operator (cp_parser* parser)
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
+
+  location_t start_loc = token->location;
+
   /* Figure out which operator we have.  */
   switch (token->type)
     {
@@ -13349,7 +13482,7 @@  cp_parser_operator (cp_parser* parser)
 	  break;
 
 	/* Consume the `new' or `delete' token.  */
-	cp_lexer_consume_token (parser->lexer);
+	location_t end_loc = cp_lexer_consume_token (parser->lexer)->location;
 
 	/* Peek at the next token.  */
 	token = cp_lexer_peek_token (parser->lexer);
@@ -13360,7 +13493,8 @@  cp_parser_operator (cp_parser* parser)
 	    /* Consume the `[' token.  */
 	    cp_lexer_consume_token (parser->lexer);
 	    /* Look for the `]' token.  */
-	    cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
+	    end_loc = cp_parser_require (parser, CPP_CLOSE_SQUARE,
+                                         RT_CLOSE_SQUARE)->location;
 	    id = ansi_opname (op == NEW_EXPR
 			      ? VEC_NEW_EXPR : VEC_DELETE_EXPR);
 	  }
@@ -13368,7 +13502,9 @@  cp_parser_operator (cp_parser* parser)
 	else
 	  id = ansi_opname (op);
 
-	return id;
+	location_t loc = make_location (start_loc, start_loc, end_loc);
+
+	return id; // FIXME: what to do about loc
       }
 
     case CPP_PLUS:
@@ -13614,7 +13750,7 @@  cp_parser_operator (cp_parser* parser)
       id = error_mark_node;
     }
 
-  return id;
+  return id; // FIXME: cp_expr (id, start_loc);
 }
 
 /* Parse a template-declaration.
@@ -17357,6 +17493,7 @@  cp_parser_using_declaration (cp_parser* parser,
 	  decl = cp_parser_lookup_name_simple (parser,
 					       identifier,
 					       token->location);
+	  STRIP_LOCATION_EXPRS (decl);
 	  if (decl == error_mark_node)
 	    cp_parser_name_lookup_error (parser, identifier,
 					 decl, NLE_NULL,
@@ -17623,6 +17760,8 @@  cp_parser_asm_definition (cp_parser* parser)
     return;
   /* Look for the string.  */
   string = cp_parser_string_literal (parser, false, false);
+  STRIP_LOCATION_EXPRS (string);
+
   if (string == error_mark_node)
     {
       cp_parser_skip_to_closing_parenthesis (parser, true, false,
@@ -20696,6 +20835,7 @@  cp_parser_class_name (cp_parser *parser,
     }
 
   decl = cp_parser_maybe_treat_template_as_class (decl, class_head_p);
+  STRIP_LOCATION_EXPRS (decl);
 
   /* If this is a typename, create a TYPENAME_TYPE.  */
   if (typename_p && decl != error_mark_node)
@@ -22876,6 +23016,7 @@  cp_parser_asm_specification_opt (cp_parser* parser)
 
   /* Look for the string-literal.  */
   asm_specification = cp_parser_string_literal (parser, false, false);
+  STRIP_LOCATION_EXPRS (asm_specification);
 
   /* Look for the `)'.  */
   cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
@@ -22928,6 +23069,7 @@  cp_parser_asm_operand_list (cp_parser* parser)
 	name = NULL_TREE;
       /* Look for the string-literal.  */
       string_literal = cp_parser_string_literal (parser, false, false);
+      STRIP_LOCATION_EXPRS (string_literal);
 
       /* Look for the `('.  */
       cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
@@ -22976,6 +23118,7 @@  cp_parser_asm_clobber_list (cp_parser* parser)
 
       /* Look for the string literal.  */
       string_literal = cp_parser_string_literal (parser, false, false);
+      STRIP_LOCATION_EXPRS (string_literal);
       /* Add it to the list.  */
       clobbers = tree_cons (NULL_TREE, string_literal, clobbers);
       /* If the next token is not a `,', then the list is
@@ -24317,7 +24460,11 @@  cp_parser_lookup_name (cp_parser *parser, tree name,
 
   maybe_record_typedef_use (decl);
 
-  return decl;
+  if (TREE_CODE (decl) == VAR_DECL
+      || TREE_CODE (decl) == PARM_DECL)
+    return wrap_with_location (decl, name_location);
+  else
+    return decl;
 }
 
 /* Like cp_parser_lookup_name, but for use in the typical case where
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index e7e5d8e..df95da9 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2463,7 +2463,12 @@  finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
 tree
 finish_increment_expr (tree expr, enum tree_code code)
 {
-  return build_x_unary_op (input_location, code, expr, tf_warning_or_error);
+  /* input_location holds the location of the trailing operator token.  */
+  tree result = build_x_unary_op (input_location, code, expr,
+				     tf_warning_or_error);
+  set_source_range (result, get_expr_start (expr),
+		    get_finish (input_location));
+  return result;
 }
 
 /* Finish a use of `this'.  Returns an expression for `this'.  */
@@ -2558,10 +2563,12 @@  finish_pseudo_destructor_expr (tree object, tree scope, tree destructor,
 /* Finish an expression of the form CODE EXPR.  */
 
 tree
-finish_unary_op_expr (location_t loc, enum tree_code code, tree expr,
+finish_unary_op_expr (location_t op_loc, enum tree_code code, tree expr,
 		      tsubst_flags_t complain)
 {
-  tree result = build_x_unary_op (loc, code, expr, complain);
+  location_t combined_loc = make_location (op_loc,
+					   op_loc, get_expr_finish (expr));
+  tree result = build_x_unary_op (combined_loc, code, expr, complain);
   tree result_ovl, expr_ovl;
 
   if (!(complain & tf_warning))
@@ -2581,9 +2588,9 @@  finish_unary_op_expr (location_t loc, enum tree_code code, tree expr,
     result_ovl = cp_fully_fold (result_ovl);
 
   if (CONSTANT_CLASS_P (result_ovl) && TREE_OVERFLOW_P (result_ovl))
-    overflow_warning (input_location, result_ovl);
+    overflow_warning (combined_loc, result_ovl);
 
-  return result;
+  return result; // cp_expr (result, combined_loc);
 }
 
 /* Finish a compound-literal expression.  TYPE is the type to which
@@ -3669,7 +3676,7 @@  finish_id_expression (tree id_expression,
 	}
     }
 
-  return decl;
+  return decl; // return use_as_rvalue (decl); // FIXME: is this neeeded?
 }
 
 /* Implement the __typeof keyword: Return the type of EXPR, suitable for
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index d2db31a..c68bba0 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -56,6 +56,8 @@  lvalue_kind (const_tree ref)
   cp_lvalue_kind op1_lvalue_kind = clk_none;
   cp_lvalue_kind op2_lvalue_kind = clk_none;
 
+  STRIP_LOCATION_EXPRS (ref);
+
   /* Expressions of reference type are sometimes wrapped in
      INDIRECT_REFs.  INDIRECT_REFs are just internal compiler
      representation, not part of the language, so we have to look
@@ -3598,6 +3600,7 @@  check_abi_tag_args (tree args, tree name)
   for (tree arg = args; arg; arg = TREE_CHAIN (arg))
     {
       tree elt = TREE_VALUE (arg);
+      STRIP_LOCATION_EXPRS (elt);
       if (TREE_CODE (elt) != STRING_CST
 	  || (!same_type_ignoring_top_level_qualifiers_p
 	      (strip_array_types (TREE_TYPE (elt)),
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 5f7d4bb..27df803 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -36,6 +36,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-objc.h"
 #include "c-family/c-ubsan.h"
 #include "params.h"
+//#include "print-tree.h"
 
 static tree cp_build_addr_expr_strict (tree, tsubst_flags_t);
 static tree cp_build_function_call (tree, tree, tsubst_flags_t);
@@ -2435,7 +2436,11 @@  build_class_member_access_expr (tree object, tree member,
 	  member_type = cp_build_qualified_type (member_type, type_quals);
 	}
 
-      result = build3_loc (input_location, COMPONENT_REF, member_type,
+      location_t combined_loc =
+	make_location (input_location,
+		       get_expr_start (object),
+		       get_finish (input_location));
+      result = build3_loc (combined_loc, COMPONENT_REF, member_type,
 			   object, member, NULL_TREE);
 
       /* Mark the expression const or volatile, as appropriate.  Even
@@ -3669,6 +3674,12 @@  convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
       tree type = typetail ? TREE_VALUE (typetail) : 0;
       tree val = (**values)[i];
 
+#if 0
+      fprintf (stderr, "arg %i at start of loop body\n", i);
+      debug_tree (val);
+      inform (EXPR_LOCATION (val), "arg %i at start of loop body", i);
+#endif
+
       if (val == error_mark_node || type == error_mark_node)
 	return -1;
 
@@ -3683,12 +3694,14 @@  convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
             return -1;
 	}
 
+#if 0
       /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
 	 Strip such NOP_EXPRs, since VAL is used in non-lvalue context.  */
       if (TREE_CODE (val) == NOP_EXPR
 	  && TREE_TYPE (val) == TREE_TYPE (TREE_OPERAND (val, 0))
 	  && (type == 0 || TREE_CODE (type) != REFERENCE_TYPE))
 	val = TREE_OPERAND (val, 0);
+#endif
 
       if (type == 0 || TREE_CODE (type) != REFERENCE_TYPE)
 	{
@@ -3746,6 +3759,12 @@  convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
 
       if (typetail)
 	typetail = TREE_CHAIN (typetail);
+
+#if 0
+      fprintf (stderr, "values[%i] at end of loop body\n", i);
+      debug_tree ((**values)[i]);
+      inform (EXPR_LOCATION ((**values)[i]), "values[%i] at end of loop body", i);
+#endif
     }
 
   if (typetail != 0 && typetail != void_list_node)
@@ -4024,7 +4043,9 @@  cp_build_binary_op (location_t location,
     }
 
   /* Strip NON_LVALUE_EXPRs, etc., since we aren't using as an lvalue.  */
+  STRIP_LOCATION_EXPRS (op0);
   STRIP_TYPE_NOPS (op0);
+  STRIP_LOCATION_EXPRS (op1);
   STRIP_TYPE_NOPS (op1);
 
   /* DTRT if one side is an overloaded function, but complain about it.  */
@@ -5102,7 +5123,7 @@  cp_build_binary_op (location_t location,
 	instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
     }
 
-  result = build2 (resultcode, build_type, op0, op1);
+  result = build2_loc (location, resultcode, build_type, op0, op1);
   if (final_type != 0)
     result = cp_convert (final_type, result, complain);
 
@@ -5429,7 +5450,7 @@  build_nop (tree type, tree expr)
 {
   if (type == error_mark_node || error_operand_p (expr))
     return expr;
-  return build1 (NOP_EXPR, type, expr);
+  return build1_loc (EXPR_LOCATION (expr), NOP_EXPR, type, expr);
 }
 
 /* Take the address of ARG, whatever that means under C++ semantics.
@@ -7254,9 +7275,11 @@  build_const_cast (tree type, tree expr, tsubst_flags_t complain)
 /* Like cp_build_c_cast, but for the c-common bits.  */
 
 tree
-build_c_cast (location_t /*loc*/, tree type, tree expr)
+build_c_cast (location_t loc, tree type, tree expr)
 {
-  return cp_build_c_cast (type, expr, tf_warning_or_error);
+  tree result = cp_build_c_cast (type, expr, tf_warning_or_error);
+  protected_set_expr_location (result, loc);
+  return result;
 }
 
 /* Build an expression representing an explicit C-style cast to type
@@ -7784,7 +7807,10 @@  build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 	  return rval;
 	}
     }
-  return cp_build_modify_expr (lhs, modifycode, rhs, complain);
+
+  tree result = cp_build_modify_expr (lhs, modifycode, rhs, complain);
+  protected_set_expr_location (result, loc);
+  return result;
 }
 
 /* Helper function for get_delta_difference which assumes FROM is a base
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 839091c..6d58bda 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1740,7 +1740,9 @@  build_x_arrow (location_t loc, tree expr, tsubst_flags_t complain)
 	  return expr;
 	}
 
-      return cp_build_indirect_ref (last_rval, RO_NULL, complain);
+      tree result = cp_build_indirect_ref (last_rval, RO_NULL, complain);
+      protected_set_expr_location (result, loc);
+      return result;
     }
 
   if (complain & tf_error)
diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index 9e51b95..072f3a5 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -779,7 +779,7 @@  diagnostic_show_locus (diagnostic_context * context,
 {
   if (!context->show_caret
       || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION
-      || diagnostic_location (diagnostic, 0) == context->last_location)
+      /*|| diagnostic_location (diagnostic, 0) == context->last_location*/)
     return;
 
   context->last_location = diagnostic_location (diagnostic, 0);
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c
new file mode 100644
index 0000000..e2e801e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c
@@ -0,0 +1,568 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret" } */
+
+/* This is a collection of unittests to verify that we're correctly
+   capturing the source code ranges of various kinds of expression.
+
+   It uses the various "diagnostic_test_*_expression_range_plugin"
+   plugins which handles "__emit_expression_range" by generating a warning
+   at the given source range of the input argument.  Each of the
+   different plugins do this at a different phase of the internal
+   representation (tree, gimple, etc), so we can verify that the
+   source code range information is valid at each phase.
+
+   We want to accept an expression of any type.  To do this in C, we
+   use variadic arguments, but C requires at least one argument before
+   the ellipsis, so we have a dummy one.  */
+
+extern void __emit_expression_range (int dummy, ...);
+
+int global;
+
+/* Simple expressions.  ************************************************/
+
+void test_literals (void)
+{
+  __emit_expression_range (0, 1066 ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 1066 );
+                               ^~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, "hello" ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, "hello" );
+                               ^~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_variables (int param)
+{
+  __emit_expression_range (0, param ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, param );
+                               ^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, global ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, global );
+                               ^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+#if 1
+
+void test_parentheses (int a, int b)
+{
+  __emit_expression_range (0, (a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) );
+                               ~~~^~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, (a + b) * (a - b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) * (a - b) );
+                               ~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, !(a && b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !(a && b) );
+                               ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Postfix expressions.  ************************************************/
+
+void test_array_reference (int *arr)
+{
+  __emit_expression_range (0, arr[100] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, arr[100] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+}
+
+int test_function_call (int p, int q, int r)
+{
+  __emit_expression_range (0, test_function_call (p, q, r) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, test_function_call (p, q, r) );
+                               ~~~~~~~~~~~~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+  return 0;
+}
+
+struct test_struct
+{
+  int field;
+};
+
+int test_structure_references (struct test_struct *ptr)
+{
+  struct test_struct local;
+  local.field = 42;
+
+  __emit_expression_range (0, local.field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, local.field );
+                               ~~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, ptr->field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ptr->field );
+                               ~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+int test_postfix_incdec (int i)
+{
+  __emit_expression_range (0, i++ ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i++ );
+                               ~^~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, i-- ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i-- );
+                               ~^~
+   { dg-end-multiline-output "" } */
+}
+
+/* Unary operators.  ****************************************************/
+
+int test_prefix_incdec (int i)
+{
+  __emit_expression_range (0, ++i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ++i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, --i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, --i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_address_operator (void)
+{
+  __emit_expression_range (0, &global ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &global );
+                               ^~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_indirection (int *ptr)
+{
+  __emit_expression_range (0, *ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *ptr );
+                               ^~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_unary_minus (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_ones_complement (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_logical_negation (int flag)
+{
+  __emit_expression_range (0, !flag ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !flag );
+                               ^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Casts.  ****************************************************/
+
+void test_cast (void *ptr)
+{
+  __emit_expression_range (0, (int *)ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (int *)ptr );
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, *(int *)0xdeadbeef ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *(int *)0xdeadbeef );
+                               ^~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+/* Binary operators.  *******************************************/
+
+void test_multiplicative_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs * rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs * rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs / rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs / rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs % rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs % rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_additive_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs + rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs + rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs - rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs - rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_shift_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs << rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs << rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >> rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >> rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_relational_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs < rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs < rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs > rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs > rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs <= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs <= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_equality_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs == rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs == rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs != rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs != rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_bitwise_binary_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs & rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs & rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs ^ rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs ^ rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs | rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs | rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_logical_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs && rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs && rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs || rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs || rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Conditional operator.  *******************************************/
+
+void test_conditional_operators (int flag, int on_true, int on_false)
+{
+  __emit_expression_range (0, flag ? on_true : on_false ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, flag ? on_true : on_false );
+                               ~~~~~^~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Assignment expressions.  *******************************************/
+
+void test_assignment_expressions (int dest, int other)
+{
+  __emit_expression_range (0, dest = other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest = other );
+                               ~~~~~^~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest *= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest *= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest /= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest /= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest %= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest %= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest += other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest += other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest -= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest -= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest <<= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest <<= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest >>= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest >>= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest &= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest &= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest ^= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest ^= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest |= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest |= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Comma operator.  *******************************************/
+
+void test_comma_operator (int a, int b)
+{
+  __emit_expression_range (0, (a++, a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a++, a + b) );
+                               ~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* 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)
+{
+  __emit_expression_range (0, "foo"[i] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, "foo"[i] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, &"foo" "bar" ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &"foo" "bar" );
+                               ^~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Examples of non-trivial expressions.  ****************************/
+
+extern double sqrt (double x);
+
+void test_quadratic (double a, double b, double c)
+{
+  __emit_expression_range (0, b * b - 4 * a * c ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, b * b - 4 * a * c );
+                               ~~~~~~^~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0,
+     (-b + sqrt (b * b - 4 * a * c))
+     / (2 * a)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+      (-b + sqrt (b * b - 4 * a * c))
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+      / (2 * a));
+      ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+/* C++-specific expresssions. ****************************************/
+
+void test_cp_literal_keywords (int a, int b)
+{
+  this; /* { dg-error "invalid use of 'this' in non-member function" } */
+/* { dg-begin-multiline-output "" }
+   this;
+   ^~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+class base {
+ public:
+  base ();
+  base (int i);
+  virtual ~base ();
+};
+class derived : public base { ~derived (); };
+
+void test_cp_casts (base *ptr)
+{
+  __emit_expression_range (0, dynamic_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dynamic_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, static_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, static_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, reinterpret_cast <int *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, reinterpret_cast <int *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, const_cast <base *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, const_cast <base *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_new (void)
+{
+  __emit_expression_range (0, ::new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ::new base);
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base);
+                               ^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base));
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base (42));
+                               ^~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base) (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base) (42));
+                               ^~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  /* TODO: placement new.  */
+}
+
+#endif
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c b/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
new file mode 100644
index 0000000..0e98702
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
@@ -0,0 +1,106 @@ 
+/* This plugin verifies the source-code location ranges of
+   expressions, at the pre-gimplification tree stage.  */
+/* { dg-options "-O" } */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "cp/cp-tree.h"
+#include "stringpool.h"
+#include "toplev.h"
+#include "basic-block.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "print-tree.h"
+
+int plugin_is_GPL_compatible;
+
+static void
+emit_warning (location_t loc)
+{
+  source_range src_range = get_range_from_loc (line_table, loc);
+  warning_at (loc, 0,
+	      "tree range %i:%i-%i:%i",
+	      LOCATION_LINE (src_range.m_start),
+	      LOCATION_COLUMN (src_range.m_start),
+	      LOCATION_LINE (src_range.m_finish),
+	      LOCATION_COLUMN (src_range.m_finish));
+}
+
+tree
+cb_walk_tree_fn (tree * tp, int * walk_subtrees,
+		 void * data ATTRIBUTE_UNUSED)
+{
+  if (TREE_CODE (*tp) != CALL_EXPR)
+    return NULL_TREE;
+
+  tree call_expr = *tp;
+  tree fn = CALL_EXPR_FN (call_expr);
+  if (TREE_CODE (fn) != ADDR_EXPR)
+    return NULL_TREE;
+  fn = TREE_OPERAND (fn, 0);
+  STRIP_LOCATION_EXPRS (fn);
+  if (TREE_CODE (fn) != FUNCTION_DECL)
+    return NULL_TREE;
+  if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fn)), "__emit_expression_range"))
+    return NULL_TREE;
+
+  /* Get arg 1; print it! */
+  tree arg = CALL_EXPR_ARG (call_expr, 1);
+
+#if 0
+  fprintf (stderr, "call_expr: %p\n", call_expr);
+  fprintf (stderr, "arg: %p\n", arg);
+  debug_tree (arg);
+#endif
+
+  emit_warning (EXPR_LOCATION (arg));
+
+  return NULL_TREE;
+}
+
+static void
+callback (void *gcc_data, void *user_data)
+{
+  tree fndecl = (tree)gcc_data;
+  walk_tree (&DECL_SAVED_TREE (fndecl), cb_walk_tree_fn, NULL, NULL);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  struct register_pass_info pass_info;
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  register_callback (plugin_name,
+		     PLUGIN_PRE_GENERICIZE,
+		     callback,
+		     NULL);
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp
index 3ed1397..2266380 100644
--- a/gcc/testsuite/g++.dg/plugin/plugin.exp
+++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
@@ -62,7 +62,10 @@  set plugin_test_list [list \
     { dumb_plugin.c dumb-plugin-test-1.C } \
     { header_plugin.c header-plugin-test.C } \
     { decl_plugin.c decl-plugin-test.C } \
-    { def_plugin.c def-plugin-test.C } ]
+    { def_plugin.c def-plugin-test.C } \
+    { diagnostic_plugin_test_tree_expression_range.c \
+	  diagnostic-test-expressions-1.c } \
+]
 
 foreach plugin_test $plugin_test_list {
     # Replace each source file with its full-path name
diff --git a/gcc/tree.c b/gcc/tree.c
index d5a71a3..e7f4dcf 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13884,7 +13884,7 @@  nonnull_arg_p (const_tree arg)
 /* Given location LOC, strip away any packed range information
    or ad-hoc information.  */
 
-static location_t
+location_t
 get_pure_location (location_t loc)
 {
   if (IS_ADHOC_LOC (loc))
@@ -13914,20 +13914,20 @@  set_block (location_t loc, tree block)
   return COMBINE_LOCATION_DATA (line_table, pure_loc, src_range, block);
 }
 
-void
+location_t
 set_source_range (tree expr, location_t start, location_t finish)
 {
   source_range src_range;
   src_range.m_start = start;
   src_range.m_finish = finish;
-  set_source_range (expr, src_range);
+  return set_source_range (expr, src_range);
 }
 
-void
+location_t
 set_source_range (tree expr, source_range src_range)
 {
   if (!EXPR_P (expr))
-    return;
+    return UNKNOWN_LOCATION;
 
   location_t pure_loc = get_pure_location (EXPR_LOCATION (expr));
   location_t adhoc = COMBINE_LOCATION_DATA (line_table,
@@ -13935,6 +13935,21 @@  set_source_range (tree expr, source_range src_range)
 					    src_range,
 					    NULL);
   SET_EXPR_LOCATION (expr, adhoc);
+  return adhoc;
+}
+
+location_t
+make_location (location_t caret, location_t start, location_t finish)
+{
+  location_t pure_loc = get_pure_location (caret);
+  source_range src_range;
+  src_range.m_start = start;
+  src_range.m_finish = finish;
+  location_t combined_loc = COMBINE_LOCATION_DATA (line_table,
+						   pure_loc,
+						   src_range,
+						   NULL);
+  return combined_loc;
 }
 
 /* Return the name of combined function FN, for debugging purposes.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index 41c0f7c..54164f1 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5337,6 +5337,7 @@  type_with_alias_set_p (const_tree t)
   return false;
 }
 
+extern location_t get_pure_location (location_t loc);
 extern location_t set_block (location_t loc, tree block);
 
 extern void gt_ggc_mx (tree &);
@@ -5345,10 +5346,10 @@  extern void gt_pch_nx (tree &, gt_pointer_operator, void *);
 
 extern bool nonnull_arg_p (const_tree);
 
-extern void
+extern location_t
 set_source_range (tree expr, location_t start, location_t finish);
 
-extern void
+extern location_t
 set_source_range (tree expr, source_range src_range);
 
 static inline source_range
@@ -5358,4 +5359,37 @@  get_decl_source_range (tree decl)
   return get_range_from_loc (line_table, loc);
 }
 
+extern location_t
+make_location (location_t caret, location_t start, location_t finish);
+
+/* FIXME.  */
+inline location_t
+get_start (location_t loc)
+{
+  source_range src_range = get_range_from_loc (line_table, loc);
+  return src_range.m_start;
+}
+
+/* FIXME.  */
+inline location_t
+get_finish (location_t loc)
+{
+  source_range src_range = get_range_from_loc (line_table, loc);
+  return src_range.m_finish;
+}
+
+/* FIXME.  */
+inline location_t
+get_expr_start (tree expr)
+{
+  return get_start (EXPR_LOCATION (expr));
+}
+
+/* FIXME.  */
+inline location_t
+get_expr_finish (tree expr)
+{
+  return get_finish (EXPR_LOCATION (expr));
+}
+
 #endif  /* GCC_TREE_H  */