This is a combination of various patches from v1 of the kit,
including:
  12/22: Add source-ranges for trees
  13/22: gcc-rich-location.[ch]: add methods for working with tree ranges
  14/22: C: capture tree ranges for various expressions

The implementation of how ranges are stored has completely changed
since v1 of the kit.  Rather than introducing a SOURCE_RANGE tree node
and adding fields to decl and expr, the patch now captures ranges
for all C expressions during parsing within a new field of c_expr,
and for all tree nodes with a location_t, it stores them in
ad-hoc locations for later use.

Hence compound expressions get ranges; see:
  
https://dmalcolm.fedorapeople.org/gcc/2015-09-22/diagnostic-test-expressions-1.html

and for this example:

  int test (int foo)
  {
    return foo * 100;
           ^^^   ^^^
  }

we have access to the ranges of "foo" and "100" during C parsing via
the c_expr, but once we have GENERIC, all we have is a VAR_DECL and an
INTEGER_CST (the former's location is in at the top of the
function, and the latter has no location).

This restriction means that I had to remove various expressions from
diagnostic-test-expressions-1.c; specifically:
  test_global
  test_param
  test_local
  test_integer_constants
  test_character_constants
  test_floating_constants
  test_enumeration_constant
  test_string_literal
  test_unary_plus
  test_sizeof

There are still some FIXMEs in here that probably need addressing.

gcc/ChangeLog:
        * Makefile.in (OBJS): Add gcc-rich-location.o.
        * gcc-rich-location.c: New file.
        * gcc-rich-location.h: New file.
        * gimple.h (gimple_set_block): Use "set_block".
        * print-tree.c (print_node): Print any source range information.
        * tree-cfg.c (move_block_to_fn): Use "set_block".
        (move_block_to_fn): Likewise.
        * tree-inline.c (copy_phis_for_bb): Likewise.
        * tree.c (tree_set_block): Use "set_block".
        (set_source_range): New functions.
        (set_block): New function.
        * tree.h (CAN_HAVE_RANGE_P): New.
        (EXPR_LOCATION_RANGE): New.
        (EXPR_HAS_RANGE): New.
        (get_expr_source_range): New inline function.
        (DECL_LOCATION_RANGE): New.
        (set_source_range): New decls.
        (set_block): New decl.
        (get_decl_source_range): New inline function.

gcc/c-family/ChangeLog:
        * c-common.c (c_fully_fold_internal): Capture existing souce_range,
        and store it on the result.

gcc/c/ChangeLog:
        * c-parser.c (set_c_expr_source_range): New functions.
        (c_parser_expr_no_commas): Call set_c_expr_source_range on the ret
        based on the range from the start of the LHS to the end of the
        RHS.
        (c_parser_conditional_expression): Likewise, based on the range
        from the start of the cond.value to the end of exp2.value.
        (c_parser_binary_expression): Call set_c_expr_source_range on
        the stack values for TRUTH_ANDIF_EXPR and TRUTH_ORIF_EXPR.
        (c_parser_cast_expression): Call set_c_expr_source_range on ret
        based on the cast_loc through to the end of the expr.
        (c_parser_unary_expression): Likewise, based on the
        op_loc through to the end of op.
        (c_parser_sizeof_expression) Likewise, based on the start of the
        sizeof token through to either the closing paren or the end of
        expr.
        (c_parser_postfix_expression): Likewise, using the token range,
        or from the open paren through to the close paren for
        parenthesized expressions.
        (c_parser_postfix_expression_after_primary): Likewise, for
        various kinds of expression.
        * c-tree.h (struct c_expr): Add field "src_range".
        (set_c_expr_source_range): New decls.
        * c-typeck.c (parser_build_unary_op): Call set_c_expr_source_range
        on ret for prefix unary ops.
        (parser_build_binary_op): Likewise, running from the start of
        arg1.value through to the end of arg2.value.

gcc/testsuite/ChangeLog:
        * gcc.dg/plugin/diagnostic-test-expressions-1.c: New file.
        * gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c:
        New file.
        * gcc.dg/plugin/plugin.exp (plugin_test_list): Add
        diagnostic_plugin_test_tree_expression_range.c and
        diagnostic-test-expressions-1.c.

libcpp/ChangeLog:
        * include/line-map.h (location_adhoc_data): Add field "src_range".
        (get_combined_adhoc_loc): Add source_range param.
        (get_range_from_adhoc_loc): New decl.
        (COMBINE_LOCATION_DATA): Add  source_range param.
        * line-map.c (location_adhoc_data_hash): Contribute the src_range
        start and finish to the hash value.
        (location_adhoc_data_eq): Require that the src_range values be
        equal.
        (get_combined_adhoc_loc): Add source_range param and store it.
        Remove the requirement that "data" be non-NULL.
        (get_range_from_adhoc_loc): New function.
        (linemap_expand_location): Move the update of "loc" until after
        extracting "data".
---
 gcc/Makefile.in                                    |   1 +
 gcc/c-family/c-common.c                            |  10 +-
 gcc/c/c-parser.c                                   |  89 ++++-
 gcc/c/c-tree.h                                     |  11 +
 gcc/c/c-typeck.c                                   |  10 +
 gcc/gcc-rich-location.c                            |  86 +++++
 gcc/gcc-rich-location.h                            |  47 +++
 gcc/gimple.h                                       |   6 +-
 gcc/print-tree.c                                   |  21 +
 .../gcc.dg/plugin/diagnostic-test-expressions-1.c  | 422 +++++++++++++++++++++
 .../diagnostic_plugin_test_tree_expression_range.c | 159 ++++++++
 gcc/testsuite/gcc.dg/plugin/plugin.exp             |   2 +
 gcc/tree-cfg.c                                     |   9 +-
 gcc/tree-inline.c                                  |   5 +-
 gcc/tree.c                                         |  40 +-
 gcc/tree.h                                         |  40 ++
 libcpp/include/line-map.h                          |  13 +-
 libcpp/line-map.c                                  |  26 +-
 18 files changed, 964 insertions(+), 33 deletions(-)
 create mode 100644 gcc/gcc-rich-location.c
 create mode 100644 gcc/gcc-rich-location.h
 create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-expressions-1.c
 create mode 100644 
gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 009c745..8cd446d 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1255,6 +1255,7 @@ OBJS = \
        fold-const.o \
        function.o \
        fwprop.o \
+       gcc-rich-location.o \
        gcse.o \
        gcse-common.o \
        ggc-common.o \
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index ded23d3..4505db7 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -1178,6 +1178,7 @@ c_fully_fold_internal (tree expr, bool in_init, bool 
*maybe_const_operands,
   bool op0_const_self = true, op1_const_self = true, op2_const_self = true;
   bool nowarning = TREE_NO_WARNING (expr);
   bool unused_p;
+  source_range old_range;
 
   /* This function is not relevant to C++ because C++ folds while
      parsing, and may need changes to be correct for C++ when C++
@@ -1193,6 +1194,9 @@ c_fully_fold_internal (tree expr, bool in_init, bool 
*maybe_const_operands,
       || code == SAVE_EXPR)
     return expr;
 
+  if (IS_EXPR_CODE_CLASS (kind))
+    old_range = EXPR_LOCATION_RANGE (expr);
+
   /* Operands of variable-length expressions (function calls) have
      already been folded, as have __builtin_* function calls, and such
      expressions cannot occur in constant expressions.  */
@@ -1617,7 +1621,11 @@ c_fully_fold_internal (tree expr, bool in_init, bool 
*maybe_const_operands,
       TREE_NO_WARNING (ret) = 1;
     }
   if (ret != expr)
-    protected_set_expr_location (ret, loc);
+    {
+      protected_set_expr_location (ret, loc);
+      if (IS_EXPR_CODE_CLASS (kind))
+       set_source_range (&ret, old_range.m_start, old_range.m_finish);
+    }
   return ret;
 }
 
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 5edf563..f0f39d4 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -67,6 +67,24 @@ along with GCC; see the file COPYING3.  If not see
 #include "gomp-constants.h"
 #include "c-family/c-indentation.h"
 
+void
+set_c_expr_source_range (c_expr *expr,
+                        location_t start, location_t finish)
+{
+  expr->src_range.m_start = start;
+  expr->src_range.m_finish = finish;
+  set_source_range (&expr->value,
+                   start, finish);
+}
+
+void
+set_c_expr_source_range (c_expr *expr,
+                        source_range src_range)
+{
+  expr->src_range = src_range;
+  set_source_range (&expr->value, src_range);
+}
+
 
 /* Initialization routine for this file.  */
 
@@ -6053,6 +6071,9 @@ c_parser_expr_no_commas (c_parser *parser, struct c_expr 
*after,
   ret.value = build_modify_expr (op_location, lhs.value, lhs.original_type,
                                 code, exp_location, rhs.value,
                                 rhs.original_type);
+  set_c_expr_source_range (&ret,
+                          lhs.src_range.m_start,
+                          rhs.src_range.m_finish);
   if (code == NOP_EXPR)
     ret.original_code = MODIFY_EXPR;
   else
@@ -6083,7 +6104,7 @@ c_parser_conditional_expression (c_parser *parser, struct 
c_expr *after,
                                 tree omp_atomic_lhs)
 {
   struct c_expr cond, exp1, exp2, ret;
-  location_t cond_loc, colon_loc, middle_loc;
+  location_t start, cond_loc, colon_loc, middle_loc;
 
   gcc_assert (!after || c_dialect_objc ());
 
@@ -6091,6 +6112,10 @@ c_parser_conditional_expression (c_parser *parser, 
struct c_expr *after,
 
   if (c_parser_next_token_is_not (parser, CPP_QUERY))
     return cond;
+  if (cond.value != error_mark_node)
+    start = cond.src_range.m_start;
+  else
+    start = UNKNOWN_LOCATION;
   cond_loc = c_parser_peek_token (parser)->location;
   cond = convert_lvalue_to_rvalue (cond_loc, cond, true, true);
   c_parser_consume_token (parser);
@@ -6166,6 +6191,9 @@ c_parser_conditional_expression (c_parser *parser, struct 
c_expr *after,
                           ? t1
                           : NULL);
     }
+  set_c_expr_source_range (&ret,
+                          start,
+                          exp2.src_range.m_finish);
   return ret;
 }
 
@@ -6318,6 +6346,7 @@ c_parser_binary_expression (c_parser *parser, struct 
c_expr *after,
     {
       enum c_parser_prec oprec;
       enum tree_code ocode;
+      source_range src_range;
       if (parser->error)
        goto out;
       switch (c_parser_peek_token (parser)->type)
@@ -6406,6 +6435,7 @@ c_parser_binary_expression (c_parser *parser, struct 
c_expr *after,
       switch (ocode)
        {
        case TRUTH_ANDIF_EXPR:
+         src_range = stack[sp].expr.src_range;
          stack[sp].expr
            = convert_lvalue_to_rvalue (stack[sp].loc,
                                        stack[sp].expr, true, true);
@@ -6413,8 +6443,10 @@ c_parser_binary_expression (c_parser *parser, struct 
c_expr *after,
            (stack[sp].loc, default_conversion (stack[sp].expr.value));
          c_inhibit_evaluation_warnings += (stack[sp].expr.value
                                            == truthvalue_false_node);
+         set_c_expr_source_range (&stack[sp].expr, src_range);
          break;
        case TRUTH_ORIF_EXPR:
+         src_range = stack[sp].expr.src_range;
          stack[sp].expr
            = convert_lvalue_to_rvalue (stack[sp].loc,
                                        stack[sp].expr, true, true);
@@ -6422,6 +6454,7 @@ c_parser_binary_expression (c_parser *parser, struct 
c_expr *after,
            (stack[sp].loc, default_conversion (stack[sp].expr.value));
          c_inhibit_evaluation_warnings += (stack[sp].expr.value
                                            == truthvalue_true_node);
+         set_c_expr_source_range (&stack[sp].expr, src_range);
          break;
        default:
          break;
@@ -6490,6 +6523,10 @@ c_parser_cast_expression (c_parser *parser, struct 
c_expr *after)
        expr = convert_lvalue_to_rvalue (expr_loc, expr, true, true);
       }
       ret.value = c_cast_expr (cast_loc, type_name, expr.value);
+      if (ret.value && expr.value)
+       set_c_expr_source_range (&ret,
+                                cast_loc,
+                                expr.src_range.m_finish);
       ret.original_code = ERROR_MARK;
       ret.original_type = NULL;
       return ret;
@@ -6539,6 +6576,7 @@ c_parser_unary_expression (c_parser *parser)
   struct c_expr ret, op;
   location_t op_loc = c_parser_peek_token (parser)->location;
   location_t exp_loc;
+  location_t finish;
   ret.original_code = ERROR_MARK;
   ret.original_type = NULL;
   switch (c_parser_peek_token (parser)->type)
@@ -6578,8 +6616,10 @@ c_parser_unary_expression (c_parser *parser)
       c_parser_consume_token (parser);
       exp_loc = c_parser_peek_token (parser)->location;
       op = c_parser_cast_expression (parser, NULL);
+      finish = op.src_range.m_finish;
       op = convert_lvalue_to_rvalue (exp_loc, op, true, true);
       ret.value = build_indirect_ref (op_loc, op.value, RO_UNARY_STAR);
+      set_c_expr_source_range (&ret, op_loc, finish);
       return ret;
     case CPP_PLUS:
       if (!c_dialect_objc () && !in_system_header_at (input_location))
@@ -6667,8 +6707,15 @@ static struct c_expr
 c_parser_sizeof_expression (c_parser *parser)
 {
   struct c_expr expr;
+  struct c_expr result;
   location_t expr_loc;
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_SIZEOF));
+
+  location_t start;
+  location_t finish = UNKNOWN_LOCATION;
+
+  start = c_parser_peek_token (parser)->location;
+
   c_parser_consume_token (parser);
   c_inhibit_evaluation_warnings++;
   in_sizeof++;
@@ -6682,6 +6729,7 @@ c_parser_sizeof_expression (c_parser *parser)
       expr_loc = c_parser_peek_token (parser)->location;
       type_name = c_parser_type_name (parser);
       c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      finish = parser->tokens_buf[0].range.m_finish; // FIXME: better access 
API to last token
       if (type_name == NULL)
        {
          struct c_expr ret;
@@ -6697,17 +6745,19 @@ c_parser_sizeof_expression (c_parser *parser)
          expr = c_parser_postfix_expression_after_paren_type (parser,
                                                               type_name,
                                                               expr_loc);
+         finish = expr.src_range.m_finish;
          goto sizeof_expr;
        }
       /* sizeof ( type-name ).  */
       c_inhibit_evaluation_warnings--;
       in_sizeof--;
-      return c_expr_sizeof_type (expr_loc, type_name);
+      result = c_expr_sizeof_type (expr_loc, type_name);
     }
   else
     {
       expr_loc = c_parser_peek_token (parser)->location;
       expr = c_parser_unary_expression (parser);
+      finish = expr.src_range.m_finish;
     sizeof_expr:
       c_inhibit_evaluation_warnings--;
       in_sizeof--;
@@ -6715,8 +6765,11 @@ c_parser_sizeof_expression (c_parser *parser)
       if (TREE_CODE (expr.value) == COMPONENT_REF
          && DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1)))
        error_at (expr_loc, "%<sizeof%> applied to a bit-field");
-      return c_expr_sizeof_expr (expr_loc, expr);
+      result = c_expr_sizeof_expr (expr_loc, expr);
     }
+  if (finish != UNKNOWN_LOCATION)
+    set_c_expr_source_range (&result, start, finish);
+  return result;
 }
 
 /* Parse an alignof expression.  */
@@ -7136,12 +7189,14 @@ c_parser_postfix_expression (c_parser *parser)
   struct c_expr expr, e1;
   struct c_type_name *t1, *t2;
   location_t loc = c_parser_peek_token (parser)->location;;
+  source_range tok_range = c_parser_peek_token (parser)->range;
   expr.original_code = ERROR_MARK;
   expr.original_type = NULL;
   switch (c_parser_peek_token (parser)->type)
     {
     case CPP_NUMBER:
       expr.value = c_parser_peek_token (parser)->value;
+      set_c_expr_source_range (&expr, tok_range);
       loc = c_parser_peek_token (parser)->location;
       c_parser_consume_token (parser);
       if (TREE_CODE (expr.value) == FIXED_CST
@@ -7156,6 +7211,7 @@ c_parser_postfix_expression (c_parser *parser)
     case CPP_CHAR32:
     case CPP_WCHAR:
       expr.value = c_parser_peek_token (parser)->value;
+      set_c_expr_source_range (&expr, tok_range);
       c_parser_consume_token (parser);
       break;
     case CPP_STRING:
@@ -7164,6 +7220,7 @@ c_parser_postfix_expression (c_parser *parser)
     case CPP_WSTRING:
     case CPP_UTF8STRING:
       expr.value = c_parser_peek_token (parser)->value;
+      set_c_expr_source_range (&expr, tok_range);
       expr.original_code = STRING_CST;
       c_parser_consume_token (parser);
       break;
@@ -7171,6 +7228,7 @@ c_parser_postfix_expression (c_parser *parser)
       gcc_assert (c_dialect_objc ());
       expr.value
        = objc_build_string_object (c_parser_peek_token (parser)->value);
+      set_c_expr_source_range (&expr, tok_range);
       c_parser_consume_token (parser);
       break;
     case CPP_NAME:
@@ -7184,6 +7242,7 @@ c_parser_postfix_expression (c_parser *parser)
                                             (c_parser_peek_token (parser)->type
                                              == CPP_OPEN_PAREN),
                                             &expr.original_type);
+           set_c_expr_source_range (&expr, tok_range);
            break;
          }
        case C_ID_CLASSNAME:
@@ -7272,6 +7331,7 @@ c_parser_postfix_expression (c_parser *parser)
       else
        {
          /* A parenthesized expression.  */
+         location_t loc_open_paren = c_parser_peek_token (parser)->location;
          c_parser_consume_token (parser);
          expr = c_parser_expression (parser);
          if (TREE_CODE (expr.value) == MODIFY_EXPR)
@@ -7279,6 +7339,8 @@ c_parser_postfix_expression (c_parser *parser)
          if (expr.original_code != C_MAYBE_CONST_EXPR)
            expr.original_code = ERROR_MARK;
          /* Don't change EXPR.ORIGINAL_TYPE.  */
+         location_t loc_close_paren = c_parser_peek_token (parser)->location;
+         set_c_expr_source_range (&expr, loc_open_paren, loc_close_paren);
          c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
                                     "expected %<)%>");
        }
@@ -7869,6 +7931,8 @@ c_parser_postfix_expression_after_primary (c_parser 
*parser,
   vec<tree, va_gc> *exprlist;
   vec<tree, va_gc> *origtypes = NULL;
   vec<location_t> arg_loc = vNULL;
+  location_t start;
+  location_t finish;
 
   while (true)
     {
@@ -7905,7 +7969,10 @@ c_parser_postfix_expression_after_primary (c_parser 
*parser,
                {
                  c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE,
                                             "expected %<]%>");
+                 start = expr.src_range.m_start;
+                 finish = parser->tokens_buf[0].range.m_finish; // FIXME: 
better access API to last token
                  expr.value = build_array_ref (op_loc, expr.value, idx);
+                 set_c_expr_source_range (&expr, start, finish);
                }
            }
          expr.original_code = ERROR_MARK;
@@ -7948,9 +8015,13 @@ c_parser_postfix_expression_after_primary (c_parser 
*parser,
                        "%<memset%> used with constant zero length parameter; "
                        "this could be due to transposed parameters");
 
+         start = expr.src_range.m_start;
+         finish = parser->tokens_buf[0].range.m_finish; // FIXME: better 
access API to last token
          expr.value
            = c_build_function_call_vec (expr_loc, arg_loc, expr.value,
                                         exprlist, origtypes);
+         set_c_expr_source_range (&expr, start, finish);
+
          expr.original_code = ERROR_MARK;
          if (TREE_CODE (expr.value) == INTEGER_CST
              && TREE_CODE (orig_expr.value) == FUNCTION_DECL
@@ -7979,8 +8050,11 @@ c_parser_postfix_expression_after_primary (c_parser 
*parser,
               expr.original_type = NULL;
              return expr;
            }
+         start = expr.src_range.m_start;
+         finish = c_parser_peek_token (parser)->range.m_finish;
          c_parser_consume_token (parser);
          expr.value = build_component_ref (op_loc, expr.value, ident);
+         set_c_expr_source_range (&expr, start, finish);
          expr.original_code = ERROR_MARK;
          if (TREE_CODE (expr.value) != COMPONENT_REF)
            expr.original_type = NULL;
@@ -8008,12 +8082,15 @@ c_parser_postfix_expression_after_primary (c_parser 
*parser,
              expr.original_type = NULL;
              return expr;
            }
+         start = expr.src_range.m_start;
+         finish = c_parser_peek_token (parser)->range.m_finish;
          c_parser_consume_token (parser);
          expr.value = build_component_ref (op_loc,
                                            build_indirect_ref (op_loc,
                                                                expr.value,
                                                                RO_ARROW),
                                            ident);
+         set_c_expr_source_range (&expr, start, finish);
          expr.original_code = ERROR_MARK;
          if (TREE_CODE (expr.value) != COMPONENT_REF)
            expr.original_type = NULL;
@@ -8029,6 +8106,8 @@ c_parser_postfix_expression_after_primary (c_parser 
*parser,
          break;
        case CPP_PLUS_PLUS:
          /* Postincrement.  */
+         start = expr.src_range.m_start;
+         finish = c_parser_peek_token (parser)->range.m_finish;
          c_parser_consume_token (parser);
          /* If the expressions have array notations, we expand them.  */
          if (flag_cilkplus
@@ -8040,11 +8119,14 @@ c_parser_postfix_expression_after_primary (c_parser 
*parser,
              expr.value = build_unary_op (op_loc,
                                           POSTINCREMENT_EXPR, expr.value, 0);
            }
+         set_c_expr_source_range (&expr, start, finish);
          expr.original_code = ERROR_MARK;
          expr.original_type = NULL;
          break;
        case CPP_MINUS_MINUS:
          /* Postdecrement.  */
+         start = expr.src_range.m_start;
+         finish = c_parser_peek_token (parser)->range.m_finish;
          c_parser_consume_token (parser);
          /* If the expressions have array notations, we expand them.  */
          if (flag_cilkplus
@@ -8056,6 +8138,7 @@ c_parser_postfix_expression_after_primary (c_parser 
*parser,
              expr.value = build_unary_op (op_loc,
                                           POSTDECREMENT_EXPR, expr.value, 0);
            }
+         set_c_expr_source_range (&expr, start, finish);
          expr.original_code = ERROR_MARK;
          expr.original_type = NULL;
          break;
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 667529a..9453caf 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -132,6 +132,9 @@ struct c_expr
      The type of an enum constant is a plain integer type, but this
      field will be the enum type.  */
   tree original_type;
+
+  /* FIXME.  */
+  source_range src_range;
 };
 
 /* Type alias for struct c_expr. This allows to use the structure
@@ -709,4 +712,12 @@ extern void pedwarn_c90 (location_t, int opt, const char 
*, ...)
 extern bool pedwarn_c99 (location_t, int opt, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
 
+extern void
+set_c_expr_source_range (c_expr *expr,
+                        location_t start, location_t finish);
+
+extern void
+set_c_expr_source_range (c_expr *expr,
+                        source_range src_range);
+
 #endif /* ! GCC_C_TREE_H */
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 3b26231..8f3e0a8 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -3395,6 +3395,12 @@ parser_build_unary_op (location_t loc, enum tree_code 
code, struct c_expr arg)
     overflow_warning (loc, result.value);
     }
 
+  /* We are typically called when parsing a prefix token at LOC acting on
+     ARG.  Reflect this by updating the source range of the result to
+     start at LOC and end at the end of ARG.  */
+  set_c_expr_source_range (&result,
+                          loc, arg.src_range.m_finish);
+
   return result;
 }
 
@@ -3432,6 +3438,10 @@ parser_build_binary_op (location_t location, enum 
tree_code code,
   if (location != UNKNOWN_LOCATION)
     protected_set_expr_location (result.value, location);
 
+  set_c_expr_source_range (&result,
+                          arg1.src_range.m_start,
+                          arg2.src_range.m_finish);
+
   /* Check for cases such as x+y<<z which users are likely
      to misinterpret.  */
   if (warn_parentheses)
diff --git a/gcc/gcc-rich-location.c b/gcc/gcc-rich-location.c
new file mode 100644
index 0000000..b0ec47b
--- /dev/null
+++ b/gcc/gcc-rich-location.c
@@ -0,0 +1,86 @@
+/* Implementation of gcc_rich_location class
+   Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
+#include "tree-core.h"
+#include "tree.h"
+#include "diagnostic-core.h"
+#include "gcc-rich-location.h"
+#include "print-tree.h"
+#include "pretty-print.h"
+#include "intl.h"
+#include "cpplib.h"
+#include "diagnostic.h"
+
+/* Extract any source range information from EXPR and write it
+   to *R.  */
+
+static bool
+get_range_for_expr (tree expr, location_range *r)
+{
+  if (EXPR_HAS_RANGE (expr))
+    {
+      source_range sr = EXPR_LOCATION_RANGE (expr);
+
+      /* Do we have meaningful data?  */
+      if (sr.m_start && sr.m_finish)
+       {
+         r->m_start = expand_location (sr.m_start);
+         r->m_finish = expand_location (sr.m_finish);
+         return true;
+       }
+    }
+
+  return false;
+}
+
+/* Add a range to the rich_location, covering expression EXPR. */
+
+void
+gcc_rich_location::add_expr (tree expr)
+{
+  gcc_assert (expr);
+
+  location_range r;
+  r.m_show_caret_p = false;
+  if (get_range_for_expr (expr, &r))
+    add_range (&r);
+}
+
+/* If T is an expression, add a range for it to the rich_location.  */
+
+void
+gcc_rich_location::maybe_add_expr (tree t)
+{
+  if (EXPR_P (t))
+    add_expr (t);
+}
diff --git a/gcc/gcc-rich-location.h b/gcc/gcc-rich-location.h
new file mode 100644
index 0000000..c82cbf1
--- /dev/null
+++ b/gcc/gcc-rich-location.h
@@ -0,0 +1,47 @@
+/* Declarations relating to class gcc_rich_location
+   Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_RICH_LOCATION_H
+#define GCC_RICH_LOCATION_H
+
+/* A gcc_rich_location is libcpp's rich_location with additional
+   helper methods for working with gcc's types.  */
+class gcc_rich_location : public rich_location
+{
+ public:
+  /* Constructors.  */
+
+  /* Constructing from a location.  */
+  gcc_rich_location (source_location loc) :
+    rich_location (loc) {}
+
+  /* Constructing from a source_range.  */
+  gcc_rich_location (source_range src_range) :
+    rich_location (src_range) {}
+
+
+  /* Methods for adding ranges via gcc entities.  */
+  void
+  add_expr (tree expr);
+
+  void
+  maybe_add_expr (tree t);
+};
+
+#endif /* GCC_RICH_LOCATION_H */
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 91c26b6..ba8f410 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -1709,11 +1709,7 @@ gimple_block (const gimple *g)
 static inline void
 gimple_set_block (gimple *g, tree block)
 {
-  if (block)
-    g->location =
-       COMBINE_LOCATION_DATA (line_table, g->location, block);
-  else
-    g->location = LOCATION_LOCUS (g->location);
+  g->location = set_block (g->location, block);
 }
 
 
diff --git a/gcc/print-tree.c b/gcc/print-tree.c
index ea50056..8b3794a 100644
--- a/gcc/print-tree.c
+++ b/gcc/print-tree.c
@@ -936,6 +936,27 @@ print_node (FILE *file, const char *prefix, tree node, int 
indent)
       expanded_location xloc = expand_location (EXPR_LOCATION (node));
       indent_to (file, indent+4);
       fprintf (file, "%s:%d:%d", xloc.file, xloc.line, xloc.column);
+
+      /* Print the range, if any */
+      source_range r = EXPR_LOCATION_RANGE (node);
+      if (r.m_start)
+       {
+         xloc = expand_location (r.m_start);
+         fprintf (file, " start: %s:%d:%d", xloc.file, xloc.line, xloc.column);
+       }
+      else
+       {
+         fprintf (file, " start: unknown");
+       }
+      if (r.m_finish)
+       {
+         xloc = expand_location (r.m_finish);
+         fprintf (file, " finish: %s:%d:%d", xloc.file, xloc.line, 
xloc.column);
+       }
+      else
+       {
+         fprintf (file, " finish: unknown");
+       }
     }
 
   fprintf (file, ">");
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-expressions-1.c 
b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-expressions-1.c
new file mode 100644
index 0000000..5485aaf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-expressions-1.c
@@ -0,0 +1,422 @@
+/* { 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;
+
+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 "" } */
+
+}
+
+/* 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 "" } */
+}
+
+/* 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 "" } */
+
+}
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
new file mode 100644
index 0000000..ef7d13f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
@@ -0,0 +1,159 @@
+/* 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 "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 "gcc-rich-location.h"
+#include "print-tree.h"
+
+/*
+  Hack: fails with linker error:
+./diagnostic_plugin_test_tree_expression_range.so: undefined symbol: 
_ZN17gcc_rich_location8add_exprEP9tree_node
+  since nothing in the tree is using gcc_rich_location::add_expr yet.
+
+  I've tried various workarounds (adding DEBUG_FUNCTION to the
+  method, taking its address), but can't seem to fix it that way.
+  So as a nasty workaround, the following material is copied&pasted
+  from gcc-rich-location.c: */
+
+static bool
+get_range_for_expr (tree expr, location_range *r)
+{
+  if (EXPR_HAS_RANGE (expr))
+    {
+      source_range sr = EXPR_LOCATION_RANGE (expr);
+
+      /* Do we have meaningful data?  */
+      if (sr.m_start && sr.m_finish)
+       {
+         r->m_start = expand_location (sr.m_start);
+         r->m_finish = expand_location (sr.m_finish);
+         return true;
+       }
+    }
+
+  return false;
+}
+
+/* Add a range to the rich_location, covering expression EXPR. */
+
+void
+gcc_rich_location::add_expr (tree expr)
+{
+  gcc_assert (expr);
+
+  location_range r;
+  r.m_show_caret_p = false;
+  if (get_range_for_expr (expr, &r))
+    add_range (&r);
+}
+
+/* FIXME: end of material taken from gcc-rich-location.c */
+
+
+int plugin_is_GPL_compatible;
+
+static void
+emit_warning (rich_location *richloc)
+{
+  if (richloc->get_num_locations () < 2)
+    {
+      error_at_rich_loc (richloc, "range not found");
+      return;
+    }
+
+  location_range *range = richloc->get_range (1);
+  warning_at_rich_loc (richloc, 0,
+                      "tree range %i:%i-%i:%i",
+                      range->m_start.line,
+                      range->m_start.column,
+                      range->m_finish.line,
+                      range->m_finish.column);
+}
+
+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);
+  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! */
+  //debug_tree (call_expr);
+
+  tree arg = CALL_EXPR_ARG (call_expr, 1);
+  //debug_tree (arg);
+
+  gcc_rich_location richloc (EXPR_LOCATION (arg));
+  richloc.add_expr (arg);
+  emit_warning (&richloc);
+
+  return NULL_TREE; //  should we be setting *walk_subtrees?
+}
+
+static void
+callback (void *gcc_data, void *user_data)
+{
+  //fprintf (stdout, "callback called!\n");
+  tree fndecl = (tree)gcc_data;
+
+  /* FIXME: is this actually going to be valid on all frontends
+     before genericize? */
+  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/gcc.dg/plugin/plugin.exp 
b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index 941bccc..b7efcf5 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -66,6 +66,8 @@ set plugin_test_list [list \
     { diagnostic_plugin_test_show_locus.c \
          diagnostic-test-show-locus-bw.c \
          diagnostic-test-show-locus-color.c } \
+    { diagnostic_plugin_test_tree_expression_range.c \
+         diagnostic-test-expressions-1.c } \
 ]
 
 foreach plugin_test $plugin_test_list {
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index 807d96f..e605b0b 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -6738,10 +6738,7 @@ move_block_to_fn (struct function *dest_cfun, 
basic_block bb,
            continue;
          if (d->orig_block == NULL_TREE || block == d->orig_block)
            {
-             if (d->new_block == NULL_TREE)
-               locus = LOCATION_LOCUS (locus);
-             else
-               locus = COMBINE_LOCATION_DATA (line_table, locus, d->new_block);
+             locus = set_block (locus, d->new_block);
              gimple_phi_arg_set_location (phi, i, locus);
            }
        }
@@ -6801,9 +6798,7 @@ move_block_to_fn (struct function *dest_cfun, basic_block 
bb,
        tree block = LOCATION_BLOCK (e->goto_locus);
        if (d->orig_block == NULL_TREE
            || block == d->orig_block)
-         e->goto_locus = d->new_block ?
-             COMBINE_LOCATION_DATA (line_table, e->goto_locus, d->new_block) :
-             LOCATION_LOCUS (e->goto_locus);
+         e->goto_locus = set_block (e->goto_locus, d->new_block);
       }
 }
 
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index abaea3f..d98b7d2 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2352,10 +2352,7 @@ copy_phis_for_bb (basic_block bb, copy_body_data *id)
                  tree *n;
                  n = id->decl_map->get (LOCATION_BLOCK (locus));
                  gcc_assert (n);
-                 if (*n)
-                   locus = COMBINE_LOCATION_DATA (line_table, locus, *n);
-                 else
-                   locus = LOCATION_LOCUS (locus);
+                 locus = set_block (locus, *n);
                }
              else
                locus = LOCATION_LOCUS (locus);
diff --git a/gcc/tree.c b/gcc/tree.c
index 84fd34d..9e91d2c 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -11659,10 +11659,7 @@ tree_set_block (tree t, tree b)
 
   if (IS_EXPR_CODE_CLASS (c))
     {
-      if (b)
-       t->exp.locus = COMBINE_LOCATION_DATA (line_table, t->exp.locus, b);
-      else
-       t->exp.locus = LOCATION_LOCUS (t->exp.locus);
+      t->exp.locus = set_block (t->exp.locus, b);
     }
   else
     gcc_unreachable ();
@@ -13646,5 +13643,40 @@ nonnull_arg_p (const_tree arg)
   return false;
 }
 
+void
+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);
+}
+
+void
+set_source_range (tree *expr, source_range src_range)
+{
+  if (!EXPR_P (*expr))
+    return;
+
+  location_t adhoc = COMBINE_LOCATION_DATA (line_table,
+                                           EXPR_LOCATION (*expr),
+                                           src_range,
+                                           NULL /* FIXME */);
+  SET_EXPR_LOCATION (*expr, adhoc);
+}
+
+location_t
+set_block (location_t loc, tree block)
+{
+  source_range src_range;
+  if (IS_ADHOC_LOC (loc))
+    /* FIXME: can we update in-place?  */
+    src_range = get_range_from_adhoc_loc (line_table, loc);
+  else
+    src_range = source_range::from_location (loc);
+
+  return COMBINE_LOCATION_DATA (line_table, loc, src_range, block);
+}
+
 
 #include "gt-tree.h"
diff --git a/gcc/tree.h b/gcc/tree.h
index e500151..2cfbf30 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1066,10 +1066,28 @@ extern void omp_clause_range_check_failed (const_tree, 
const char *, int,
 #define EXPR_FILENAME(NODE) LOCATION_FILE (EXPR_CHECK ((NODE))->exp.locus)
 #define EXPR_LINENO(NODE) LOCATION_LINE (EXPR_CHECK (NODE)->exp.locus)
 
+#define CAN_HAVE_RANGE_P(NODE) (CAN_HAVE_LOCATION_P (NODE))
+#define EXPR_LOCATION_RANGE(NODE) (get_expr_source_range (EXPR_CHECK ((NODE))))
+
+#define EXPR_HAS_RANGE(NODE) \
+    (CAN_HAVE_RANGE_P (NODE) \
+     ? EXPR_LOCATION_RANGE (NODE).m_start != UNKNOWN_LOCATION \
+     : false)
+
 /* True if a tree is an expression or statement that can have a
    location.  */
 #define CAN_HAVE_LOCATION_P(NODE) ((NODE) && EXPR_P (NODE))
 
+static inline source_range
+get_expr_source_range (tree expr)
+{
+  location_t loc = EXPR_LOCATION (expr);
+  if (IS_ADHOC_LOC (loc))
+    return get_range_from_adhoc_loc (line_table, loc);
+  else
+    return source_range::from_location (loc);
+}
+
 extern void protected_set_expr_location (tree, location_t);
 
 /* In a TARGET_EXPR node.  */
@@ -2092,6 +2110,9 @@ extern machine_mode element_mode (const_tree t);
 #define DECL_IS_BUILTIN(DECL) \
   (LOCATION_LOCUS (DECL_SOURCE_LOCATION (DECL)) <= BUILTINS_LOCATION)
 
+#define DECL_LOCATION_RANGE(NODE) \
+  (get_decl_source_range (DECL_MINIMAL_CHECK (NODE)))
+
 /*  For FIELD_DECLs, this is the RECORD_TYPE, UNION_TYPE, or
     QUAL_UNION_TYPE node that the field is a member of.  For VAR_DECL,
     PARM_DECL, FUNCTION_DECL, LABEL_DECL, RESULT_DECL, and CONST_DECL
@@ -5133,10 +5154,29 @@ type_with_alias_set_p (const_tree t)
   return false;
 }
 
+extern void
+set_source_range (tree *expr, location_t start, location_t finish);
+
+extern void
+set_source_range (tree *expr, source_range src_range);
+
+extern location_t
+set_block (location_t loc, tree block);
+
 extern void gt_ggc_mx (tree &);
 extern void gt_pch_nx (tree &);
 extern void gt_pch_nx (tree &, gt_pointer_operator, void *);
 
 extern bool nonnull_arg_p (const_tree);
 
+static inline source_range
+get_decl_source_range (tree decl)
+{
+  location_t loc = DECL_SOURCE_LOCATION (decl);
+  if (IS_ADHOC_LOC (loc))
+    return get_range_from_adhoc_loc (line_table, loc);
+  else
+    return source_range::from_location (loc);
+}
+
 #endif  /* GCC_TREE_H  */
diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
index bd73780..8deb798 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -499,9 +499,11 @@ struct GTY(()) maps_info_macro {
   unsigned int cache;
 };
 
-/* Data structure to associate an arbitrary data to a source location.  */
+/* Data structure to associate a source_range together with an arbitrary
+   data pointer with a source location.  */
 struct GTY(()) location_adhoc_data {
   source_location locus;
+  source_range src_range;
   void * GTY((skip)) data;
 };
 
@@ -800,10 +802,14 @@ LINEMAPS_LAST_ALLOCATED_MACRO_MAP (const line_maps *set)
 
 extern void location_adhoc_data_fini (struct line_maps *);
 extern source_location get_combined_adhoc_loc (struct line_maps *,
-                                              source_location, void *);
+                                              source_location,
+                                              source_range,
+                                              void *);
 extern void *get_data_from_adhoc_loc (struct line_maps *, source_location);
 extern source_location get_location_from_adhoc_loc (struct line_maps *,
                                                    source_location);
+extern source_range get_range_from_adhoc_loc (struct line_maps *,
+                                             source_location);
 
 /* Get whether location LOC is an ad-hoc location.  */
 
@@ -818,9 +824,10 @@ IS_ADHOC_LOC (source_location loc)
 inline source_location
 COMBINE_LOCATION_DATA (struct line_maps *set,
                       source_location loc,
+                      source_range src_range,
                       void *block)
 {
-  return get_combined_adhoc_loc (set, loc, block);
+  return get_combined_adhoc_loc (set, loc, src_range, block);
 }
 
 extern void rebuild_location_adhoc_htab (struct line_maps *);
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index a6fa782..439157e 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -69,7 +69,10 @@ location_adhoc_data_hash (const void *l)
 {
   const struct location_adhoc_data *lb =
       (const struct location_adhoc_data *) l;
-  return (hashval_t) lb->locus + (size_t) lb->data;
+  return ((hashval_t) lb->locus
+         + (hashval_t) lb->src_range.m_start
+         + (hashval_t) lb->src_range.m_finish
+         + (size_t) lb->data);
 }
 
 /* Compare function for location_adhoc_data hashtable.  */
@@ -81,7 +84,10 @@ location_adhoc_data_eq (const void *l1, const void *l2)
       (const struct location_adhoc_data *) l1;
   const struct location_adhoc_data *lb2 =
       (const struct location_adhoc_data *) l2;
-  return lb1->locus == lb2->locus && lb1->data == lb2->data;
+  return (lb1->locus == lb2->locus
+         && lb1->src_range.m_start == lb2->src_range.m_start
+         && lb1->src_range.m_finish == lb2->src_range.m_finish
+         && lb1->data == lb2->data);
 }
 
 /* Update the hashtable when location_adhoc_data is reallocated.  */
@@ -110,19 +116,20 @@ rebuild_location_adhoc_htab (struct line_maps *set)
 
 source_location
 get_combined_adhoc_loc (struct line_maps *set,
-                       source_location locus, void *data)
+                       source_location locus,
+                       source_range src_range,
+                       void *data)
 {
   struct location_adhoc_data lb;
   struct location_adhoc_data **slot;
 
-  linemap_assert (data);
-
   if (IS_ADHOC_LOC (locus))
     locus
       = set->location_adhoc_data_map.data[locus & MAX_SOURCE_LOCATION].locus;
   if (locus == 0 && data == NULL)
     return 0;
   lb.locus = locus;
+  lb.src_range = src_range;
   lb.data = data;
   slot = (struct location_adhoc_data **)
       htab_find_slot (set->location_adhoc_data_map.htab, &lb, INSERT);
@@ -177,6 +184,13 @@ get_location_from_adhoc_loc (struct line_maps *set, 
source_location loc)
   return set->location_adhoc_data_map.data[loc & MAX_SOURCE_LOCATION].locus;
 }
 
+source_range
+get_range_from_adhoc_loc (struct line_maps *set, source_location loc)
+{
+  linemap_assert (IS_ADHOC_LOC (loc));
+  return set->location_adhoc_data_map.data[loc & 
MAX_SOURCE_LOCATION].src_range;
+}
+
 /* Finalize the location_adhoc_data structure.  */
 void
 location_adhoc_data_fini (struct line_maps *set)
@@ -1478,9 +1492,9 @@ linemap_expand_location (struct line_maps *set,
   memset (&xloc, 0, sizeof (xloc));
   if (IS_ADHOC_LOC (loc))
     {
-      loc = set->location_adhoc_data_map.data[loc & MAX_SOURCE_LOCATION].locus;
       xloc.data
        = set->location_adhoc_data_map.data[loc & MAX_SOURCE_LOCATION].data;
+      loc = set->location_adhoc_data_map.data[loc & MAX_SOURCE_LOCATION].locus;
     }
 
   if (loc < RESERVED_LOCATION_COUNT)
-- 
1.8.5.3

Reply via email to